From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- mobile/android/thirdparty/AndroidManifest.xml | 4 + mobile/android/thirdparty/README | 3 + mobile/android/thirdparty/build.gradle | 54 + .../ConnectionClosedException.java | 50 + .../ConnectionReuseStrategy.java | 70 + .../ch/boye/httpclientandroidlib/Consts.java | 51 + .../ContentTooLongException.java | 50 + .../boye/httpclientandroidlib/FormattedHeader.java | 60 + .../ch/boye/httpclientandroidlib/Header.java | 74 + .../boye/httpclientandroidlib/HeaderElement.java | 108 ++ .../HeaderElementIterator.java | 57 + .../boye/httpclientandroidlib/HeaderIterator.java | 56 + .../httpclientandroidlib/HttpClientConnection.java | 102 ++ .../boye/httpclientandroidlib/HttpConnection.java | 104 ++ .../HttpConnectionFactory.java | 42 + .../HttpConnectionMetrics.java | 77 + .../ch/boye/httpclientandroidlib/HttpEntity.java | 199 +++ .../HttpEntityEnclosingRequest.java | 60 + .../boye/httpclientandroidlib/HttpException.java | 67 + .../ch/boye/httpclientandroidlib/HttpHeaders.java | 206 +++ .../ch/boye/httpclientandroidlib/HttpHost.java | 290 ++++ .../httpclientandroidlib/HttpInetConnection.java | 47 + .../ch/boye/httpclientandroidlib/HttpMessage.java | 210 +++ .../ch/boye/httpclientandroidlib/HttpRequest.java | 53 + .../httpclientandroidlib/HttpRequestFactory.java | 43 + .../HttpRequestInterceptor.java | 68 + .../ch/boye/httpclientandroidlib/HttpResponse.java | 155 ++ .../httpclientandroidlib/HttpResponseFactory.java | 67 + .../HttpResponseInterceptor.java | 68 + .../httpclientandroidlib/HttpServerConnection.java | 88 ++ .../ch/boye/httpclientandroidlib/HttpStatus.java | 175 ++ .../ch/boye/httpclientandroidlib/HttpVersion.java | 110 ++ .../MalformedChunkCodingException.java | 57 + .../MessageConstraintException.java | 50 + .../MethodNotSupportedException.java | 59 + .../boye/httpclientandroidlib/NameValuePair.java | 47 + .../NoHttpResponseException.java | 50 + .../boye/httpclientandroidlib/ParseException.java | 61 + .../httpclientandroidlib/ProtocolException.java | 67 + .../boye/httpclientandroidlib/ProtocolVersion.java | 264 ++++ .../ch/boye/httpclientandroidlib/README.txt | 1 + .../httpclientandroidlib/ReasonPhraseCatalog.java | 51 + .../ch/boye/httpclientandroidlib/RequestLine.java | 49 + .../ch/boye/httpclientandroidlib/StatusLine.java | 52 + .../boye/httpclientandroidlib/TokenIterator.java | 59 + .../TruncatedChunkException.java | 48 + .../UnsupportedHttpVersionException.java | 57 + .../httpclientandroidlib/androidextra/Base64.java | 741 +++++++++ .../androidextra/HttpClientAndroidLog.java | 113 ++ .../httpclientandroidlib/annotation/GuardedBy.java | 76 + .../httpclientandroidlib/annotation/Immutable.java | 59 + .../annotation/NotThreadSafe.java | 50 + .../annotation/ThreadSafe.java | 51 + .../annotation/package-info.java | 34 + .../ch/boye/httpclientandroidlib/auth/AUTH.java | 64 + .../boye/httpclientandroidlib/auth/AuthOption.java | 62 + .../auth/AuthProtocolState.java | 33 + .../boye/httpclientandroidlib/auth/AuthScheme.java | 130 ++ .../auth/AuthSchemeFactory.java | 51 + .../auth/AuthSchemeProvider.java | 46 + .../auth/AuthSchemeRegistry.java | 155 ++ .../boye/httpclientandroidlib/auth/AuthScope.java | 302 ++++ .../boye/httpclientandroidlib/auth/AuthState.java | 235 +++ .../auth/AuthenticationException.java | 70 + .../auth/BasicUserPrincipal.java | 89 ++ .../httpclientandroidlib/auth/ChallengeState.java | 38 + .../auth/ContextAwareAuthScheme.java | 62 + .../httpclientandroidlib/auth/Credentials.java | 44 + .../auth/InvalidCredentialsException.java | 69 + .../auth/MalformedChallengeException.java | 70 + .../httpclientandroidlib/auth/NTCredentials.java | 177 +++ .../httpclientandroidlib/auth/NTUserPrincipal.java | 113 ++ .../auth/UsernamePasswordCredentials.java | 120 ++ .../httpclientandroidlib/auth/package-info.java | 31 + .../auth/params/AuthPNames.java | 74 + .../auth/params/AuthParamBean.java | 55 + .../auth/params/AuthParams.java | 82 + .../auth/params/package-info.java | 32 + .../httpclientandroidlib/client/AuthCache.java | 49 + .../client/AuthenticationHandler.java | 101 ++ .../client/AuthenticationStrategy.java | 130 ++ .../client/BackoffManager.java | 54 + .../client/CircularRedirectException.java | 68 + .../client/ClientProtocolException.java | 61 + .../client/ConnectionBackoffStrategy.java | 64 + .../httpclientandroidlib/client/CookieStore.java | 71 + .../client/CredentialsProvider.java | 71 + .../httpclientandroidlib/client/HttpClient.java | 258 +++ .../client/HttpRequestRetryHandler.java | 60 + .../client/HttpResponseException.java | 52 + .../client/NonRepeatableRequestException.java | 72 + .../client/RedirectException.java | 69 + .../client/RedirectHandler.java | 77 + .../client/RedirectStrategy.java | 81 + .../client/RequestDirector.java | 77 + .../client/ResponseHandler.java | 54 + .../client/ServiceUnavailableRetryStrategy.java | 60 + .../client/UserTokenHandler.java | 58 + .../client/cache/CacheResponseStatus.java | 55 + .../client/cache/HeaderConstants.java | 81 + .../client/cache/HttpCacheContext.java | 71 + .../client/cache/HttpCacheEntry.java | 263 +++ .../HttpCacheEntrySerializationException.java | 48 + .../client/cache/HttpCacheEntrySerializer.java | 54 + .../client/cache/HttpCacheInvalidator.java | 58 + .../client/cache/HttpCacheStorage.java | 81 + .../client/cache/HttpCacheUpdateCallback.java | 52 + .../client/cache/HttpCacheUpdateException.java | 48 + .../client/cache/InputLimit.java | 76 + .../client/cache/Resource.java | 60 + .../client/cache/ResourceFactory.java | 67 + .../httpclientandroidlib/client/cache/package.html | 78 + .../client/config/AuthSchemes.java | 71 + .../client/config/CookieSpecs.java | 69 + .../client/config/RequestConfig.java | 442 ++++++ .../client/config/package-info.java | 31 + .../client/entity/DecompressingEntity.java | 105 ++ .../client/entity/DeflateDecompressingEntity.java | 96 ++ .../client/entity/DeflateInputStream.java | 228 +++ .../client/entity/EntityBuilder.java | 342 ++++ .../client/entity/GzipCompressingEntity.java | 113 ++ .../client/entity/GzipDecompressingEntity.java | 80 + .../entity/LazyDecompressingInputStream.java | 105 ++ .../client/entity/UrlEncodedFormEntity.java | 107 ++ .../client/entity/package-info.java | 31 + .../client/methods/AbortableHttpRequest.java | 82 + .../methods/AbstractExecutionAwareRequest.java | 131 ++ .../client/methods/CloseableHttpResponse.java | 40 + .../client/methods/Configurable.java | 44 + .../client/methods/HttpDelete.java | 77 + .../methods/HttpEntityEnclosingRequestBase.java | 76 + .../client/methods/HttpExecutionAware.java | 47 + .../client/methods/HttpGet.java | 77 + .../client/methods/HttpHead.java | 80 + .../client/methods/HttpOptions.java | 100 ++ .../client/methods/HttpPatch.java | 75 + .../client/methods/HttpPost.java | 84 + .../client/methods/HttpPut.java | 76 + .../client/methods/HttpRequestBase.java | 124 ++ .../client/methods/HttpRequestWrapper.java | 171 ++ .../client/methods/HttpTrace.java | 79 + .../client/methods/HttpUriRequest.java | 84 + .../client/methods/RequestBuilder.java | 351 ++++ .../client/methods/package-info.java | 31 + .../httpclientandroidlib/client/package-info.java | 31 + .../client/params/AllClientPNames.java | 63 + .../client/params/AuthPolicy.java | 79 + .../client/params/ClientPNames.java | 133 ++ .../client/params/ClientParamBean.java | 106 ++ .../client/params/CookiePolicy.java | 80 + .../client/params/HttpClientParamConfig.java | 88 ++ .../client/params/HttpClientParams.java | 116 ++ .../client/params/package-info.java | 32 + .../client/protocol/ClientContext.java | 132 ++ .../client/protocol/ClientContextConfigurer.java | 72 + .../client/protocol/HttpClientContext.java | 249 +++ .../client/protocol/RequestAcceptEncoding.java | 62 + .../client/protocol/RequestAddCookies.java | 204 +++ .../client/protocol/RequestAuthCache.java | 147 ++ .../client/protocol/RequestAuthenticationBase.java | 126 ++ .../client/protocol/RequestClientConnControl.java | 92 ++ .../client/protocol/RequestDefaultHeaders.java | 89 ++ .../client/protocol/RequestExpectContinue.java | 82 + .../protocol/RequestProxyAuthentication.java | 92 ++ .../protocol/RequestTargetAuthentication.java | 83 + .../client/protocol/ResponseAuthCache.java | 151 ++ .../client/protocol/ResponseContentEncoding.java | 110 ++ .../client/protocol/ResponseProcessCookies.java | 156 ++ .../client/protocol/package-info.java | 31 + .../client/utils/CloneUtils.java | 86 + .../client/utils/DateUtils.java | 250 +++ .../client/utils/HttpClientUtils.java | 149 ++ .../httpclientandroidlib/client/utils/Idn.java | 42 + .../httpclientandroidlib/client/utils/JdkIdn.java | 71 + .../client/utils/Punycode.java | 54 + .../client/utils/Rfc3492Idn.java | 141 ++ .../client/utils/URIBuilder.java | 490 ++++++ .../client/utils/URIUtils.java | 428 +++++ .../client/utils/URLEncodedUtils.java | 628 ++++++++ .../client/utils/package-info.java | 31 + .../concurrent/BasicFuture.java | 154 ++ .../concurrent/Cancellable.java | 39 + .../concurrent/FutureCallback.java | 44 + .../concurrent/package-info.java | 31 + .../config/ConnectionConfig.java | 192 +++ .../boye/httpclientandroidlib/config/Lookup.java | 40 + .../config/MessageConstraints.java | 113 ++ .../boye/httpclientandroidlib/config/Registry.java | 63 + .../config/RegistryBuilder.java | 72 + .../httpclientandroidlib/config/SocketConfig.java | 197 +++ .../httpclientandroidlib/config/package-info.java | 31 + .../conn/BasicEofSensorWatcher.java | 105 ++ .../conn/BasicManagedEntity.java | 208 +++ .../conn/ClientConnectionManager.java | 117 ++ .../conn/ClientConnectionManagerFactory.java | 47 + .../conn/ClientConnectionOperator.java | 107 ++ .../conn/ClientConnectionRequest.java | 74 + .../conn/ConnectTimeoutException.java | 94 ++ .../conn/ConnectionKeepAliveStrategy.java | 66 + .../conn/ConnectionPoolTimeoutException.java | 60 + .../conn/ConnectionReleaseTrigger.java | 70 + .../conn/ConnectionRequest.java | 69 + .../httpclientandroidlib/conn/DnsResolver.java | 54 + .../conn/EofSensorInputStream.java | 289 ++++ .../conn/EofSensorWatcher.java | 95 ++ .../conn/HttpClientConnectionManager.java | 176 +++ .../conn/HttpConnectionFactory.java | 41 + .../conn/HttpHostConnectException.java | 82 + .../conn/HttpInetSocketAddress.java | 65 + .../conn/HttpRoutedConnection.java | 81 + .../conn/ManagedClientConnection.java | 228 +++ .../conn/ManagedHttpClientConnection.java | 80 + .../conn/MultihomePlainSocketFactory.java | 173 ++ .../conn/OperatedClientConnection.java | 155 ++ .../conn/SchemePortResolver.java | 43 + .../conn/UnsupportedSchemeException.java | 51 + .../httpclientandroidlib/conn/package-info.java | 31 + .../conn/params/ConnConnectionPNames.java | 64 + .../conn/params/ConnConnectionParamBean.java | 59 + .../conn/params/ConnManagerPNames.java | 67 + .../conn/params/ConnManagerParamBean.java | 63 + .../conn/params/ConnManagerParams.java | 147 ++ .../conn/params/ConnPerRoute.java | 46 + .../conn/params/ConnPerRouteBean.java | 112 ++ .../conn/params/ConnRoutePNames.java | 79 + .../conn/params/ConnRouteParamBean.java | 70 + .../conn/params/ConnRouteParams.java | 178 +++ .../conn/params/package-info.java | 32 + .../conn/routing/BasicRouteDirector.java | 181 +++ .../conn/routing/HttpRoute.java | 328 ++++ .../conn/routing/HttpRouteDirector.java | 74 + .../conn/routing/HttpRoutePlanner.java | 67 + .../conn/routing/RouteInfo.java | 161 ++ .../conn/routing/RouteTracker.java | 366 +++++ .../conn/routing/package-info.java | 31 + .../conn/scheme/HostNameResolver.java | 52 + .../conn/scheme/LayeredSchemeSocketFactory.java | 68 + .../conn/scheme/LayeredSocketFactory.java | 69 + .../conn/scheme/LayeredSocketFactoryAdaptor.java | 53 + .../conn/scheme/PlainSocketFactory.java | 160 ++ .../httpclientandroidlib/conn/scheme/Scheme.java | 260 +++ .../conn/scheme/SchemeLayeredSocketFactory.java | 69 + .../scheme/SchemeLayeredSocketFactoryAdaptor.java | 57 + .../scheme/SchemeLayeredSocketFactoryAdaptor2.java | 74 + .../conn/scheme/SchemeRegistry.java | 168 ++ .../conn/scheme/SchemeSocketFactory.java | 130 ++ .../conn/scheme/SchemeSocketFactoryAdaptor.java | 100 ++ .../conn/scheme/SocketFactory.java | 127 ++ .../conn/scheme/SocketFactoryAdaptor.java | 97 ++ .../conn/scheme/package-info.java | 32 + .../conn/socket/ConnectionSocketFactory.java | 80 + .../socket/LayeredConnectionSocketFactory.java | 63 + .../conn/socket/PlainConnectionSocketFactory.java | 83 + .../conn/socket/package-info.java | 31 + .../conn/ssl/AbstractVerifier.java | 386 +++++ .../conn/ssl/AllowAllHostnameVerifier.java | 54 + .../conn/ssl/BrowserCompatHostnameVerifier.java | 67 + .../conn/ssl/DistinguishedNameParser.java | 131 ++ .../conn/ssl/PrivateKeyDetails.java | 63 + .../conn/ssl/PrivateKeyStrategy.java | 44 + .../conn/ssl/SSLConnectionSocketFactory.java | 295 ++++ .../conn/ssl/SSLContextBuilder.java | 259 +++ .../httpclientandroidlib/conn/ssl/SSLContexts.java | 90 ++ .../conn/ssl/SSLInitializationException.java | 37 + .../conn/ssl/SSLSocketFactory.java | 570 +++++++ .../conn/ssl/StrictHostnameVerifier.java | 69 + .../httpclientandroidlib/conn/ssl/TokenParser.java | 266 ++++ .../conn/ssl/TrustSelfSignedStrategy.java | 45 + .../conn/ssl/TrustStrategy.java | 57 + .../conn/ssl/X509HostnameVerifier.java | 85 + .../conn/ssl/package-info.java | 31 + .../conn/util/InetAddressUtils.java | 123 ++ .../conn/util/package-info.java | 31 + .../httpclientandroidlib/cookie/ClientCookie.java | 62 + .../boye/httpclientandroidlib/cookie/Cookie.java | 137 ++ .../cookie/CookieAttributeHandler.java | 73 + .../cookie/CookieIdentityComparator.java | 80 + .../httpclientandroidlib/cookie/CookieOrigin.java | 94 ++ .../cookie/CookiePathComparator.java | 81 + .../CookieRestrictionViolationException.java | 61 + .../httpclientandroidlib/cookie/CookieSpec.java | 109 ++ .../cookie/CookieSpecFactory.java | 51 + .../cookie/CookieSpecProvider.java | 46 + .../cookie/CookieSpecRegistry.java | 167 ++ .../cookie/MalformedCookieException.java | 71 + .../ch/boye/httpclientandroidlib/cookie/SM.java | 43 + .../httpclientandroidlib/cookie/SetCookie.java | 109 ++ .../httpclientandroidlib/cookie/SetCookie2.java | 60 + .../httpclientandroidlib/cookie/package-info.java | 31 + .../cookie/params/CookieSpecPNames.java | 65 + .../cookie/params/CookieSpecParamBean.java | 62 + .../cookie/params/package-info.java | 32 + .../entity/AbstractHttpEntity.java | 191 +++ .../entity/BasicHttpEntity.java | 125 ++ .../entity/BufferedHttpEntity.java | 125 ++ .../entity/ByteArrayEntity.java | 131 ++ .../entity/ContentLengthStrategy.java | 57 + .../entity/ContentProducer.java | 44 + .../httpclientandroidlib/entity/ContentType.java | 305 ++++ .../entity/EntityTemplate.java | 76 + .../httpclientandroidlib/entity/FileEntity.java | 121 ++ .../entity/HttpEntityWrapper.java | 105 ++ .../entity/InputStreamEntity.java | 155 ++ .../entity/SerializableEntity.java | 126 ++ .../httpclientandroidlib/entity/StringEntity.java | 188 +++ .../entity/mime/AbstractMultipartForm.java | 211 +++ .../entity/mime/FormBodyPart.java | 116 ++ .../httpclientandroidlib/entity/mime/Header.java | 144 ++ .../mime/HttpBrowserCompatibleMultipart.java | 79 + .../entity/mime/HttpMultipartMode.java | 43 + .../entity/mime/HttpRFC6532Multipart.java | 72 + .../entity/mime/HttpStrictMultipart.java | 72 + .../httpclientandroidlib/entity/mime/MIME.java | 53 + .../entity/mime/MinimalField.java | 63 + .../entity/mime/MultipartEntityBuilder.java | 207 +++ .../entity/mime/MultipartFormEntity.java | 100 ++ .../entity/mime/content/AbstractContentBody.java | 96 ++ .../entity/mime/content/ByteArrayBody.java | 111 ++ .../entity/mime/content/ContentBody.java | 43 + .../entity/mime/content/ContentDescriptor.java | 89 ++ .../entity/mime/content/FileBody.java | 144 ++ .../entity/mime/content/InputStreamBody.java | 112 ++ .../entity/mime/content/StringBody.java | 197 +++ .../entity/mime/content/package-info.java | 31 + .../entity/mime/package-info.java | 31 + .../httpclientandroidlib/entity/package-info.java | 31 + .../impl/AbstractHttpClientConnection.java | 323 ++++ .../impl/AbstractHttpServerConnection.java | 310 ++++ .../impl/BHttpConnectionBase.java | 393 +++++ .../httpclientandroidlib/impl/ConnSupport.java | 75 + .../impl/DefaultBHttpClientConnection.java | 182 +++ .../impl/DefaultBHttpClientConnectionFactory.java | 103 ++ .../impl/DefaultBHttpServerConnection.java | 174 ++ .../impl/DefaultBHttpServerConnectionFactory.java | 103 ++ .../impl/DefaultConnectionReuseStrategy.java | 189 +++ .../impl/DefaultHttpClientConnection.java | 70 + .../impl/DefaultHttpRequestFactory.java | 109 ++ .../impl/DefaultHttpResponseFactory.java | 109 ++ .../impl/DefaultHttpServerConnection.java | 71 + .../impl/EnglishReasonPhraseCatalog.java | 224 +++ .../impl/HttpConnectionMetricsImpl.java | 148 ++ .../impl/NoConnectionReuseStrategy.java | 53 + .../impl/SocketHttpClientConnection.java | 283 ++++ .../impl/SocketHttpServerConnection.java | 271 ++++ .../impl/auth/AuthSchemeBase.java | 169 ++ .../impl/auth/BasicScheme.java | 219 +++ .../impl/auth/BasicSchemeFactory.java | 71 + .../impl/auth/DigestScheme.java | 489 ++++++ .../impl/auth/DigestSchemeFactory.java | 71 + .../impl/auth/HttpAuthenticator.java | 245 +++ .../impl/auth/HttpEntityDigester.java | 75 + .../httpclientandroidlib/impl/auth/NTLMEngine.java | 70 + .../impl/auth/NTLMEngineException.java | 67 + .../impl/auth/NTLMEngineImpl.java | 1672 ++++++++++++++++++++ .../httpclientandroidlib/impl/auth/NTLMScheme.java | 164 ++ .../impl/auth/NTLMSchemeFactory.java | 56 + .../impl/auth/RFC2617Scheme.java | 151 ++ .../impl/auth/SpnegoTokenGenerator.java | 47 + .../auth/UnsupportedDigestAlgorithmException.java | 69 + .../impl/auth/package-info.java | 32 + .../impl/client/AIMDBackoffManager.java | 164 ++ .../impl/client/AbstractAuthenticationHandler.java | 189 +++ .../impl/client/AbstractHttpClient.java | 990 ++++++++++++ .../impl/client/AuthenticationStrategyAdaptor.java | 172 ++ .../impl/client/AuthenticationStrategyImpl.java | 245 +++ .../impl/client/AutoRetryHttpClient.java | 190 +++ .../impl/client/BasicAuthCache.java | 105 ++ .../impl/client/BasicCookieStore.java | 144 ++ .../impl/client/BasicCredentialsProvider.java | 109 ++ .../impl/client/BasicResponseHandler.java | 73 + .../impl/client/ClientParamsStack.java | 269 ++++ .../httpclientandroidlib/impl/client/Clock.java | 43 + .../impl/client/CloseableHttpClient.java | 244 +++ .../impl/client/CloseableHttpResponseProxy.java | 87 + .../impl/client/ContentEncodingHttpClient.java | 93 ++ .../impl/client/DecompressingHttpClient.java | 214 +++ .../impl/client/DefaultBackoffStrategy.java | 54 + .../client/DefaultConnectionKeepAliveStrategy.java | 71 + .../impl/client/DefaultHttpClient.java | 225 +++ .../client/DefaultHttpRequestRetryHandler.java | 203 +++ .../client/DefaultProxyAuthenticationHandler.java | 90 ++ .../impl/client/DefaultRedirectHandler.java | 180 +++ .../impl/client/DefaultRedirectStrategy.java | 233 +++ .../client/DefaultRedirectStrategyAdaptor.java | 81 + .../impl/client/DefaultRequestDirector.java | 1150 ++++++++++++++ .../DefaultServiceUnavailableRetryStrategy.java | 80 + .../client/DefaultTargetAuthenticationHandler.java | 91 ++ .../impl/client/DefaultUserTokenHandler.java | 101 ++ .../impl/client/EntityEnclosingRequestWrapper.java | 113 ++ .../impl/client/FutureRequestExecutionMetrics.java | 156 ++ .../impl/client/FutureRequestExecutionService.java | 142 ++ .../impl/client/HttpAuthenticator.java | 61 + .../impl/client/HttpClientBuilder.java | 954 +++++++++++ .../impl/client/HttpClients.java | 85 + .../impl/client/HttpRequestFutureTask.java | 118 ++ .../impl/client/HttpRequestTaskCallable.java | 119 ++ .../impl/client/InternalHttpClient.java | 242 +++ .../impl/client/LaxRedirectStrategy.java | 65 + .../impl/client/MinimalHttpClient.java | 156 ++ .../impl/client/NoopUserTokenHandler.java | 47 + .../impl/client/NullBackoffStrategy.java | 47 + .../impl/client/ProxyAuthenticationStrategy.java | 57 + .../impl/client/ProxyClient.java | 254 +++ .../impl/client/RedirectLocations.java | 227 +++ .../impl/client/RequestWrapper.java | 164 ++ .../impl/client/RoutedRequest.java | 67 + .../client/StandardHttpRequestRetryHandler.java | 80 + .../impl/client/SystemClock.java | 40 + .../client/SystemDefaultCredentialsProvider.java | 145 ++ .../impl/client/SystemDefaultHttpClient.java | 149 ++ .../impl/client/TargetAuthenticationStrategy.java | 57 + .../impl/client/TunnelRefusedException.java | 58 + .../cache/AsynchronousValidationRequest.java | 178 +++ .../impl/client/cache/AsynchronousValidator.java | 150 ++ .../impl/client/cache/BasicHttpCache.java | 376 +++++ .../impl/client/cache/BasicHttpCacheStorage.java | 96 ++ .../impl/client/cache/BasicIdGenerator.java | 86 + .../impl/client/cache/CacheConfig.java | 764 +++++++++ .../impl/client/cache/CacheEntity.java | 99 ++ .../impl/client/cache/CacheEntryUpdater.java | 173 ++ .../impl/client/cache/CacheInvalidator.java | 288 ++++ .../impl/client/cache/CacheKeyGenerator.java | 178 +++ .../impl/client/cache/CacheMap.java | 50 + .../impl/client/cache/CacheValidityPolicy.java | 320 ++++ .../impl/client/cache/CacheableRequestPolicy.java | 96 ++ .../client/cache/CachedHttpResponseGenerator.java | 166 ++ .../cache/CachedResponseSuitabilityChecker.java | 346 ++++ .../impl/client/cache/CachingExec.java | 870 ++++++++++ .../client/cache/CachingHttpClientBuilder.java | 149 ++ .../impl/client/cache/CachingHttpClients.java | 74 + .../impl/client/cache/CombinedEntity.java | 104 ++ .../client/cache/ConditionalRequestBuilder.java | 140 ++ .../impl/client/cache/DefaultFailureCache.java | 143 ++ .../cache/DefaultHttpCacheEntrySerializer.java | 71 + .../ExponentialBackOffSchedulingStrategy.java | 174 ++ .../impl/client/cache/FailureCache.java | 57 + .../impl/client/cache/FailureCacheValue.java | 67 + .../impl/client/cache/FileResource.java | 77 + .../impl/client/cache/FileResourceFactory.java | 111 ++ .../impl/client/cache/HeapResource.java | 67 + .../impl/client/cache/HeapResourceFactory.java | 83 + .../impl/client/cache/HttpCache.java | 166 ++ .../impl/client/cache/IOUtils.java | 109 ++ .../client/cache/ImmediateSchedulingStrategy.java | 88 ++ .../impl/client/cache/ManagedHttpCacheStorage.java | 163 ++ .../impl/client/cache/OptionsHttp11Response.java | 182 +++ .../impl/client/cache/Proxies.java | 56 + .../client/cache/RequestProtocolCompliance.java | 376 +++++ .../impl/client/cache/RequestProtocolError.java | 40 + .../impl/client/cache/ResourceReference.java | 62 + .../impl/client/cache/ResponseCachingPolicy.java | 311 ++++ .../client/cache/ResponseProtocolCompliance.java | 251 +++ .../impl/client/cache/ResponseProxyHandler.java | 88 ++ .../impl/client/cache/SchedulingStrategy.java | 45 + .../client/cache/SizeLimitedResponseReader.java | 150 ++ .../impl/client/cache/Variant.java | 55 + .../impl/client/cache/WarningValue.java | 370 +++++ .../impl/client/cache/package.html | 42 + .../impl/client/package-info.java | 51 + .../impl/conn/AbstractClientConnAdapter.java | 369 +++++ .../impl/conn/AbstractPoolEntry.java | 262 +++ .../impl/conn/AbstractPooledConnAdapter.java | 191 +++ .../impl/conn/BasicClientConnectionManager.java | 276 ++++ .../conn/BasicHttpClientConnectionManager.java | 370 +++++ .../boye/httpclientandroidlib/impl/conn/CPool.java | 67 + .../httpclientandroidlib/impl/conn/CPoolEntry.java | 101 ++ .../httpclientandroidlib/impl/conn/CPoolProxy.java | 245 +++ .../impl/conn/ConnectionShutdownException.java | 50 + .../impl/conn/DefaultClientConnection.java | 292 ++++ .../impl/conn/DefaultClientConnectionOperator.java | 263 +++ .../impl/conn/DefaultHttpResponseParser.java | 168 ++ .../conn/DefaultHttpResponseParserFactory.java | 77 + .../impl/conn/DefaultHttpRoutePlanner.java | 123 ++ .../conn/DefaultManagedHttpClientConnection.java | 135 ++ .../impl/conn/DefaultProxyRoutePlanner.java | 66 + .../impl/conn/DefaultResponseParser.java | 125 ++ .../impl/conn/DefaultRoutePlanner.java | 107 ++ .../impl/conn/DefaultSchemePortResolver.java | 61 + .../impl/conn/HttpClientConnectionOperator.java | 173 ++ .../impl/conn/HttpConnPool.java | 84 + .../impl/conn/HttpPoolEntry.java | 98 ++ .../impl/conn/IdleConnectionHandler.java | 181 +++ .../impl/conn/InMemoryDnsResolver.java | 94 ++ .../impl/conn/LoggingInputStream.java | 145 ++ .../conn/LoggingManagedHttpClientConnection.java | 132 ++ .../impl/conn/LoggingOutputStream.java | 104 ++ .../impl/conn/LoggingSessionInputBuffer.java | 138 ++ .../impl/conn/LoggingSessionOutputBuffer.java | 118 ++ .../impl/conn/ManagedClientConnectionImpl.java | 461 ++++++ .../conn/ManagedHttpClientConnectionFactory.java | 121 ++ .../impl/conn/PoolingClientConnectionManager.java | 328 ++++ .../conn/PoolingHttpClientConnectionManager.java | 516 ++++++ .../impl/conn/ProxySelectorRoutePlanner.java | 279 ++++ .../impl/conn/SchemeRegistryFactory.java | 90 ++ .../impl/conn/SingleClientConnManager.java | 427 +++++ .../impl/conn/SystemDefaultDnsResolver.java | 47 + .../impl/conn/SystemDefaultRoutePlanner.java | 132 ++ .../boye/httpclientandroidlib/impl/conn/Wire.java | 152 ++ .../impl/conn/package-info.java | 32 + .../impl/conn/tsccm/AbstractConnPool.java | 234 +++ .../impl/conn/tsccm/BasicPoolEntry.java | 163 ++ .../impl/conn/tsccm/BasicPoolEntryRef.java | 76 + .../impl/conn/tsccm/BasicPooledConnAdapter.java | 75 + .../impl/conn/tsccm/ConnPoolByRoute.java | 829 ++++++++++ .../impl/conn/tsccm/PoolEntryRequest.java | 69 + .../impl/conn/tsccm/RouteSpecificPool.java | 313 ++++ .../conn/tsccm/ThreadSafeClientConnManager.java | 377 +++++ .../impl/conn/tsccm/WaitingThread.java | 198 +++ .../impl/conn/tsccm/WaitingThreadAborter.java | 69 + .../impl/conn/tsccm/package-info.java | 33 + .../cookie/AbstractCookieAttributeHandler.java | 52 + .../impl/cookie/AbstractCookieSpec.java | 104 ++ .../impl/cookie/BasicClientCookie.java | 361 +++++ .../impl/cookie/BasicClientCookie2.java | 101 ++ .../impl/cookie/BasicCommentHandler.java | 51 + .../impl/cookie/BasicDomainHandler.java | 116 ++ .../impl/cookie/BasicExpiresHandler.java | 66 + .../impl/cookie/BasicMaxAgeHandler.java | 67 + .../impl/cookie/BasicPathHandler.java | 87 + .../impl/cookie/BasicSecureHandler.java | 60 + .../impl/cookie/BestMatchSpec.java | 207 +++ .../impl/cookie/BestMatchSpecFactory.java | 86 + .../impl/cookie/BrowserCompatSpec.java | 219 +++ .../impl/cookie/BrowserCompatSpecFactory.java | 92 ++ .../BrowserCompatVersionAttributeHandler.java | 66 + .../impl/cookie/CookieSpecBase.java | 121 ++ .../impl/cookie/DateParseException.java | 62 + .../impl/cookie/DateUtils.java | 156 ++ .../impl/cookie/IgnoreSpec.java | 63 + .../impl/cookie/IgnoreSpecFactory.java | 58 + .../impl/cookie/NetscapeDomainHandler.java | 106 ++ .../impl/cookie/NetscapeDraftHeaderParser.java | 138 ++ .../impl/cookie/NetscapeDraftSpec.java | 171 ++ .../impl/cookie/NetscapeDraftSpecFactory.java | 81 + .../impl/cookie/PublicSuffixFilter.java | 132 ++ .../impl/cookie/PublicSuffixListParser.java | 127 ++ .../impl/cookie/RFC2109DomainHandler.java | 120 ++ .../impl/cookie/RFC2109Spec.java | 240 +++ .../impl/cookie/RFC2109SpecFactory.java | 86 + .../impl/cookie/RFC2109VersionHandler.java | 74 + .../cookie/RFC2965CommentUrlAttributeHandler.java | 66 + .../cookie/RFC2965DiscardAttributeHandler.java | 66 + .../impl/cookie/RFC2965DomainAttributeHandler.java | 185 +++ .../impl/cookie/RFC2965PortAttributeHandler.java | 160 ++ .../impl/cookie/RFC2965Spec.java | 239 +++ .../impl/cookie/RFC2965SpecFactory.java | 86 + .../cookie/RFC2965VersionAttributeHandler.java | 94 ++ .../impl/cookie/package-info.java | 32 + .../DisallowIdentityContentLengthStrategy.java | 63 + .../impl/entity/EntityDeserializer.java | 143 ++ .../impl/entity/EntitySerializer.java | 121 ++ .../impl/entity/LaxContentLengthStrategy.java | 126 ++ .../impl/entity/StrictContentLengthStrategy.java | 116 ++ .../impl/entity/package-info.java | 31 + .../impl/execchain/BackoffStrategyExec.java | 104 ++ .../impl/execchain/ClientExecChain.java | 74 + .../impl/execchain/ConnectionHolder.java | 153 ++ .../impl/execchain/HttpResponseProxy.java | 185 +++ .../impl/execchain/MainClientExec.java | 568 +++++++ .../impl/execchain/MinimalClientExec.java | 251 +++ .../impl/execchain/ProtocolExec.java | 214 +++ .../impl/execchain/RedirectExec.java | 185 +++ .../impl/execchain/RequestAbortedException.java | 55 + .../impl/execchain/RequestEntityProxy.java | 137 ++ .../impl/execchain/ResponseEntityProxy.java | 152 ++ .../impl/execchain/RetryExec.java | 126 ++ .../execchain/ServiceUnavailableRetryExec.java | 108 ++ .../impl/execchain/TunnelRefusedException.java | 55 + .../impl/execchain/package-info.java | 31 + .../impl/io/AbstractMessageParser.java | 284 ++++ .../impl/io/AbstractMessageWriter.java | 119 ++ .../impl/io/AbstractSessionInputBuffer.java | 401 +++++ .../impl/io/AbstractSessionOutputBuffer.java | 307 ++++ .../impl/io/ChunkedInputStream.java | 301 ++++ .../impl/io/ChunkedOutputStream.java | 208 +++ .../impl/io/ContentLengthInputStream.java | 232 +++ .../impl/io/ContentLengthOutputStream.java | 136 ++ .../impl/io/DefaultHttpRequestParser.java | 140 ++ .../impl/io/DefaultHttpRequestParserFactory.java | 71 + .../impl/io/DefaultHttpRequestWriter.java | 69 + .../impl/io/DefaultHttpRequestWriterFactory.java | 63 + .../impl/io/DefaultHttpResponseParser.java | 141 ++ .../impl/io/DefaultHttpResponseParserFactory.java | 71 + .../impl/io/DefaultHttpResponseWriter.java | 69 + .../impl/io/DefaultHttpResponseWriterFactory.java | 63 + .../impl/io/HttpRequestParser.java | 102 ++ .../impl/io/HttpRequestWriter.java | 62 + .../impl/io/HttpResponseParser.java | 103 ++ .../impl/io/HttpResponseWriter.java | 62 + .../impl/io/HttpTransportMetricsImpl.java | 63 + .../impl/io/IdentityInputStream.java | 99 ++ .../impl/io/IdentityOutputStream.java | 104 ++ .../impl/io/SessionInputBufferImpl.java | 399 +++++ .../impl/io/SessionOutputBufferImpl.java | 283 ++++ .../impl/io/SocketInputBuffer.java | 108 ++ .../impl/io/SocketOutputBuffer.java | 75 + .../httpclientandroidlib/impl/io/package-info.java | 32 + .../httpclientandroidlib/impl/package-info.java | 32 + .../impl/pool/BasicConnFactory.java | 177 +++ .../impl/pool/BasicConnPool.java | 89 ++ .../impl/pool/BasicPoolEntry.java | 64 + .../impl/pool/package-info.java | 32 + .../boye/httpclientandroidlib/io/BufferInfo.java | 58 + .../ch/boye/httpclientandroidlib/io/EofSensor.java | 42 + .../httpclientandroidlib/io/HttpMessageParser.java | 56 + .../io/HttpMessageParserFactory.java | 42 + .../httpclientandroidlib/io/HttpMessageWriter.java | 54 + .../io/HttpMessageWriterFactory.java | 41 + .../io/HttpTransportMetrics.java | 48 + .../io/SessionInputBuffer.java | 152 ++ .../io/SessionOutputBuffer.java | 120 ++ .../boye/httpclientandroidlib/io/package-info.java | 32 + .../message/AbstractHttpMessage.java | 164 ++ .../httpclientandroidlib/message/BasicHeader.java | 91 ++ .../message/BasicHeaderElement.java | 162 ++ .../message/BasicHeaderElementIterator.java | 151 ++ .../message/BasicHeaderIterator.java | 175 ++ .../message/BasicHeaderValueFormatter.java | 422 +++++ .../message/BasicHeaderValueParser.java | 359 +++++ .../message/BasicHttpEntityEnclosingRequest.java | 75 + .../message/BasicHttpRequest.java | 114 ++ .../message/BasicHttpResponse.java | 218 +++ .../message/BasicLineFormatter.java | 319 ++++ .../message/BasicLineParser.java | 456 ++++++ .../message/BasicListHeaderIterator.java | 190 +++ .../message/BasicNameValuePair.java | 111 ++ .../message/BasicRequestLine.java | 83 + .../message/BasicStatusLine.java | 100 ++ .../message/BasicTokenIterator.java | 414 +++++ .../message/BufferedHeader.java | 130 ++ .../httpclientandroidlib/message/HeaderGroup.java | 311 ++++ .../message/HeaderValueFormatter.java | 122 ++ .../message/HeaderValueParser.java | 134 ++ .../message/LineFormatter.java | 131 ++ .../httpclientandroidlib/message/LineParser.java | 137 ++ .../httpclientandroidlib/message/ParserCursor.java | 100 ++ .../httpclientandroidlib/message/package-info.java | 32 + .../ch/boye/httpclientandroidlib/package-info.java | 42 + .../params/AbstractHttpParams.java | 124 ++ .../params/BasicHttpParams.java | 190 +++ .../params/CoreConnectionPNames.java | 170 ++ .../params/CoreProtocolPNames.java | 152 ++ .../params/DefaultedHttpParams.java | 163 ++ .../params/HttpAbstractParamBean.java | 48 + .../params/HttpConnectionParamBean.java | 71 + .../params/HttpConnectionParams.java | 243 +++ .../params/HttpParamConfig.java | 78 + .../httpclientandroidlib/params/HttpParams.java | 195 +++ .../params/HttpParamsNames.java | 57 + .../params/HttpProtocolParamBean.java | 69 + .../params/HttpProtocolParams.java | 240 +++ .../params/SyncBasicHttpParams.java | 89 ++ .../httpclientandroidlib/params/package-info.java | 32 + .../pool/AbstractConnPool.java | 533 +++++++ .../httpclientandroidlib/pool/ConnFactory.java | 44 + .../boye/httpclientandroidlib/pool/ConnPool.java | 68 + .../httpclientandroidlib/pool/ConnPoolControl.java | 56 + .../boye/httpclientandroidlib/pool/PoolEntry.java | 183 +++ .../pool/PoolEntryCallback.java | 41 + .../httpclientandroidlib/pool/PoolEntryFuture.java | 155 ++ .../boye/httpclientandroidlib/pool/PoolStats.java | 114 ++ .../pool/RouteSpecificPool.java | 184 +++ .../httpclientandroidlib/pool/package-info.java | 32 + .../protocol/BasicHttpContext.java | 95 ++ .../protocol/BasicHttpProcessor.java | 246 +++ .../protocol/ChainBuilder.java | 126 ++ .../protocol/DefaultedHttpContext.java | 84 + .../protocol/ExecutionContext.java | 80 + .../boye/httpclientandroidlib/protocol/HTTP.java | 135 ++ .../httpclientandroidlib/protocol/HttpContext.java | 75 + .../protocol/HttpCoreContext.java | 152 ++ .../protocol/HttpDateGenerator.java | 77 + .../protocol/HttpExpectationVerifier.java | 81 + .../protocol/HttpProcessor.java | 55 + .../protocol/HttpProcessorBuilder.java | 151 ++ .../protocol/HttpRequestExecutor.java | 311 ++++ .../protocol/HttpRequestHandler.java | 62 + .../protocol/HttpRequestHandlerMapper.java | 50 + .../protocol/HttpRequestHandlerRegistry.java | 107 ++ .../protocol/HttpRequestHandlerResolver.java | 51 + .../protocol/HttpRequestInterceptorList.java | 103 ++ .../protocol/HttpResponseInterceptorList.java | 103 ++ .../httpclientandroidlib/protocol/HttpService.java | 447 ++++++ .../protocol/ImmutableHttpProcessor.java | 143 ++ .../protocol/RequestConnControl.java | 69 + .../protocol/RequestContent.java | 127 ++ .../httpclientandroidlib/protocol/RequestDate.java | 65 + .../protocol/RequestExpectContinue.java | 93 ++ .../protocol/RequestTargetHost.java | 95 ++ .../protocol/RequestUserAgent.java | 79 + .../protocol/ResponseConnControl.java | 105 ++ .../protocol/ResponseContent.java | 133 ++ .../protocol/ResponseDate.java | 66 + .../protocol/ResponseServer.java | 71 + .../protocol/SyncBasicHttpContext.java | 74 + .../protocol/UriHttpRequestHandlerMapper.java | 115 ++ .../protocol/UriPatternMatcher.java | 165 ++ .../protocol/package-info.java | 32 + .../ch/boye/httpclientandroidlib/util/Args.java | 111 ++ .../ch/boye/httpclientandroidlib/util/Asserts.java | 62 + .../httpclientandroidlib/util/ByteArrayBuffer.java | 347 ++++ .../httpclientandroidlib/util/CharArrayBuffer.java | 464 ++++++ .../httpclientandroidlib/util/CharsetUtils.java | 58 + .../httpclientandroidlib/util/EncodingUtils.java | 152 ++ .../httpclientandroidlib/util/EntityUtils.java | 291 ++++ .../httpclientandroidlib/util/ExceptionUtils.java | 82 + .../boye/httpclientandroidlib/util/LangUtils.java | 101 ++ .../boye/httpclientandroidlib/util/NetUtils.java | 54 + .../boye/httpclientandroidlib/util/TextUtils.java | 54 + .../httpclientandroidlib/util/VersionInfo.java | 324 ++++ .../httpclientandroidlib/util/package-info.java | 31 + .../thirdparty/com/adjust/sdk/ActivityHandler.java | 781 +++++++++ .../thirdparty/com/adjust/sdk/ActivityKind.java | 35 + .../thirdparty/com/adjust/sdk/ActivityPackage.java | 100 ++ .../thirdparty/com/adjust/sdk/ActivityState.java | 151 ++ .../android/thirdparty/com/adjust/sdk/Adjust.java | 79 + .../com/adjust/sdk/AdjustAttribution.java | 62 + .../thirdparty/com/adjust/sdk/AdjustConfig.java | 128 ++ .../thirdparty/com/adjust/sdk/AdjustEvent.java | 112 ++ .../thirdparty/com/adjust/sdk/AdjustFactory.java | 141 ++ .../thirdparty/com/adjust/sdk/AdjustInstance.java | 86 + .../com/adjust/sdk/AdjustReferrerReceiver.java | 35 + .../com/adjust/sdk/AttributionHandler.java | 155 ++ .../thirdparty/com/adjust/sdk/Constants.java | 53 + .../thirdparty/com/adjust/sdk/DeviceInfo.java | 290 ++++ .../com/adjust/sdk/IActivityHandler.java | 36 + .../com/adjust/sdk/IAttributionHandler.java | 20 + .../android/thirdparty/com/adjust/sdk/ILogger.java | 20 + .../thirdparty/com/adjust/sdk/IPackageHandler.java | 27 + .../thirdparty/com/adjust/sdk/IRequestHandler.java | 9 + mobile/android/thirdparty/com/adjust/sdk/LICENSE | 21 + .../thirdparty/com/adjust/sdk/LogLevel.java | 19 + .../android/thirdparty/com/adjust/sdk/Logger.java | 107 ++ .../adjust/sdk/OnAttributionChangedListener.java | 5 + .../thirdparty/com/adjust/sdk/PackageBuilder.java | 291 ++++ .../thirdparty/com/adjust/sdk/PackageHandler.java | 274 ++++ .../thirdparty/com/adjust/sdk/Reflection.java | 210 +++ .../thirdparty/com/adjust/sdk/RequestHandler.java | 210 +++ .../com/adjust/sdk/UnitTestActivity.java | 38 + mobile/android/thirdparty/com/adjust/sdk/Util.java | 202 +++ .../com/adjust/sdk/plugin/AndroidIdUtil.java | 10 + .../com/adjust/sdk/plugin/MacAddressUtil.java | 82 + .../thirdparty/com/adjust/sdk/plugin/Plugin.java | 12 + .../com/jakewharton/disklrucache/DiskLruCache.java | 943 +++++++++++ .../jakewharton/disklrucache/StrictLineReader.java | 196 +++ .../com/jakewharton/disklrucache/Util.java | 77 + .../keepsafe/switchboard/AsyncConfigLoader.java | 54 + .../keepsafe/switchboard/DeviceUuidFactory.java | 70 + .../com/keepsafe/switchboard/Preferences.java | 105 ++ .../com/keepsafe/switchboard/Switch.java | 72 + .../com/keepsafe/switchboard/SwitchBoard.java | 390 +++++ .../com/squareup/leakcanary/LeakCanary.java | 21 + .../com/squareup/leakcanary/RefWatcher.java | 20 + .../thirdparty/com/squareup/picasso/Action.java | 83 + .../com/squareup/picasso/AssetBitmapHunter.java | 51 + .../com/squareup/picasso/BitmapHunter.java | 357 +++++ .../thirdparty/com/squareup/picasso/Cache.java | 64 + .../thirdparty/com/squareup/picasso/Callback.java | 31 + .../picasso/ContactsPhotoBitmapHunter.java | 130 ++ .../picasso/ContentStreamBitmapHunter.java | 67 + .../squareup/picasso/DeferredRequestCreator.java | 70 + .../com/squareup/picasso/Dispatcher.java | 315 ++++ .../com/squareup/picasso/Downloader.java | 99 ++ .../com/squareup/picasso/FetchAction.java | 30 + .../com/squareup/picasso/FileBitmapHunter.java | 57 + .../thirdparty/com/squareup/picasso/GetAction.java | 30 + .../com/squareup/picasso/ImageViewAction.java | 75 + .../thirdparty/com/squareup/picasso/LruCache.java | 146 ++ .../com/squareup/picasso/MarkableInputStream.java | 157 ++ .../squareup/picasso/MediaStoreBitmapHunter.java | 116 ++ .../com/squareup/picasso/NetworkBitmapHunter.java | 113 ++ .../thirdparty/com/squareup/picasso/Picasso.java | 522 ++++++ .../com/squareup/picasso/PicassoDrawable.java | 186 +++ .../squareup/picasso/PicassoExecutorService.java | 81 + .../thirdparty/com/squareup/picasso/Request.java | 307 ++++ .../com/squareup/picasso/RequestCreator.java | 374 +++++ .../com/squareup/picasso/ResourceBitmapHunter.java | 55 + .../thirdparty/com/squareup/picasso/Stats.java | 143 ++ .../com/squareup/picasso/StatsSnapshot.java | 120 ++ .../thirdparty/com/squareup/picasso/Target.java | 45 + .../com/squareup/picasso/TargetAction.java | 46 + .../com/squareup/picasso/Transformation.java | 34 + .../squareup/picasso/UrlConnectionDownloader.java | 100 ++ .../thirdparty/com/squareup/picasso/Utils.java | 304 ++++ .../thirdparty/org/json/simple/ItemList.java | 147 ++ .../thirdparty/org/json/simple/JSONArray.java | 107 ++ .../thirdparty/org/json/simple/JSONAware.java | 12 + .../thirdparty/org/json/simple/JSONObject.java | 129 ++ .../org/json/simple/JSONStreamAware.java | 15 + .../thirdparty/org/json/simple/JSONValue.java | 272 ++++ .../android/thirdparty/org/json/simple/LICENSE.txt | 202 +++ .../org/json/simple/parser/ContainerFactory.java | 23 + .../org/json/simple/parser/ContentHandler.java | 110 ++ .../org/json/simple/parser/JSONParser.java | 533 +++++++ .../org/json/simple/parser/ParseException.java | 96 ++ .../thirdparty/org/json/simple/parser/Yylex.java | 688 ++++++++ .../thirdparty/org/json/simple/parser/Yytoken.java | 58 + .../thirdparty/org/lucasr/dspec/DesignSpec.java | 645 ++++++++ .../thirdparty/org/lucasr/dspec/RawResource.java | 60 + .../apache/commons/codec/BinaryDecoder.java | 43 + .../apache/commons/codec/BinaryEncoder.java | 43 + .../mozilla/apache/commons/codec/CharEncoding.java | 127 ++ .../org/mozilla/apache/commons/codec/Decoder.java | 56 + .../apache/commons/codec/DecoderException.java | 90 ++ .../org/mozilla/apache/commons/codec/Encoder.java | 47 + .../apache/commons/codec/EncoderException.java | 91 ++ .../apache/commons/codec/StringDecoder.java | 41 + .../apache/commons/codec/StringEncoder.java | 41 + .../commons/codec/StringEncoderComparator.java | 87 + .../apache/commons/codec/binary/Base32.java | 471 ++++++ .../commons/codec/binary/Base32InputStream.java | 85 + .../commons/codec/binary/Base32OutputStream.java | 85 + .../apache/commons/codec/binary/Base64.java | 756 +++++++++ .../commons/codec/binary/Base64InputStream.java | 89 ++ .../commons/codec/binary/Base64OutputStream.java | 89 ++ .../apache/commons/codec/binary/BaseNCodec.java | 445 ++++++ .../codec/binary/BaseNCodecInputStream.java | 132 ++ .../codec/binary/BaseNCodecOutputStream.java | 142 ++ .../apache/commons/codec/binary/BinaryCodec.java | 297 ++++ .../mozilla/apache/commons/codec/binary/Hex.java | 302 ++++ .../apache/commons/codec/binary/StringUtils.java | 287 ++++ .../apache/commons/codec/binary/package.html | 21 + .../apache/commons/codec/digest/DigestUtils.java | 583 +++++++ .../apache/commons/codec/digest/package.html | 21 + .../commons/codec/language/AbstractCaverphone.java | 78 + .../apache/commons/codec/language/Caverphone.java | 104 ++ .../apache/commons/codec/language/Caverphone1.java | 126 ++ .../apache/commons/codec/language/Caverphone2.java | 129 ++ .../commons/codec/language/ColognePhonetic.java | 417 +++++ .../commons/codec/language/DoubleMetaphone.java | 1106 +++++++++++++ .../apache/commons/codec/language/Metaphone.java | 408 +++++ .../commons/codec/language/RefinedSoundex.java | 203 +++ .../apache/commons/codec/language/Soundex.java | 279 ++++ .../commons/codec/language/SoundexUtils.java | 124 ++ .../apache/commons/codec/language/package.html | 21 + .../mozilla/apache/commons/codec/net/BCodec.java | 209 +++ .../mozilla/apache/commons/codec/net/QCodec.java | 312 ++++ .../commons/codec/net/QuotedPrintableCodec.java | 388 +++++ .../apache/commons/codec/net/RFC1522Codec.java | 179 +++ .../mozilla/apache/commons/codec/net/URLCodec.java | 362 +++++ .../mozilla/apache/commons/codec/net/Utils.java | 50 + .../mozilla/apache/commons/codec/net/package.html | 23 + .../org/mozilla/apache/commons/codec/overview.html | 29 + .../org/mozilla/apache/commons/codec/package.html | 100 ++ 844 files changed, 117184 insertions(+) create mode 100644 mobile/android/thirdparty/AndroidManifest.xml create mode 100644 mobile/android/thirdparty/README create mode 100644 mobile/android/thirdparty/build.gradle create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionClosedException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionReuseStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/Consts.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/ContentTooLongException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/FormattedHeader.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/Header.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElement.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElementIterator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderIterator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionMetrics.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntityEnclosingRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHeaders.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHost.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpInetConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpMessage.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestInterceptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponse.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseInterceptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpServerConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpStatus.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpVersion.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/MalformedChunkCodingException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/MessageConstraintException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/MethodNotSupportedException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/NameValuePair.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/NoHttpResponseException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/ParseException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolVersion.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/README.txt create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/ReasonPhraseCatalog.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/RequestLine.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/StatusLine.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/TokenIterator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/TruncatedChunkException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/UnsupportedHttpVersionException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/Base64.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/HttpClientAndroidLog.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/GuardedBy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/Immutable.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/NotThreadSafe.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/ThreadSafe.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AUTH.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthOption.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthProtocolState.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScheme.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeProvider.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeRegistry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScope.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthState.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthenticationException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/BasicUserPrincipal.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ChallengeState.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ContextAwareAuthScheme.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/Credentials.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/InvalidCredentialsException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/MalformedChallengeException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTCredentials.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTUserPrincipal.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/UsernamePasswordCredentials.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/BackoffManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CircularRedirectException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ClientProtocolException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ConnectionBackoffStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CookieStore.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CredentialsProvider.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpRequestRetryHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpResponseException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/NonRepeatableRequestException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RequestDirector.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ResponseHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ServiceUnavailableRetryStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/UserTokenHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/CacheResponseStatus.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HeaderConstants.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializationException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheInvalidator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheStorage.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateCallback.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/InputLimit.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/Resource.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/ResourceFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/package.html create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/AuthSchemes.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/CookieSpecs.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/RequestConfig.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DecompressingEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateDecompressingEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateInputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/EntityBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipCompressingEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipDecompressingEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/LazyDecompressingInputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/UrlEncodedFormEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbortableHttpRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbstractExecutionAwareRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/CloseableHttpResponse.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/Configurable.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpDelete.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpEntityEnclosingRequestBase.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpExecutionAware.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpGet.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpHead.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpOptions.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPatch.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPost.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPut.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestBase.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestWrapper.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpTrace.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpUriRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/RequestBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AllClientPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AuthPolicy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/CookiePolicy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParamConfig.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContextConfigurer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/HttpClientContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAcceptEncoding.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAddCookies.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthenticationBase.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestClientConnControl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestDefaultHeaders.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestExpectContinue.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestProxyAuthentication.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestTargetAuthentication.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseAuthCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseContentEncoding.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseProcessCookies.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/CloneUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/DateUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/HttpClientUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Idn.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/JdkIdn.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Punycode.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Rfc3492Idn.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URLEncodedUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/BasicFuture.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/Cancellable.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/FutureCallback.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/ConnectionConfig.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Lookup.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/MessageConstraints.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Registry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/RegistryBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/SocketConfig.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicEofSensorWatcher.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicManagedEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManagerFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionOperator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectTimeoutException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionKeepAliveStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionPoolTimeoutException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionReleaseTrigger.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/DnsResolver.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorInputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorWatcher.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpClientConnectionManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpConnectionFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpHostConnectException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpInetSocketAddress.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpRoutedConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedHttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/MultihomePlainSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/OperatedClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/SchemePortResolver.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/UnsupportedSchemeException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRoute.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRouteBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRoutePNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/BasicRouteDirector.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoute.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRouteDirector.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoutePlanner.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteInfo.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteTracker.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/HostNameResolver.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactoryAdaptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/PlainSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/Scheme.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor2.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeRegistry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactoryAdaptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactoryAdaptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/ConnectionSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/LayeredConnectionSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/PlainConnectionSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AbstractVerifier.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AllowAllHostnameVerifier.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/BrowserCompatHostnameVerifier.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/DistinguishedNameParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyDetails.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLConnectionSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContextBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContexts.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLInitializationException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLSocketFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/StrictHostnameVerifier.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TokenParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustSelfSignedStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/X509HostnameVerifier.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/InetAddressUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/ClientCookie.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/Cookie.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieIdentityComparator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieOrigin.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookiePathComparator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieRestrictionViolationException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecProvider.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecRegistry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/MalformedCookieException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SM.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie2.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/AbstractHttpEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BasicHttpEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BufferedHttpEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ByteArrayEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentLengthStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentProducer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentType.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/EntityTemplate.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/FileEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/HttpEntityWrapper.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/InputStreamEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/SerializableEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/StringEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/AbstractMultipartForm.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/FormBodyPart.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/Header.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpBrowserCompatibleMultipart.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpMultipartMode.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpRFC6532Multipart.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpStrictMultipart.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MIME.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MinimalField.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartEntityBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartFormEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/AbstractContentBody.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ByteArrayBody.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentBody.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentDescriptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/FileBody.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/InputStreamBody.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/StringBody.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpServerConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/BHttpConnectionBase.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/ConnSupport.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnectionFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnectionFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultConnectionReuseStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpRequestFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpResponseFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpServerConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/EnglishReasonPhraseCatalog.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/HttpConnectionMetricsImpl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/NoConnectionReuseStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpServerConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/AuthSchemeBase.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicScheme.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicSchemeFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestScheme.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestSchemeFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpAuthenticator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpEntityDigester.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngine.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMScheme.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMSchemeFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/RFC2617Scheme.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AIMDBackoffManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractAuthenticationHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyAdaptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyImpl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AutoRetryHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicAuthCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCookieStore.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCredentialsProvider.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicResponseHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ClientParamsStack.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/Clock.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpResponseProxy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ContentEncodingHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DecompressingHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultBackoffStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultConnectionKeepAliveStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpRequestRetryHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultProxyAuthenticationHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategyAdaptor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRequestDirector.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultServiceUnavailableRetryStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultTargetAuthenticationHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultUserTokenHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/EntityEnclosingRequestWrapper.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionMetrics.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionService.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpAuthenticator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClientBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClients.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestFutureTask.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestTaskCallable.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/InternalHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/LaxRedirectStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/MinimalHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NoopUserTokenHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NullBackoffStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyAuthenticationStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RedirectLocations.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RequestWrapper.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RoutedRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/StandardHttpRequestRetryHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemClock.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultCredentialsProvider.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultHttpClient.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TargetAuthenticationStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TunnelRefusedException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidationRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCacheStorage.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicIdGenerator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheConfig.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntryUpdater.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheInvalidator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheKeyGenerator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheMap.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheValidityPolicy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheableRequestPolicy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedHttpResponseGenerator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedResponseSuitabilityChecker.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClientBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClients.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CombinedEntity.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ConditionalRequestBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultFailureCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultHttpCacheEntrySerializer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ExponentialBackOffSchedulingStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCacheValue.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResource.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResourceFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResource.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResourceFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HttpCache.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/IOUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ImmediateSchedulingStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ManagedHttpCacheStorage.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/OptionsHttp11Response.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Proxies.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolCompliance.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolError.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResourceReference.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseCachingPolicy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProtocolCompliance.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProxyHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SchedulingStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SizeLimitedResponseReader.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Variant.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/WarningValue.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/package.html create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPoolEntry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicClientConnectionManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicHttpClientConnectionManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolEntry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolProxy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ConnectionShutdownException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParserFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultManagedHttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultProxyRoutePlanner.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultResponseParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultRoutePlanner.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultSchemePortResolver.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpClientConnectionOperator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpConnPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpPoolEntry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/IdleConnectionHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/InMemoryDnsResolver.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingInputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingManagedHttpClientConnection.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingOutputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedClientConnectionImpl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedHttpClientConnectionFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingClientConnectionManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingHttpClientConnectionManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SchemeRegistryFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultDnsResolver.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultRoutePlanner.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/Wire.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThread.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieSpec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie2.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicCommentHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicDomainHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicExpiresHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicMaxAgeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicPathHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicSecureHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpecFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpecFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatVersionAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/CookieSpecBase.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateParseException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpecFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDomainHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftHeaderParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpecFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixFilter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixListParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109DomainHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109Spec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109SpecFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109VersionHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965CommentUrlAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DiscardAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DomainAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965PortAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965Spec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965SpecFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965VersionAttributeHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/DisallowIdentityContentLengthStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntityDeserializer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntitySerializer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/LaxContentLengthStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/StrictContentLengthStrategy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/BackoffStrategyExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ClientExecChain.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ConnectionHolder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/HttpResponseProxy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MainClientExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MinimalClientExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ProtocolExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RedirectExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestAbortedException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestEntityProxy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ResponseEntityProxy.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RetryExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ServiceUnavailableRetryExec.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/TunnelRefusedException.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageWriter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionInputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionOutputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedInputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedOutputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthInputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthOutputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParserFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriterFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParserFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriterFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestWriter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseWriter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpTransportMetricsImpl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityInputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityOutputStream.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionInputBufferImpl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionOutputBufferImpl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketInputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketOutputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicPoolEntry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/BufferInfo.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/EofSensor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParserFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriterFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpTransportMetrics.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionInputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionOutputBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/AbstractHttpMessage.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeader.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElement.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElementIterator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderIterator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueFormatter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpEntityEnclosingRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpRequest.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpResponse.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineFormatter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicListHeaderIterator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicNameValuePair.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicRequestLine.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicStatusLine.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicTokenIterator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BufferedHeader.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderGroup.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueFormatter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineFormatter.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineParser.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/ParserCursor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/AbstractHttpParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/BasicHttpParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreConnectionPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreProtocolPNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/DefaultedHttpParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpAbstractParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamConfig.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamsNames.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParamBean.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/SyncBasicHttpParams.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/AbstractConnPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnFactory.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPoolControl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryCallback.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryFuture.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolStats.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/RouteSpecificPool.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpProcessor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ChainBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/DefaultedHttpContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ExecutionContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HTTP.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpCoreContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpDateGenerator.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpExpectationVerifier.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessorBuilder.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestExecutor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandler.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerMapper.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerRegistry.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerResolver.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestInterceptorList.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpResponseInterceptorList.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpService.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ImmutableHttpProcessor.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestConnControl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestContent.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestDate.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestExpectContinue.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestTargetHost.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestUserAgent.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseConnControl.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseContent.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseDate.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseServer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/SyncBasicHttpContext.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriHttpRequestHandlerMapper.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriPatternMatcher.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/package-info.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Args.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Asserts.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ByteArrayBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharArrayBuffer.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharsetUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EncodingUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EntityUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ExceptionUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/LangUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/NetUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/TextUtils.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/VersionInfo.java create mode 100644 mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/package-info.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/ActivityHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/ActivityKind.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/ActivityPackage.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/ActivityState.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/Adjust.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/AdjustAttribution.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/AdjustConfig.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/AdjustEvent.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/AdjustFactory.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/AdjustInstance.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/AdjustReferrerReceiver.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/AttributionHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/Constants.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/DeviceInfo.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/IActivityHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/IAttributionHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/ILogger.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/IPackageHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/IRequestHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/LICENSE create mode 100644 mobile/android/thirdparty/com/adjust/sdk/LogLevel.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/Logger.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/OnAttributionChangedListener.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/PackageBuilder.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/Reflection.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/RequestHandler.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/UnitTestActivity.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/Util.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/plugin/AndroidIdUtil.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/plugin/MacAddressUtil.java create mode 100644 mobile/android/thirdparty/com/adjust/sdk/plugin/Plugin.java create mode 100644 mobile/android/thirdparty/com/jakewharton/disklrucache/DiskLruCache.java create mode 100644 mobile/android/thirdparty/com/jakewharton/disklrucache/StrictLineReader.java create mode 100644 mobile/android/thirdparty/com/jakewharton/disklrucache/Util.java create mode 100644 mobile/android/thirdparty/com/keepsafe/switchboard/AsyncConfigLoader.java create mode 100644 mobile/android/thirdparty/com/keepsafe/switchboard/DeviceUuidFactory.java create mode 100644 mobile/android/thirdparty/com/keepsafe/switchboard/Preferences.java create mode 100644 mobile/android/thirdparty/com/keepsafe/switchboard/Switch.java create mode 100644 mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java create mode 100644 mobile/android/thirdparty/com/squareup/leakcanary/LeakCanary.java create mode 100644 mobile/android/thirdparty/com/squareup/leakcanary/RefWatcher.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Action.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/AssetBitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/BitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Cache.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Callback.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/ContactsPhotoBitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/ContentStreamBitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/DeferredRequestCreator.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Downloader.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/FetchAction.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/FileBitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/GetAction.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/ImageViewAction.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/LruCache.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/MarkableInputStream.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/MediaStoreBitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/NetworkBitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Picasso.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/PicassoDrawable.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/PicassoExecutorService.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Request.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/RequestCreator.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/ResourceBitmapHunter.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Stats.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/StatsSnapshot.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Target.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/TargetAction.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Transformation.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/UrlConnectionDownloader.java create mode 100644 mobile/android/thirdparty/com/squareup/picasso/Utils.java create mode 100644 mobile/android/thirdparty/org/json/simple/ItemList.java create mode 100644 mobile/android/thirdparty/org/json/simple/JSONArray.java create mode 100644 mobile/android/thirdparty/org/json/simple/JSONAware.java create mode 100644 mobile/android/thirdparty/org/json/simple/JSONObject.java create mode 100644 mobile/android/thirdparty/org/json/simple/JSONStreamAware.java create mode 100644 mobile/android/thirdparty/org/json/simple/JSONValue.java create mode 100644 mobile/android/thirdparty/org/json/simple/LICENSE.txt create mode 100644 mobile/android/thirdparty/org/json/simple/parser/ContainerFactory.java create mode 100644 mobile/android/thirdparty/org/json/simple/parser/ContentHandler.java create mode 100644 mobile/android/thirdparty/org/json/simple/parser/JSONParser.java create mode 100644 mobile/android/thirdparty/org/json/simple/parser/ParseException.java create mode 100644 mobile/android/thirdparty/org/json/simple/parser/Yylex.java create mode 100644 mobile/android/thirdparty/org/json/simple/parser/Yytoken.java create mode 100644 mobile/android/thirdparty/org/lucasr/dspec/DesignSpec.java create mode 100644 mobile/android/thirdparty/org/lucasr/dspec/RawResource.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryDecoder.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryEncoder.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/CharEncoding.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/Decoder.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/DecoderException.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/Encoder.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/EncoderException.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringDecoder.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoder.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoderComparator.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32InputStream.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32OutputStream.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64InputStream.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64OutputStream.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodec.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecInputStream.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecOutputStream.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BinaryCodec.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Hex.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/StringUtils.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/package.html create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/DigestUtils.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/package.html create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/AbstractCaverphone.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone1.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone2.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/ColognePhonetic.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/DoubleMetaphone.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Metaphone.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/RefinedSoundex.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Soundex.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/SoundexUtils.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/package.html create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/BCodec.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QCodec.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QuotedPrintableCodec.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/RFC1522Codec.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/URLCodec.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/Utils.java create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/package.html create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/overview.html create mode 100644 mobile/android/thirdparty/org/mozilla/apache/commons/codec/package.html (limited to 'mobile/android/thirdparty') diff --git a/mobile/android/thirdparty/AndroidManifest.xml b/mobile/android/thirdparty/AndroidManifest.xml new file mode 100644 index 000000000..3638db69d --- /dev/null +++ b/mobile/android/thirdparty/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/mobile/android/thirdparty/README b/mobile/android/thirdparty/README new file mode 100644 index 000000000..5f813e4bc --- /dev/null +++ b/mobile/android/thirdparty/README @@ -0,0 +1,3 @@ +This directory contains the source code of Java libraries used by +Mozilla Fennec (Firefox for Android) but developed by third-party +organizations. diff --git a/mobile/android/thirdparty/build.gradle b/mobile/android/thirdparty/build.gradle new file mode 100644 index 000000000..a192e5009 --- /dev/null +++ b/mobile/android/thirdparty/build.gradle @@ -0,0 +1,54 @@ +buildDir "${topobjdir}/gradle/build/mobile/android/thirdparty" + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION + + defaultConfig { + targetSdkVersion 23 + minSdkVersion 15 + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + lintOptions { + abortOnError false + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java { + srcDir '.' + + if (!mozconfig.substs.MOZ_INSTALL_TRACKING) { + exclude 'com/adjust/**' + } + + // Exclude LeakCanary: It will be added again via a gradle dependency. This version + // here is only the no-op library for mach-based builds. + exclude 'com/squareup/leakcanary/**' + } + } + } +} + +dependencies { + compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}" +} + +apply plugin: 'idea' + +idea { + module { + // This is cosmetic. See the excludes in the root project. + if (!mozconfig.substs.MOZ_INSTALL_TRACKING) { + excludeDirs += file('com/adjust/sdk') + } + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionClosedException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionClosedException.java new file mode 100644 index 000000000..bcd7308df --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionClosedException.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +/** + * Signals that the connection has been closed unexpectedly. + * + * @since 4.0 + */ +public class ConnectionClosedException extends IOException { + + private static final long serialVersionUID = 617550366255636674L; + + /** + * Creates a new ConnectionClosedException with the specified detail message. + * + * @param message The exception detail message + */ + public ConnectionClosedException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionReuseStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionReuseStrategy.java new file mode 100644 index 000000000..ec099b001 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ConnectionReuseStrategy.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Interface for deciding whether a connection can be re-used for + * subsequent requests and should be kept alive. + *

+ * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + */ +public interface ConnectionReuseStrategy { + + /** + * Decides whether a connection can be kept open after a request. + * If this method returns false, the caller MUST + * close the connection to correctly comply with the HTTP protocol. + * If it returns true, the caller SHOULD attempt to + * keep the connection open for reuse with another request. + *
+ * One can use the HTTP context to retrieve additional objects that + * may be relevant for the keep-alive strategy: the actual HTTP + * connection, the original HTTP request, target host if known, + * number of times the connection has been reused already and so on. + *
+ * If the connection is already closed, false is returned. + * The stale connection check MUST NOT be triggered by a + * connection reuse strategy. + * + * @param response + * The last response received over that connection. + * @param context the context in which the connection is being + * used. + * + * @return true if the connection is allowed to be reused, or + * false if it MUST NOT be reused + */ + boolean keepAlive(HttpResponse response, HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/Consts.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/Consts.java new file mode 100644 index 000000000..81e2a7592 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/Consts.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.nio.charset.Charset; + +/** + * Commons constants. + * + * @since 4.2 + */ +public final class Consts { + + public static final int CR = 13; // + public static final int LF = 10; // + public static final int SP = 32; // + public static final int HT = 9; // + + public static final Charset UTF_8 = Charset.forName("UTF-8"); + public static final Charset ASCII = Charset.forName("US-ASCII"); + public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + + private Consts() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ContentTooLongException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ContentTooLongException.java new file mode 100644 index 000000000..64f83b3af --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ContentTooLongException.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +/** + * Signals that HTTP entity content is too long. + * + * @since 4.2 + */ +public class ContentTooLongException extends IOException { + + private static final long serialVersionUID = -924287689552495383L; + + /** + * Creates a new ContentTooLongException with the specified detail message. + * + * @param message exception message + */ + public ContentTooLongException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/FormattedHeader.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/FormattedHeader.java new file mode 100644 index 000000000..cd3f4126d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/FormattedHeader.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * An HTTP header which is already formatted. + * For example when headers are received, the original formatting + * can be preserved. This allows for the header to be sent without + * another formatting step. + * + * @since 4.0 + */ +public interface FormattedHeader extends Header { + + /** + * Obtains the buffer with the formatted header. + * The returned buffer MUST NOT be modified. + * + * @return the formatted header, in a buffer that must not be modified + */ + CharArrayBuffer getBuffer(); + + /** + * Obtains the start of the header value in the {@link #getBuffer buffer}. + * By accessing the value in the buffer, creation of a temporary string + * can be avoided. + * + * @return index of the first character of the header value + * in the buffer returned by {@link #getBuffer getBuffer}. + */ + int getValuePos(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/Header.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/Header.java new file mode 100644 index 000000000..50ce75a92 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/Header.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * Represents an HTTP header field. + * + *

The HTTP header fields follow the same generic format as + * that given in Section 3.1 of RFC 822. Each header field consists + * of a name followed by a colon (":") and the field value. Field names + * are case-insensitive. The field value MAY be preceded by any amount + * of LWS, though a single SP is preferred. + * + *

+ *     message-header = field-name ":" [ field-value ]
+ *     field-name     = token
+ *     field-value    = *( field-content | LWS )
+ *     field-content  = <the OCTETs making up the field-value
+ *                      and consisting of either *TEXT or combinations
+ *                      of token, separators, and quoted-string>
+ *
+ * + * @since 4.0 + */ +public interface Header { + + /** + * Get the name of the Header. + * + * @return the name of the Header, never {@code null} + */ + String getName(); + + /** + * Get the value of the Header. + * + * @return the value of the Header, may be {@code null} + */ + String getValue(); + + /** + * Parses the value. + * + * @return an array of {@link HeaderElement} entries, may be empty, but is never {@code null} + * @throws ParseException + */ + HeaderElement[] getElements() throws ParseException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElement.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElement.java new file mode 100644 index 000000000..f6fcf8f04 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElement.java @@ -0,0 +1,108 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * One element of an HTTP {@link Header header} value consisting of + * a name / value pair and a number of optional name / value parameters. + *

+ * Some HTTP headers (such as the set-cookie header) have values that + * can be decomposed into multiple elements. Such headers must be in the + * following form: + *

+ *
+ * header  = [ element ] *( "," [ element ] )
+ * element = name [ "=" [ value ] ] *( ";" [ param ] )
+ * param   = name [ "=" [ value ] ]
+ *
+ * name    = token
+ * value   = ( token | quoted-string )
+ *
+ * token         = 1*<any char except "=", ",", ";", <"> and
+ *                       white space>
+ * quoted-string = <"> *( text | quoted-char ) <">
+ * text          = any char except <">
+ * quoted-char   = "\" char
+ * 
+ *

+ * Any amount of white space is allowed between any part of the + * header, element or param and is ignored. A missing value in any + * element or param will be stored as the empty {@link String}; + * if the "=" is also missing null will be stored instead. + * + * @since 4.0 + */ +public interface HeaderElement { + + /** + * Returns header element name. + * + * @return header element name + */ + String getName(); + + /** + * Returns header element value. + * + * @return header element value + */ + String getValue(); + + /** + * Returns an array of name / value pairs. + * + * @return array of name / value pairs + */ + NameValuePair[] getParameters(); + + /** + * Returns the first parameter with the given name. + * + * @param name parameter name + * + * @return name / value pair + */ + NameValuePair getParameterByName(String name); + + /** + * Returns the total count of parameters. + * + * @return parameter count + */ + int getParameterCount(); + + /** + * Returns parameter with the given index. + * + * @param index index + * @return name / value pair + */ + NameValuePair getParameter(int index); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElementIterator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElementIterator.java new file mode 100644 index 000000000..c0ef11346 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderElementIterator.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.util.Iterator; + +/** + * A type-safe iterator for {@link HeaderElement} objects. + * + * @since 4.0 + */ +public interface HeaderElementIterator extends Iterator { + + /** + * Indicates whether there is another header element in this + * iteration. + * + * @return true if there is another header element, + * false otherwise + */ + boolean hasNext(); + + /** + * Obtains the next header element from this iteration. + * This method should only be called while {@link #hasNext hasNext} + * is true. + * + * @return the next header element in this iteration + */ + HeaderElement nextElement(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderIterator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderIterator.java new file mode 100644 index 000000000..a6879cea3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HeaderIterator.java @@ -0,0 +1,56 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.util.Iterator; + +/** + * A type-safe iterator for {@link Header} objects. + * + * @since 4.0 + */ +public interface HeaderIterator extends Iterator { + + /** + * Indicates whether there is another header in this iteration. + * + * @return true if there is another header, + * false otherwise + */ + boolean hasNext(); + + /** + * Obtains the next header from this iteration. + * This method should only be called while {@link #hasNext hasNext} + * is true. + * + * @return the next header in this iteration + */ + Header nextHeader(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpClientConnection.java new file mode 100644 index 000000000..f390700b6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpClientConnection.java @@ -0,0 +1,102 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +/** + * A client-side HTTP connection, which can be used for sending + * requests and receiving responses. + * + * @since 4.0 + */ +public interface HttpClientConnection extends HttpConnection { + + /** + * Checks if response data is available from the connection. May wait for + * the specified time until some data becomes available. Note that some + * implementations may completely ignore the timeout parameter. + * + * @param timeout the maximum time in milliseconds to wait for data + * @return true if data is available; false if there was no data available + * even after waiting for timeout milliseconds. + * @throws IOException if an error happens on the connection + */ + boolean isResponseAvailable(int timeout) + throws IOException; + + /** + * Sends the request line and all headers over the connection. + * @param request the request whose headers to send. + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void sendRequestHeader(HttpRequest request) + throws HttpException, IOException; + + /** + * Sends the request entity over the connection. + * @param request the request whose entity to send. + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void sendRequestEntity(HttpEntityEnclosingRequest request) + throws HttpException, IOException; + + /** + * Receives the request line and headers of the next response available from + * this connection. The caller should examine the HttpResponse object to + * find out if it should try to receive a response entity as well. + * + * @return a new HttpResponse object with status line and headers + * initialized. + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + HttpResponse receiveResponseHeader() + throws HttpException, IOException; + + /** + * Receives the next response entity available from this connection and + * attaches it to an existing HttpResponse object. + * + * @param response the response to attach the entity to + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void receiveResponseEntity(HttpResponse response) + throws HttpException, IOException; + + /** + * Writes out all pending buffered data over the open connection. + * + * @throws IOException in case of an I/O error + */ + void flush() throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnection.java new file mode 100644 index 000000000..f4891358f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnection.java @@ -0,0 +1,104 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.Closeable; +import java.io.IOException; + +/** + * A generic HTTP connection, useful on client and server side. + * + * @since 4.0 + */ +public interface HttpConnection extends Closeable { + + /** + * Closes this connection gracefully. + * This method will attempt to flush the internal output + * buffer prior to closing the underlying socket. + * This method MUST NOT be called from a different thread to force + * shutdown of the connection. Use {@link #shutdown shutdown} instead. + */ + void close() throws IOException; + + /** + * Checks if this connection is open. + * @return true if it is open, false if it is closed. + */ + boolean isOpen(); + + /** + * Checks whether this connection has gone down. + * Network connections may get closed during some time of inactivity + * for several reasons. The next time a read is attempted on such a + * connection it will throw an IOException. + * This method tries to alleviate this inconvenience by trying to + * find out if a connection is still usable. Implementations may do + * that by attempting a read with a very small timeout. Thus this + * method may block for a small amount of time before returning a result. + * It is therefore an expensive operation. + * + * @return true if attempts to use this connection are + * likely to succeed, or false if they are likely + * to fail and this connection should be closed + */ + boolean isStale(); + + /** + * Sets the socket timeout value. + * + * @param timeout timeout value in milliseconds + */ + void setSocketTimeout(int timeout); + + /** + * Returns the socket timeout value. + * + * @return positive value in milliseconds if a timeout is set, + * 0 if timeout is disabled or -1 if + * timeout is undefined. + */ + int getSocketTimeout(); + + /** + * Force-closes this connection. + * This is the only method of a connection which may be called + * from a different thread to terminate the connection. + * This method will not attempt to flush the transmitter's + * internal buffer prior to closing the underlying socket. + */ + void shutdown() throws IOException; + + /** + * Returns a collection of connection metrics. + * + * @return HttpConnectionMetrics + */ + HttpConnectionMetrics getMetrics(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionFactory.java new file mode 100644 index 000000000..867a1e9e0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionFactory.java @@ -0,0 +1,42 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; +import java.net.Socket; + +/** + * Factory for {@link HttpConnection} instances. + * + * @since 4.3 + */ +public interface HttpConnectionFactory { + + T createConnection(Socket socket) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionMetrics.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionMetrics.java new file mode 100644 index 000000000..4445eb55b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpConnectionMetrics.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * The point of access to the statistics of an {@link HttpConnection}. + * + * @since 4.0 + */ +public interface HttpConnectionMetrics { + + /** + * Returns the number of requests transferred over the connection, + * 0 if not available. + */ + long getRequestCount(); + + /** + * Returns the number of responses transferred over the connection, + * 0 if not available. + */ + long getResponseCount(); + + /** + * Returns the number of bytes transferred over the connection, + * 0 if not available. + */ + long getSentBytesCount(); + + /** + * Returns the number of bytes transferred over the connection, + * 0 if not available. + */ + long getReceivedBytesCount(); + + /** + * Return the value for the specified metric. + * + *@param metricName the name of the metric to query. + * + *@return the object representing the metric requested, + * null if the metric cannot not found. + */ + Object getMetric(String metricName); + + /** + * Resets the counts + * + */ + void reset(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntity.java new file mode 100644 index 000000000..5e223c1ec --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntity.java @@ -0,0 +1,199 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An entity that can be sent or received with an HTTP message. + * Entities can be found in some + * {@link HttpEntityEnclosingRequest requests} and in + * {@link HttpResponse responses}, where they are optional. + *

+ * There are three distinct types of entities in HttpCore, + * depending on where their {@link #getContent content} originates: + *

    + *
  • streamed: The content is received from a stream, or + * generated on the fly. In particular, this category includes + * entities being received from a {@link HttpConnection connection}. + * {@link #isStreaming Streamed} entities are generally not + * {@link #isRepeatable repeatable}. + *
  • + *
  • self-contained: The content is in memory or obtained by + * means that are independent from a connection or other entity. + * Self-contained entities are generally {@link #isRepeatable repeatable}. + *
  • + *
  • wrapping: The content is obtained from another entity. + *
  • + *
+ * This distinction is important for connection management with incoming + * entities. For entities that are created by an application and only sent + * using the HTTP components framework, the difference between streamed + * and self-contained is of little importance. In that case, it is suggested + * to consider non-repeatable entities as streamed, and those that are + * repeatable (without a huge effort) as self-contained. + * + * @since 4.0 + */ +public interface HttpEntity { + + /** + * Tells if the entity is capable of producing its data more than once. + * A repeatable entity's getContent() and writeTo(OutputStream) methods + * can be called more than once whereas a non-repeatable entity's can not. + * @return true if the entity is repeatable, false otherwise. + */ + boolean isRepeatable(); + + /** + * Tells about chunked encoding for this entity. + * The primary purpose of this method is to indicate whether + * chunked encoding should be used when the entity is sent. + * For entities that are received, it can also indicate whether + * the entity was received with chunked encoding. + *
+ * The behavior of wrapping entities is implementation dependent, + * but should respect the primary purpose. + * + * @return true if chunked encoding is preferred for this + * entity, or false if it is not + */ + boolean isChunked(); + + /** + * Tells the length of the content, if known. + * + * @return the number of bytes of the content, or + * a negative number if unknown. If the content length is known + * but exceeds {@link java.lang.Long#MAX_VALUE Long.MAX_VALUE}, + * a negative number is returned. + */ + long getContentLength(); + + /** + * Obtains the Content-Type header, if known. + * This is the header that should be used when sending the entity, + * or the one that was received with the entity. It can include a + * charset attribute. + * + * @return the Content-Type header for this entity, or + * null if the content type is unknown + */ + Header getContentType(); + + /** + * Obtains the Content-Encoding header, if known. + * This is the header that should be used when sending the entity, + * or the one that was received with the entity. + * Wrapping entities that modify the content encoding should + * adjust this header accordingly. + * + * @return the Content-Encoding header for this entity, or + * null if the content encoding is unknown + */ + Header getContentEncoding(); + + /** + * Returns a content stream of the entity. + * {@link #isRepeatable Repeatable} entities are expected + * to create a new instance of {@link InputStream} for each invocation + * of this method and therefore can be consumed multiple times. + * Entities that are not {@link #isRepeatable repeatable} are expected + * to return the same {@link InputStream} instance and therefore + * may not be consumed more than once. + *

+ * IMPORTANT: Please note all entity implementations must ensure that + * all allocated resources are properly deallocated after + * the {@link InputStream#close()} method is invoked. + * + * @return content stream of the entity. + * + * @throws IOException if the stream could not be created + * @throws IllegalStateException + * if content stream cannot be created. + * + * @see #isRepeatable() + */ + InputStream getContent() throws IOException, IllegalStateException; + + /** + * Writes the entity content out to the output stream. + *

+ *

+ * IMPORTANT: Please note all entity implementations must ensure that + * all allocated resources are properly deallocated when this method + * returns. + * + * @param outstream the output stream to write entity content to + * + * @throws IOException if an I/O error occurs + */ + void writeTo(OutputStream outstream) throws IOException; + + /** + * Tells whether this entity depends on an underlying stream. + * Streamed entities that read data directly from the socket should + * return true. Self-contained entities should return + * false. Wrapping entities should delegate this call + * to the wrapped entity. + * + * @return true if the entity content is streamed, + * false otherwise + */ + boolean isStreaming(); // don't expect an exception here + + /** + * This method is deprecated since version 4.1. Please use standard + * java convention to ensure resource deallocation by calling + * {@link InputStream#close()} on the input stream returned by + * {@link #getContent()} + *

+ * This method is called to indicate that the content of this entity + * is no longer required. All entity implementations are expected to + * release all allocated resources as a result of this method + * invocation. Content streaming entities are also expected to + * dispose of the remaining content, if any. Wrapping entities should + * delegate this call to the wrapped entity. + *

+ * This method is of particular importance for entities being + * received from a {@link HttpConnection connection}. The entity + * needs to be consumed completely in order to re-use the connection + * with keep-alive. + * + * @throws IOException if an I/O error occurs. + * + * @deprecated (4.1) Use {@link ch.boye.httpclientandroidlib.util.EntityUtils#consume(HttpEntity)} + * + * @see #getContent() and #writeTo(OutputStream) + */ + @Deprecated + void consumeContent() throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntityEnclosingRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntityEnclosingRequest.java new file mode 100644 index 000000000..f2cce4197 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpEntityEnclosingRequest.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib; + +/** + * A request with an entity. + * + * @since 4.0 + */ +public interface HttpEntityEnclosingRequest extends HttpRequest { + + /** + * Tells if this request should use the expect-continue handshake. + * The expect continue handshake gives the server a chance to decide + * whether to accept the entity enclosing request before the possibly + * lengthy entity is sent across the wire. + * @return true if the expect continue handshake should be used, false if + * not. + */ + boolean expectContinue(); + + /** + * Associates the entity with this request. + * + * @param entity the entity to send. + */ + void setEntity(HttpEntity entity); + + /** + * Returns the entity associated with this request. + * + * @return entity + */ + HttpEntity getEntity(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpException.java new file mode 100644 index 000000000..05f6381d1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpException.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * Signals that an HTTP exception has occurred. + * + * @since 4.0 + */ +public class HttpException extends Exception { + + private static final long serialVersionUID = -5437299376222011036L; + + /** + * Creates a new HttpException with a null detail message. + */ + public HttpException() { + super(); + } + + /** + * Creates a new HttpException with the specified detail message. + * + * @param message the exception detail message + */ + public HttpException(final String message) { + super(message); + } + + /** + * Creates a new HttpException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public HttpException(final String message, final Throwable cause) { + super(message); + initCause(cause); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHeaders.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHeaders.java new file mode 100644 index 000000000..cadbced78 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHeaders.java @@ -0,0 +1,206 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * Constants enumerating the HTTP headers. All headers defined in RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and RFC2518 + * (WebDAV) are listed. + * + * @since 4.1 + */ +public final class HttpHeaders { + + private HttpHeaders() { + } + + /** RFC 2616 (HTTP/1.1) Section 14.1 */ + public static final String ACCEPT = "Accept"; + + /** RFC 2616 (HTTP/1.1) Section 14.2 */ + public static final String ACCEPT_CHARSET = "Accept-Charset"; + + /** RFC 2616 (HTTP/1.1) Section 14.3 */ + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + + /** RFC 2616 (HTTP/1.1) Section 14.4 */ + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + + /** RFC 2616 (HTTP/1.1) Section 14.5 */ + public static final String ACCEPT_RANGES = "Accept-Ranges"; + + /** RFC 2616 (HTTP/1.1) Section 14.6 */ + public static final String AGE = "Age"; + + /** RFC 1945 (HTTP/1.0) Section 10.1, RFC 2616 (HTTP/1.1) Section 14.7 */ + public static final String ALLOW = "Allow"; + + /** RFC 1945 (HTTP/1.0) Section 10.2, RFC 2616 (HTTP/1.1) Section 14.8 */ + public static final String AUTHORIZATION = "Authorization"; + + /** RFC 2616 (HTTP/1.1) Section 14.9 */ + public static final String CACHE_CONTROL = "Cache-Control"; + + /** RFC 2616 (HTTP/1.1) Section 14.10 */ + public static final String CONNECTION = "Connection"; + + /** RFC 1945 (HTTP/1.0) Section 10.3, RFC 2616 (HTTP/1.1) Section 14.11 */ + public static final String CONTENT_ENCODING = "Content-Encoding"; + + /** RFC 2616 (HTTP/1.1) Section 14.12 */ + public static final String CONTENT_LANGUAGE = "Content-Language"; + + /** RFC 1945 (HTTP/1.0) Section 10.4, RFC 2616 (HTTP/1.1) Section 14.13 */ + public static final String CONTENT_LENGTH = "Content-Length"; + + /** RFC 2616 (HTTP/1.1) Section 14.14 */ + public static final String CONTENT_LOCATION = "Content-Location"; + + /** RFC 2616 (HTTP/1.1) Section 14.15 */ + public static final String CONTENT_MD5 = "Content-MD5"; + + /** RFC 2616 (HTTP/1.1) Section 14.16 */ + public static final String CONTENT_RANGE = "Content-Range"; + + /** RFC 1945 (HTTP/1.0) Section 10.5, RFC 2616 (HTTP/1.1) Section 14.17 */ + public static final String CONTENT_TYPE = "Content-Type"; + + /** RFC 1945 (HTTP/1.0) Section 10.6, RFC 2616 (HTTP/1.1) Section 14.18 */ + public static final String DATE = "Date"; + + /** RFC 2518 (WevDAV) Section 9.1 */ + public static final String DAV = "Dav"; + + /** RFC 2518 (WevDAV) Section 9.2 */ + public static final String DEPTH = "Depth"; + + /** RFC 2518 (WevDAV) Section 9.3 */ + public static final String DESTINATION = "Destination"; + + /** RFC 2616 (HTTP/1.1) Section 14.19 */ + public static final String ETAG = "ETag"; + + /** RFC 2616 (HTTP/1.1) Section 14.20 */ + public static final String EXPECT = "Expect"; + + /** RFC 1945 (HTTP/1.0) Section 10.7, RFC 2616 (HTTP/1.1) Section 14.21 */ + public static final String EXPIRES = "Expires"; + + /** RFC 1945 (HTTP/1.0) Section 10.8, RFC 2616 (HTTP/1.1) Section 14.22 */ + public static final String FROM = "From"; + + /** RFC 2616 (HTTP/1.1) Section 14.23 */ + public static final String HOST = "Host"; + + /** RFC 2518 (WevDAV) Section 9.4 */ + public static final String IF = "If"; + + /** RFC 2616 (HTTP/1.1) Section 14.24 */ + public static final String IF_MATCH = "If-Match"; + + /** RFC 1945 (HTTP/1.0) Section 10.9, RFC 2616 (HTTP/1.1) Section 14.25 */ + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + + /** RFC 2616 (HTTP/1.1) Section 14.26 */ + public static final String IF_NONE_MATCH = "If-None-Match"; + + /** RFC 2616 (HTTP/1.1) Section 14.27 */ + public static final String IF_RANGE = "If-Range"; + + /** RFC 2616 (HTTP/1.1) Section 14.28 */ + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + + /** RFC 1945 (HTTP/1.0) Section 10.10, RFC 2616 (HTTP/1.1) Section 14.29 */ + public static final String LAST_MODIFIED = "Last-Modified"; + + /** RFC 1945 (HTTP/1.0) Section 10.11, RFC 2616 (HTTP/1.1) Section 14.30 */ + public static final String LOCATION = "Location"; + + /** RFC 2518 (WevDAV) Section 9.5 */ + public static final String LOCK_TOKEN = "Lock-Token"; + + /** RFC 2616 (HTTP/1.1) Section 14.31 */ + public static final String MAX_FORWARDS = "Max-Forwards"; + + /** RFC 2518 (WevDAV) Section 9.6 */ + public static final String OVERWRITE = "Overwrite"; + + /** RFC 1945 (HTTP/1.0) Section 10.12, RFC 2616 (HTTP/1.1) Section 14.32 */ + public static final String PRAGMA = "Pragma"; + + /** RFC 2616 (HTTP/1.1) Section 14.33 */ + public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + + /** RFC 2616 (HTTP/1.1) Section 14.34 */ + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + + /** RFC 2616 (HTTP/1.1) Section 14.35 */ + public static final String RANGE = "Range"; + + /** RFC 1945 (HTTP/1.0) Section 10.13, RFC 2616 (HTTP/1.1) Section 14.36 */ + public static final String REFERER = "Referer"; + + /** RFC 2616 (HTTP/1.1) Section 14.37 */ + public static final String RETRY_AFTER = "Retry-After"; + + /** RFC 1945 (HTTP/1.0) Section 10.14, RFC 2616 (HTTP/1.1) Section 14.38 */ + public static final String SERVER = "Server"; + + /** RFC 2518 (WevDAV) Section 9.7 */ + public static final String STATUS_URI = "Status-URI"; + + /** RFC 2616 (HTTP/1.1) Section 14.39 */ + public static final String TE = "TE"; + + /** RFC 2518 (WevDAV) Section 9.8 */ + public static final String TIMEOUT = "Timeout"; + + /** RFC 2616 (HTTP/1.1) Section 14.40 */ + public static final String TRAILER = "Trailer"; + + /** RFC 2616 (HTTP/1.1) Section 14.41 */ + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + + /** RFC 2616 (HTTP/1.1) Section 14.42 */ + public static final String UPGRADE = "Upgrade"; + + /** RFC 1945 (HTTP/1.0) Section 10.15, RFC 2616 (HTTP/1.1) Section 14.43 */ + public static final String USER_AGENT = "User-Agent"; + + /** RFC 2616 (HTTP/1.1) Section 14.44 */ + public static final String VARY = "Vary"; + + /** RFC 2616 (HTTP/1.1) Section 14.45 */ + public static final String VIA = "Via"; + + /** RFC 2616 (HTTP/1.1) Section 14.46 */ + public static final String WARNING = "Warning"; + + /** RFC 1945 (HTTP/1.0) Section 10.16, RFC 2616 (HTTP/1.1) Section 14.47 */ + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHost.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHost.java new file mode 100644 index 000000000..d25bb13cf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpHost.java @@ -0,0 +1,290 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.Serializable; +import java.net.InetAddress; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Holds all of the variables needed to describe an HTTP connection to a host. + * This includes remote host name, port and scheme. + * + * + * @since 4.0 + */ +@Immutable +public final class HttpHost implements Cloneable, Serializable { + + private static final long serialVersionUID = -7529410654042457626L; + + /** The default scheme is "http". */ + public static final String DEFAULT_SCHEME_NAME = "http"; + + /** The host to use. */ + protected final String hostname; + + /** The lowercase host, for {@link #equals} and {@link #hashCode}. */ + protected final String lcHostname; + + + /** The port to use, defaults to -1 if not set. */ + protected final int port; + + /** The scheme (lowercased) */ + protected final String schemeName; + + protected final InetAddress address; + + /** + * Creates a new {@link HttpHost HttpHost}, specifying all values. + * Constructor for HttpHost. + * + * @param hostname the hostname (IP or DNS name) + * @param port the port number. + * -1 indicates the scheme default port. + * @param scheme the name of the scheme. + * null indicates the + * {@link #DEFAULT_SCHEME_NAME default scheme} + */ + public HttpHost(final String hostname, final int port, final String scheme) { + super(); + this.hostname = Args.notBlank(hostname, "Host name"); + this.lcHostname = hostname.toLowerCase(Locale.ENGLISH); + if (scheme != null) { + this.schemeName = scheme.toLowerCase(Locale.ENGLISH); + } else { + this.schemeName = DEFAULT_SCHEME_NAME; + } + this.port = port; + this.address = null; + } + + /** + * Creates a new {@link HttpHost HttpHost}, with default scheme. + * + * @param hostname the hostname (IP or DNS name) + * @param port the port number. + * -1 indicates the scheme default port. + */ + public HttpHost(final String hostname, final int port) { + this(hostname, port, null); + } + + /** + * Creates a new {@link HttpHost HttpHost}, with default scheme and port. + * + * @param hostname the hostname (IP or DNS name) + */ + public HttpHost(final String hostname) { + this(hostname, -1, null); + } + + /** + * Creates a new {@link HttpHost HttpHost}, specifying all values. + * Constructor for HttpHost. + * + * @param address the inet address. + * @param port the port number. + * -1 indicates the scheme default port. + * @param scheme the name of the scheme. + * null indicates the + * {@link #DEFAULT_SCHEME_NAME default scheme} + * + * @since 4.3 + */ + public HttpHost(final InetAddress address, final int port, final String scheme) { + super(); + this.address = Args.notNull(address, "Inet address"); + this.hostname = address.getHostAddress(); + this.lcHostname = this.hostname.toLowerCase(Locale.ENGLISH); + if (scheme != null) { + this.schemeName = scheme.toLowerCase(Locale.ENGLISH); + } else { + this.schemeName = DEFAULT_SCHEME_NAME; + } + this.port = port; + } + + /** + * Creates a new {@link HttpHost HttpHost}, with default scheme. + * + * @param address the inet address. + * @param port the port number. + * -1 indicates the scheme default port. + * + * @since 4.3 + */ + public HttpHost(final InetAddress address, final int port) { + this(address, port, null); + } + + /** + * Creates a new {@link HttpHost HttpHost}, with default scheme and port. + * + * @param address the inet address. + * + * @since 4.3 + */ + public HttpHost(final InetAddress address) { + this(address, -1, null); + } + + /** + * Copy constructor for {@link HttpHost HttpHost}. + * + * @param httphost the HTTP host to copy details from + */ + public HttpHost (final HttpHost httphost) { + super(); + Args.notNull(httphost, "HTTP host"); + this.hostname = httphost.hostname; + this.lcHostname = httphost.lcHostname; + this.schemeName = httphost.schemeName; + this.port = httphost.port; + this.address = httphost.address; + } + + /** + * Returns the host name. + * + * @return the host name (IP or DNS name) + */ + public String getHostName() { + return this.hostname; + } + + /** + * Returns the port. + * + * @return the host port, or -1 if not set + */ + public int getPort() { + return this.port; + } + + /** + * Returns the scheme name. + * + * @return the scheme name + */ + public String getSchemeName() { + return this.schemeName; + } + + /** + * Returns the inet address if explicitly set by a constructor, + * null otherwise. + * @return the inet address + * + * @since 4.3 + */ + public InetAddress getAddress() { + return this.address; + } + + /** + * Return the host URI, as a string. + * + * @return the host URI + */ + public String toURI() { + final StringBuilder buffer = new StringBuilder(); + buffer.append(this.schemeName); + buffer.append("://"); + buffer.append(this.hostname); + if (this.port != -1) { + buffer.append(':'); + buffer.append(Integer.toString(this.port)); + } + return buffer.toString(); + } + + + /** + * Obtains the host string, without scheme prefix. + * + * @return the host string, for example localhost:8080 + */ + public String toHostString() { + if (this.port != -1) { + //the highest port number is 65535, which is length 6 with the addition of the colon + final StringBuilder buffer = new StringBuilder(this.hostname.length() + 6); + buffer.append(this.hostname); + buffer.append(":"); + buffer.append(Integer.toString(this.port)); + return buffer.toString(); + } else { + return this.hostname; + } + } + + + @Override + public String toString() { + return toURI(); + } + + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof HttpHost) { + final HttpHost that = (HttpHost) obj; + return this.lcHostname.equals(that.lcHostname) + && this.port == that.port + && this.schemeName.equals(that.schemeName); + } else { + return false; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.lcHostname); + hash = LangUtils.hashCode(hash, this.port); + hash = LangUtils.hashCode(hash, this.schemeName); + return hash; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpInetConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpInetConnection.java new file mode 100644 index 000000000..9a22d3de8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpInetConnection.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.net.InetAddress; + +/** + * An HTTP connection over the Internet Protocol (IP). + * + * @since 4.0 + */ +public interface HttpInetConnection extends HttpConnection { + + InetAddress getLocalAddress(); + + int getLocalPort(); + + InetAddress getRemoteAddress(); + + int getRemotePort(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpMessage.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpMessage.java new file mode 100644 index 000000000..9ea8a3e56 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpMessage.java @@ -0,0 +1,210 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * HTTP messages consist of requests from client to server and responses + * from server to client. + *

+ *     HTTP-message   = Request | Response     ; HTTP/1.1 messages
+ * 
+ *

+ * HTTP messages use the generic message format of RFC 822 for + * transferring entities (the payload of the message). Both types + * of message consist of a start-line, zero or more header fields + * (also known as "headers"), an empty line (i.e., a line with nothing + * preceding the CRLF) indicating the end of the header fields, + * and possibly a message-body. + *

+ *
+ *      generic-message = start-line
+ *                        *(message-header CRLF)
+ *                        CRLF
+ *                        [ message-body ]
+ *      start-line      = Request-Line | Status-Line
+ * 
+ * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +public interface HttpMessage { + + /** + * Returns the protocol version this message is compatible with. + */ + ProtocolVersion getProtocolVersion(); + + /** + * Checks if a certain header is present in this message. Header values are + * ignored. + * + * @param name the header name to check for. + * @return true if at least one header with this name is present. + */ + boolean containsHeader(String name); + + /** + * Returns all the headers with a specified name of this message. Header values + * are ignored. Headers are orderd in the sequence they will be sent over a + * connection. + * + * @param name the name of the headers to return. + * @return the headers whose name property equals name. + */ + Header[] getHeaders(String name); + + /** + * Returns the first header with a specified name of this message. Header + * values are ignored. If there is more than one matching header in the + * message the first element of {@link #getHeaders(String)} is returned. + * If there is no matching header in the message null is + * returned. + * + * @param name the name of the header to return. + * @return the first header whose name property equals name + * or null if no such header could be found. + */ + Header getFirstHeader(String name); + + /** + * Returns the last header with a specified name of this message. Header values + * are ignored. If there is more than one matching header in the message the + * last element of {@link #getHeaders(String)} is returned. If there is no + * matching header in the message null is returned. + * + * @param name the name of the header to return. + * @return the last header whose name property equals name. + * or null if no such header could be found. + */ + Header getLastHeader(String name); + + /** + * Returns all the headers of this message. Headers are orderd in the sequence + * they will be sent over a connection. + * + * @return all the headers of this message + */ + Header[] getAllHeaders(); + + /** + * Adds a header to this message. The header will be appended to the end of + * the list. + * + * @param header the header to append. + */ + void addHeader(Header header); + + /** + * Adds a header to this message. The header will be appended to the end of + * the list. + * + * @param name the name of the header. + * @param value the value of the header. + */ + void addHeader(String name, String value); + + /** + * Overwrites the first header with the same name. The new header will be appended to + * the end of the list, if no header with the given name can be found. + * + * @param header the header to set. + */ + void setHeader(Header header); + + /** + * Overwrites the first header with the same name. The new header will be appended to + * the end of the list, if no header with the given name can be found. + * + * @param name the name of the header. + * @param value the value of the header. + */ + void setHeader(String name, String value); + + /** + * Overwrites all the headers in the message. + * + * @param headers the array of headers to set. + */ + void setHeaders(Header[] headers); + + /** + * Removes a header from this message. + * + * @param header the header to remove. + */ + void removeHeader(Header header); + + /** + * Removes all headers with a certain name from this message. + * + * @param name The name of the headers to remove. + */ + void removeHeaders(String name); + + /** + * Returns an iterator of all the headers. + * + * @return Iterator that returns Header objects in the sequence they are + * sent over a connection. + */ + HeaderIterator headerIterator(); + + /** + * Returns an iterator of the headers with a given name. + * + * @param name the name of the headers over which to iterate, or + * null for all headers + * + * @return Iterator that returns Header objects with the argument name + * in the sequence they are sent over a connection. + */ + HeaderIterator headerIterator(String name); + + /** + * Returns the parameters effective for this message as set by + * {@link #setParams(HttpParams)}. + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ + @Deprecated + HttpParams getParams(); + + /** + * Provides parameters to be used for the processing of this message. + * @param params the parameters + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ + @Deprecated + void setParams(HttpParams params); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequest.java new file mode 100644 index 000000000..20eff5f51 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequest.java @@ -0,0 +1,53 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * A request message from a client to a server includes, within the + * first line of that message, the method to be applied to the resource, + * the identifier of the resource, and the protocol version in use. + *
+ *      Request       = Request-Line
+ *                      *(( general-header
+ *                       | request-header
+ *                       | entity-header ) CRLF)
+ *                      CRLF
+ *                      [ message-body ]
+ * 
+ * + * @since 4.0 + */ +public interface HttpRequest extends HttpMessage { + + /** + * Returns the request line of this request. + * @return the request line. + */ + RequestLine getRequestLine(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestFactory.java new file mode 100644 index 000000000..ffc5dffda --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestFactory.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * A factory for {@link HttpRequest HttpRequest} objects. + * + * @since 4.0 + */ +public interface HttpRequestFactory { + + HttpRequest newHttpRequest(RequestLine requestline) + throws MethodNotSupportedException; + + HttpRequest newHttpRequest(String method, String uri) + throws MethodNotSupportedException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestInterceptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestInterceptor.java new file mode 100644 index 000000000..b8efdaea0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpRequestInterceptor.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * HTTP protocol interceptor is a routine that implements a specific aspect of + * the HTTP protocol. Usually protocol interceptors are expected to act upon + * one specific header or a group of related headers of the incoming message + * or populate the outgoing message with one specific header or a group of + * related headers. + *

+ * Protocol Interceptors can also manipulate content entities enclosed with messages. + * Usually this is accomplished by using the 'Decorator' pattern where a wrapper + * entity class is used to decorate the original entity. + *

+ * Protocol interceptors must be implemented as thread-safe. Similarly to + * servlets, protocol interceptors should not use instance variables unless + * access to those variables is synchronized. + * + * @since 4.0 + */ +public interface HttpRequestInterceptor { + + /** + * Processes a request. + * On the client side, this step is performed before the request is + * sent to the server. On the server side, this step is performed + * on incoming messages before the message body is evaluated. + * + * @param request the request to preprocess + * @param context the context for the request + * + * @throws HttpException in case of an HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void process(HttpRequest request, HttpContext context) + throws HttpException, IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponse.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponse.java new file mode 100644 index 000000000..7d19952d5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponse.java @@ -0,0 +1,155 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.util.Locale; + +/** + * After receiving and interpreting a request message, a server responds + * with an HTTP response message. + *

+ *     Response      = Status-Line
+ *                     *(( general-header
+ *                      | response-header
+ *                      | entity-header ) CRLF)
+ *                     CRLF
+ *                     [ message-body ]
+ * 
+ * + * @since 4.0 + */ +public interface HttpResponse extends HttpMessage { + + /** + * Obtains the status line of this response. + * The status line can be set using one of the + * {@link #setStatusLine setStatusLine} methods, + * or it can be initialized in a constructor. + * + * @return the status line, or null if not yet set + */ + StatusLine getStatusLine(); + + /** + * Sets the status line of this response. + * + * @param statusline the status line of this response + */ + void setStatusLine(StatusLine statusline); + + /** + * Sets the status line of this response. + * The reason phrase will be determined based on the current + * {@link #getLocale locale}. + * + * @param ver the HTTP version + * @param code the status code + */ + void setStatusLine(ProtocolVersion ver, int code); + + /** + * Sets the status line of this response with a reason phrase. + * + * @param ver the HTTP version + * @param code the status code + * @param reason the reason phrase, or null to omit + */ + void setStatusLine(ProtocolVersion ver, int code, String reason); + + /** + * Updates the status line of this response with a new status code. + * + * @param code the HTTP status code. + * + * @throws IllegalStateException + * if the status line has not be set + * + * @see HttpStatus + * @see #setStatusLine(StatusLine) + * @see #setStatusLine(ProtocolVersion,int) + */ + void setStatusCode(int code) + throws IllegalStateException; + + /** + * Updates the status line of this response with a new reason phrase. + * + * @param reason the new reason phrase as a single-line string, or + * null to unset the reason phrase + * + * @throws IllegalStateException + * if the status line has not be set + * + * @see #setStatusLine(StatusLine) + * @see #setStatusLine(ProtocolVersion,int) + */ + void setReasonPhrase(String reason) + throws IllegalStateException; + + /** + * Obtains the message entity of this response, if any. + * The entity is provided by calling {@link #setEntity setEntity}. + * + * @return the response entity, or + * null if there is none + */ + HttpEntity getEntity(); + + /** + * Associates a response entity with this response. + *

+ * Please note that if an entity has already been set for this response and it depends on + * an input stream ({@link HttpEntity#isStreaming()} returns true), + * it must be fully consumed in order to ensure release of resources. + * + * @param entity the entity to associate with this response, or + * null to unset + * + * @see HttpEntity#isStreaming() + * @see ch.boye.httpclientandroidlib.util.EntityUtils#updateEntity(HttpResponse, HttpEntity) + */ + void setEntity(HttpEntity entity); + + /** + * Obtains the locale of this response. + * The locale is used to determine the reason phrase + * for the {@link #setStatusCode status code}. + * It can be changed using {@link #setLocale setLocale}. + * + * @return the locale of this response, never null + */ + Locale getLocale(); + + /** + * Changes the locale of this response. + * + * @param loc the new locale + */ + void setLocale(Locale loc); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseFactory.java new file mode 100644 index 000000000..3897339c9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseFactory.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A factory for {@link HttpResponse HttpResponse} objects. + * + * @since 4.0 + */ +public interface HttpResponseFactory { + + /** + * Creates a new response from status line elements. + * + * @param ver the protocol version + * @param status the status code + * @param context the context from which to determine the locale + * for looking up a reason phrase to the status code, or + * null to use the default locale + * + * @return the new response with an initialized status line + */ + HttpResponse newHttpResponse(ProtocolVersion ver, int status, + HttpContext context); + + /** + * Creates a new response from a status line. + * + * @param statusline the status line + * @param context the context from which to determine the locale + * for looking up a reason phrase if the status code + * is updated, or + * null to use the default locale + * + * @return the new response with the argument status line + */ + HttpResponse newHttpResponse(StatusLine statusline, + HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseInterceptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseInterceptor.java new file mode 100644 index 000000000..6d4973c34 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpResponseInterceptor.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * HTTP protocol interceptor is a routine that implements a specific aspect of + * the HTTP protocol. Usually protocol interceptors are expected to act upon + * one specific header or a group of related headers of the incoming message + * or populate the outgoing message with one specific header or a group of + * related headers. Protocol + *

+ * Interceptors can also manipulate content entities enclosed with messages. + * Usually this is accomplished by using the 'Decorator' pattern where a wrapper + * entity class is used to decorate the original entity. + *

+ * Protocol interceptors must be implemented as thread-safe. Similarly to + * servlets, protocol interceptors should not use instance variables unless + * access to those variables is synchronized. + * + * @since 4.0 + */ +public interface HttpResponseInterceptor { + + /** + * Processes a response. + * On the server side, this step is performed before the response is + * sent to the client. On the client side, this step is performed + * on incoming messages before the message body is evaluated. + * + * @param response the response to postprocess + * @param context the context for the request + * + * @throws HttpException in case of an HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void process(HttpResponse response, HttpContext context) + throws HttpException, IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpServerConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpServerConnection.java new file mode 100644 index 000000000..9b0987dde --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpServerConnection.java @@ -0,0 +1,88 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +/** + * A server-side HTTP connection, which can be used for receiving + * requests and sending responses. + * + * @since 4.0 + */ +public interface HttpServerConnection extends HttpConnection { + + /** + * Receives the request line and all headers available from this connection. + * The caller should examine the returned request and decide if to receive a + * request entity as well. + * + * @return a new HttpRequest object whose request line and headers are + * initialized. + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + HttpRequest receiveRequestHeader() + throws HttpException, IOException; + + /** + * Receives the next request entity available from this connection and attaches it to + * an existing request. + * @param request the request to attach the entity to. + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void receiveRequestEntity(HttpEntityEnclosingRequest request) + throws HttpException, IOException; + + /** + * Sends the response line and headers of a response over this connection. + * @param response the response whose headers to send. + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void sendResponseHeader(HttpResponse response) + throws HttpException, IOException; + + /** + * Sends the response entity of a response over this connection. + * @param response the response whose entity to send. + * @throws HttpException in case of HTTP protocol violation + * @throws IOException in case of an I/O error + */ + void sendResponseEntity(HttpResponse response) + throws HttpException, IOException; + + /** + * Sends all pending buffered data over this connection. + * @throws IOException in case of an I/O error + */ + void flush() + throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpStatus.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpStatus.java new file mode 100644 index 000000000..1b61262a2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpStatus.java @@ -0,0 +1,175 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * Constants enumerating the HTTP status codes. + * All status codes defined in RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and + * RFC2518 (WebDAV) are listed. + * + * @see StatusLine + * + * @since 4.0 + */ +public interface HttpStatus { + + // --- 1xx Informational --- + + /** 100 Continue (HTTP/1.1 - RFC 2616) */ + public static final int SC_CONTINUE = 100; + /** 101 Switching Protocols (HTTP/1.1 - RFC 2616)*/ + public static final int SC_SWITCHING_PROTOCOLS = 101; + /** 102 Processing (WebDAV - RFC 2518) */ + public static final int SC_PROCESSING = 102; + + // --- 2xx Success --- + + /** 200 OK (HTTP/1.0 - RFC 1945) */ + public static final int SC_OK = 200; + /** 201 Created (HTTP/1.0 - RFC 1945) */ + public static final int SC_CREATED = 201; + /** 202 Accepted (HTTP/1.0 - RFC 1945) */ + public static final int SC_ACCEPTED = 202; + /** 203 Non Authoritative Information (HTTP/1.1 - RFC 2616) */ + public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203; + /** 204 No Content (HTTP/1.0 - RFC 1945) */ + public static final int SC_NO_CONTENT = 204; + /** 205 Reset Content (HTTP/1.1 - RFC 2616) */ + public static final int SC_RESET_CONTENT = 205; + /** 206 Partial Content (HTTP/1.1 - RFC 2616) */ + public static final int SC_PARTIAL_CONTENT = 206; + /** + * 207 Multi-Status (WebDAV - RFC 2518) or 207 Partial Update + * OK (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?) + */ + public static final int SC_MULTI_STATUS = 207; + + // --- 3xx Redirection --- + + /** 300 Mutliple Choices (HTTP/1.1 - RFC 2616) */ + public static final int SC_MULTIPLE_CHOICES = 300; + /** 301 Moved Permanently (HTTP/1.0 - RFC 1945) */ + public static final int SC_MOVED_PERMANENTLY = 301; + /** 302 Moved Temporarily (Sometimes Found) (HTTP/1.0 - RFC 1945) */ + public static final int SC_MOVED_TEMPORARILY = 302; + /** 303 See Other (HTTP/1.1 - RFC 2616) */ + public static final int SC_SEE_OTHER = 303; + /** 304 Not Modified (HTTP/1.0 - RFC 1945) */ + public static final int SC_NOT_MODIFIED = 304; + /** 305 Use Proxy (HTTP/1.1 - RFC 2616) */ + public static final int SC_USE_PROXY = 305; + /** 307 Temporary Redirect (HTTP/1.1 - RFC 2616) */ + public static final int SC_TEMPORARY_REDIRECT = 307; + + // --- 4xx Client Error --- + + /** 400 Bad Request (HTTP/1.1 - RFC 2616) */ + public static final int SC_BAD_REQUEST = 400; + /** 401 Unauthorized (HTTP/1.0 - RFC 1945) */ + public static final int SC_UNAUTHORIZED = 401; + /** 402 Payment Required (HTTP/1.1 - RFC 2616) */ + public static final int SC_PAYMENT_REQUIRED = 402; + /** 403 Forbidden (HTTP/1.0 - RFC 1945) */ + public static final int SC_FORBIDDEN = 403; + /** 404 Not Found (HTTP/1.0 - RFC 1945) */ + public static final int SC_NOT_FOUND = 404; + /** 405 Method Not Allowed (HTTP/1.1 - RFC 2616) */ + public static final int SC_METHOD_NOT_ALLOWED = 405; + /** 406 Not Acceptable (HTTP/1.1 - RFC 2616) */ + public static final int SC_NOT_ACCEPTABLE = 406; + /** 407 Proxy Authentication Required (HTTP/1.1 - RFC 2616)*/ + public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407; + /** 408 Request Timeout (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUEST_TIMEOUT = 408; + /** 409 Conflict (HTTP/1.1 - RFC 2616) */ + public static final int SC_CONFLICT = 409; + /** 410 Gone (HTTP/1.1 - RFC 2616) */ + public static final int SC_GONE = 410; + /** 411 Length Required (HTTP/1.1 - RFC 2616) */ + public static final int SC_LENGTH_REQUIRED = 411; + /** 412 Precondition Failed (HTTP/1.1 - RFC 2616) */ + public static final int SC_PRECONDITION_FAILED = 412; + /** 413 Request Entity Too Large (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUEST_TOO_LONG = 413; + /** 414 Request-URI Too Long (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUEST_URI_TOO_LONG = 414; + /** 415 Unsupported Media Type (HTTP/1.1 - RFC 2616) */ + public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415; + /** 416 Requested Range Not Satisfiable (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; + /** 417 Expectation Failed (HTTP/1.1 - RFC 2616) */ + public static final int SC_EXPECTATION_FAILED = 417; + + /** + * Static constant for a 418 error. + * 418 Unprocessable Entity (WebDAV drafts?) + * or 418 Reauthentication Required (HTTP/1.1 drafts?) + */ + // not used + // public static final int SC_UNPROCESSABLE_ENTITY = 418; + + /** + * Static constant for a 419 error. + * 419 Insufficient Space on Resource + * (WebDAV - draft-ietf-webdav-protocol-05?) + * or 419 Proxy Reauthentication Required + * (HTTP/1.1 drafts?) + */ + public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419; + /** + * Static constant for a 420 error. + * 420 Method Failure + * (WebDAV - draft-ietf-webdav-protocol-05?) + */ + public static final int SC_METHOD_FAILURE = 420; + /** 422 Unprocessable Entity (WebDAV - RFC 2518) */ + public static final int SC_UNPROCESSABLE_ENTITY = 422; + /** 423 Locked (WebDAV - RFC 2518) */ + public static final int SC_LOCKED = 423; + /** 424 Failed Dependency (WebDAV - RFC 2518) */ + public static final int SC_FAILED_DEPENDENCY = 424; + + // --- 5xx Server Error --- + + /** 500 Server Error (HTTP/1.0 - RFC 1945) */ + public static final int SC_INTERNAL_SERVER_ERROR = 500; + /** 501 Not Implemented (HTTP/1.0 - RFC 1945) */ + public static final int SC_NOT_IMPLEMENTED = 501; + /** 502 Bad Gateway (HTTP/1.0 - RFC 1945) */ + public static final int SC_BAD_GATEWAY = 502; + /** 503 Service Unavailable (HTTP/1.0 - RFC 1945) */ + public static final int SC_SERVICE_UNAVAILABLE = 503; + /** 504 Gateway Timeout (HTTP/1.1 - RFC 2616) */ + public static final int SC_GATEWAY_TIMEOUT = 504; + /** 505 HTTP Version Not Supported (HTTP/1.1 - RFC 2616) */ + public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505; + + /** 507 Insufficient Storage (WebDAV - RFC 2518) */ + public static final int SC_INSUFFICIENT_STORAGE = 507; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpVersion.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpVersion.java new file mode 100644 index 000000000..3eae16172 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/HttpVersion.java @@ -0,0 +1,110 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Represents an HTTP version. HTTP uses a "major.minor" numbering + * scheme to indicate versions of the protocol. + *

+ * The version of an HTTP message is indicated by an HTTP-Version field + * in the first line of the message. + *

+ *
+ *     HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+ * 
+ * + * @since 4.0 + */ +@Immutable +public final class HttpVersion extends ProtocolVersion + implements Serializable { + + private static final long serialVersionUID = -5856653513894415344L; + + /** The protocol name. */ + public static final String HTTP = "HTTP"; + + /** HTTP protocol version 0.9 */ + public static final HttpVersion HTTP_0_9 = new HttpVersion(0, 9); + + /** HTTP protocol version 1.0 */ + public static final HttpVersion HTTP_1_0 = new HttpVersion(1, 0); + + /** HTTP protocol version 1.1 */ + public static final HttpVersion HTTP_1_1 = new HttpVersion(1, 1); + + + /** + * Create an HTTP protocol version designator. + * + * @param major the major version number of the HTTP protocol + * @param minor the minor version number of the HTTP protocol + * + * @throws IllegalArgumentException if either major or minor version number is negative + */ + public HttpVersion(final int major, final int minor) { + super(HTTP, major, minor); + } + + + /** + * Obtains a specific HTTP version. + * + * @param major the major version + * @param minor the minor version + * + * @return an instance of {@link HttpVersion} with the argument version + */ + @Override + public ProtocolVersion forVersion(final int major, final int minor) { + + if ((major == this.major) && (minor == this.minor)) { + return this; + } + + if (major == 1) { + if (minor == 0) { + return HTTP_1_0; + } + if (minor == 1) { + return HTTP_1_1; + } + } + if ((major == 0) && (minor == 9)) { + return HTTP_0_9; + } + + // argument checking is done in the constructor + return new HttpVersion(major, minor); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MalformedChunkCodingException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MalformedChunkCodingException.java new file mode 100644 index 000000000..7420ce08e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MalformedChunkCodingException.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +/** + * Signals a malformed chunked stream. + * + * @since 4.0 + */ +public class MalformedChunkCodingException extends IOException { + + private static final long serialVersionUID = 2158560246948994524L; + + /** + * Creates a MalformedChunkCodingException without a detail message. + */ + public MalformedChunkCodingException() { + super(); + } + + /** + * Creates a MalformedChunkCodingException with the specified detail message. + * + * @param message The exception detail message + */ + public MalformedChunkCodingException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MessageConstraintException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MessageConstraintException.java new file mode 100644 index 000000000..cd6146194 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MessageConstraintException.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +/** + * Signals a message constraint violation. + * + * @since 4.3 + */ +public class MessageConstraintException extends IOException { + + private static final long serialVersionUID = 6077207720446368695L; + + /** + * Creates a TruncatedChunkException with the specified detail message. + * + * @param message The exception detail message + */ + public MessageConstraintException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MethodNotSupportedException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MethodNotSupportedException.java new file mode 100644 index 000000000..b418576df --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/MethodNotSupportedException.java @@ -0,0 +1,59 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + + +/** + * Signals that an HTTP method is not supported. + * + * @since 4.0 + */ +public class MethodNotSupportedException extends HttpException { + + private static final long serialVersionUID = 3365359036840171201L; + + /** + * Creates a new MethodNotSupportedException with the specified detail message. + * + * @param message The exception detail message + */ + public MethodNotSupportedException(final String message) { + super(message); + } + + /** + * Creates a new MethodNotSupportedException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public MethodNotSupportedException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/NameValuePair.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/NameValuePair.java new file mode 100644 index 000000000..76ec354f9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/NameValuePair.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * A name / value pair parameter used as an element of HTTP messages. + *
+ * parameter               = attribute "=" value
+ * attribute               = token
+ * value                   = token | quoted-string
+ * 
+ * + * + * @since 4.0 + */ +public interface NameValuePair { + + String getName(); + + String getValue(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/NoHttpResponseException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/NoHttpResponseException.java new file mode 100644 index 000000000..1622da492 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/NoHttpResponseException.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.IOException; + +/** + * Signals that the target server failed to respond with a valid HTTP response. + * + * @since 4.0 + */ +public class NoHttpResponseException extends IOException { + + private static final long serialVersionUID = -7658940387386078766L; + + /** + * Creates a new NoHttpResponseException with the specified detail message. + * + * @param message exception message + */ + public NoHttpResponseException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ParseException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ParseException.java new file mode 100644 index 000000000..1c3596485 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ParseException.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * Signals a parse error. + * Parse errors when receiving a message will typically trigger + * {@link ProtocolException}. Parse errors that do not occur during + * protocol execution may be handled differently. + * This is an unchecked exception, since there are cases where + * the data to be parsed has been generated and is therefore + * known to be parseable. + * + * @since 4.0 + */ +public class ParseException extends RuntimeException { + + private static final long serialVersionUID = -7288819855864183578L; + + /** + * Creates a {@link ParseException} without details. + */ + public ParseException() { + super(); + } + + /** + * Creates a {@link ParseException} with a detail message. + * + * @param message the exception detail message, or null + */ + public ParseException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolException.java new file mode 100644 index 000000000..78fef4913 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolException.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * Signals that an HTTP protocol violation has occurred. + * For example a malformed status line or headers, a missing message body, etc. + * + * + * @since 4.0 + */ +public class ProtocolException extends HttpException { + + private static final long serialVersionUID = -2143571074341228994L; + + /** + * Creates a new ProtocolException with a null detail message. + */ + public ProtocolException() { + super(); + } + + /** + * Creates a new ProtocolException with the specified detail message. + * + * @param message The exception detail message + */ + public ProtocolException(final String message) { + super(message); + } + + /** + * Creates a new ProtocolException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public ProtocolException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolVersion.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolVersion.java new file mode 100644 index 000000000..058d24f94 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ProtocolVersion.java @@ -0,0 +1,264 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Represents a protocol version. The "major.minor" numbering + * scheme is used to indicate versions of the protocol. + *

+ * This class defines a protocol version as a combination of + * protocol name, major version number, and minor version number. + * Note that {@link #equals} and {@link #hashCode} are defined as + * final here, they cannot be overridden in derived classes. + *

+ * + * @since 4.0 + */ +@Immutable +public class ProtocolVersion implements Serializable, Cloneable { + + private static final long serialVersionUID = 8950662842175091068L; + + + /** Name of the protocol. */ + protected final String protocol; + + /** Major version number of the protocol */ + protected final int major; + + /** Minor version number of the protocol */ + protected final int minor; + + + /** + * Create a protocol version designator. + * + * @param protocol the name of the protocol, for example "HTTP" + * @param major the major version number of the protocol + * @param minor the minor version number of the protocol + */ + public ProtocolVersion(final String protocol, final int major, final int minor) { + this.protocol = Args.notNull(protocol, "Protocol name"); + this.major = Args.notNegative(major, "Protocol minor version"); + this.minor = Args.notNegative(minor, "Protocol minor version"); + } + + /** + * Returns the name of the protocol. + * + * @return the protocol name + */ + public final String getProtocol() { + return protocol; + } + + /** + * Returns the major version number of the protocol. + * + * @return the major version number. + */ + public final int getMajor() { + return major; + } + + /** + * Returns the minor version number of the HTTP protocol. + * + * @return the minor version number. + */ + public final int getMinor() { + return minor; + } + + + /** + * Obtains a specific version of this protocol. + * This can be used by derived classes to instantiate themselves instead + * of the base class, and to define constants for commonly used versions. + *
+ * The default implementation in this class returns this + * if the version matches, and creates a new {@link ProtocolVersion} + * otherwise. + * + * @param major the major version + * @param minor the minor version + * + * @return a protocol version with the same protocol name + * and the argument version + */ + public ProtocolVersion forVersion(final int major, final int minor) { + + if ((major == this.major) && (minor == this.minor)) { + return this; + } + + // argument checking is done in the constructor + return new ProtocolVersion(this.protocol, major, minor); + } + + + /** + * Obtains a hash code consistent with {@link #equals}. + * + * @return the hashcode of this protocol version + */ + @Override + public final int hashCode() { + return this.protocol.hashCode() ^ (this.major * 100000) ^ this.minor; + } + + + /** + * Checks equality of this protocol version with an object. + * The object is equal if it is a protocl version with the same + * protocol name, major version number, and minor version number. + * The specific class of the object is not relevant, + * instances of derived classes with identical attributes are + * equal to instances of the base class and vice versa. + * + * @param obj the object to compare with + * + * @return true if the argument is the same protocol version, + * false otherwise + */ + @Override + public final boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ProtocolVersion)) { + return false; + } + final ProtocolVersion that = (ProtocolVersion) obj; + + return ((this.protocol.equals(that.protocol)) && + (this.major == that.major) && + (this.minor == that.minor)); + } + + + /** + * Checks whether this protocol can be compared to another one. + * Only protocol versions with the same protocol name can be + * {@link #compareToVersion compared}. + * + * @param that the protocol version to consider + * + * @return true if {@link #compareToVersion compareToVersion} + * can be called with the argument, false otherwise + */ + public boolean isComparable(final ProtocolVersion that) { + return (that != null) && this.protocol.equals(that.protocol); + } + + + /** + * Compares this protocol version with another one. + * Only protocol versions with the same protocol name can be compared. + * This method does not define a total ordering, as it would be + * required for {@link java.lang.Comparable}. + * + * @param that the protocol version to compare with + * + * @return a negative integer, zero, or a positive integer + * as this version is less than, equal to, or greater than + * the argument version. + * + * @throws IllegalArgumentException + * if the argument has a different protocol name than this object, + * or if the argument is null + */ + public int compareToVersion(final ProtocolVersion that) { + Args.notNull(that, "Protocol version"); + Args.check(this.protocol.equals(that.protocol), + "Versions for different protocols cannot be compared: %s %s", this, that); + int delta = getMajor() - that.getMajor(); + if (delta == 0) { + delta = getMinor() - that.getMinor(); + } + return delta; + } + + + /** + * Tests if this protocol version is greater or equal to the given one. + * + * @param version the version against which to check this version + * + * @return true if this protocol version is + * {@link #isComparable comparable} to the argument + * and {@link #compareToVersion compares} as greater or equal, + * false otherwise + */ + public final boolean greaterEquals(final ProtocolVersion version) { + return isComparable(version) && (compareToVersion(version) >= 0); + } + + + /** + * Tests if this protocol version is less or equal to the given one. + * + * @param version the version against which to check this version + * + * @return true if this protocol version is + * {@link #isComparable comparable} to the argument + * and {@link #compareToVersion compares} as less or equal, + * false otherwise + */ + public final boolean lessEquals(final ProtocolVersion version) { + return isComparable(version) && (compareToVersion(version) <= 0); + } + + + /** + * Converts this protocol version to a string. + * + * @return a protocol version string, like "HTTP/1.1" + */ + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append(this.protocol); + buffer.append('/'); + buffer.append(Integer.toString(this.major)); + buffer.append('.'); + buffer.append(Integer.toString(this.minor)); + return buffer.toString(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/README.txt b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/README.txt new file mode 100644 index 000000000..cf4624ca4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/README.txt @@ -0,0 +1 @@ +These files are managed in the android-sync repo. Do not modify directly, or your changes will be lost. diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ReasonPhraseCatalog.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ReasonPhraseCatalog.java new file mode 100644 index 000000000..4cb5be0d0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/ReasonPhraseCatalog.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.util.Locale; + +/** + * Interface for obtaining reason phrases for HTTP status codes. + * + * @since 4.0 + */ +public interface ReasonPhraseCatalog { + + /** + * Obtains the reason phrase for a status code. + * The optional context allows for catalogs that detect + * the language for the reason phrase. + * + * @param status the status code, in the range 100-599 + * @param loc the preferred locale for the reason phrase + * + * @return the reason phrase, or null if unknown + */ + String getReason(int status, Locale loc); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/RequestLine.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/RequestLine.java new file mode 100644 index 000000000..e44d737b7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/RequestLine.java @@ -0,0 +1,49 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * The Request-Line begins with a method token, followed by the + * Request-URI and the protocol version, and ending with CRLF. The + * elements are separated by SP characters. No CR or LF is allowed + * except in the final CRLF sequence. + *
+ *      Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
+ * 
+ * + * @since 4.0 + */ +public interface RequestLine { + + String getMethod(); + + ProtocolVersion getProtocolVersion(); + + String getUri(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/StatusLine.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/StatusLine.java new file mode 100644 index 000000000..741db7ab9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/StatusLine.java @@ -0,0 +1,52 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * The first line of a Response message is the Status-Line, consisting + * of the protocol version followed by a numeric status code and its + * associated textual phrase, with each element separated by SP + * characters. No CR or LF is allowed except in the final CRLF sequence. + *
+ *     Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ * 
+ * + * @see HttpStatus + * @version $Id: StatusLine.java 937295 2010-04-23 13:44:00Z olegk $ + * + * @since 4.0 + */ +public interface StatusLine { + + ProtocolVersion getProtocolVersion(); + + int getStatusCode(); + + String getReasonPhrase(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/TokenIterator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/TokenIterator.java new file mode 100644 index 000000000..4f7ea5d55 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/TokenIterator.java @@ -0,0 +1,59 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +import java.util.Iterator; + +/** + * An iterator for {@link String} tokens. + * This interface is designed as a complement to + * {@link HeaderElementIterator}, in cases where the items + * are plain strings rather than full header elements. + * + * @since 4.0 + */ +public interface TokenIterator extends Iterator { + + /** + * Indicates whether there is another token in this iteration. + * + * @return true if there is another token, + * false otherwise + */ + boolean hasNext(); + + /** + * Obtains the next token from this iteration. + * This method should only be called while {@link #hasNext hasNext} + * is true. + * + * @return the next token in this iteration + */ + String nextToken(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/TruncatedChunkException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/TruncatedChunkException.java new file mode 100644 index 000000000..1cb994923 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/TruncatedChunkException.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + +/** + * Signals a truncated chunk in a chunked stream. + * + * @since 4.1 + */ +public class TruncatedChunkException extends MalformedChunkCodingException { + + private static final long serialVersionUID = -23506263930279460L; + + /** + * Creates a TruncatedChunkException with the specified detail message. + * + * @param message The exception detail message + */ + public TruncatedChunkException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/UnsupportedHttpVersionException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/UnsupportedHttpVersionException.java new file mode 100644 index 000000000..15647f4c9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/UnsupportedHttpVersionException.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib; + + +/** + * Signals an unsupported version of the HTTP protocol. + * + * @since 4.0 + */ +public class UnsupportedHttpVersionException extends ProtocolException { + + private static final long serialVersionUID = -1348448090193107031L; + + + /** + * Creates an exception without a detail message. + */ + public UnsupportedHttpVersionException() { + super(); + } + + /** + * Creates an exception with the specified detail message. + * + * @param message The exception detail message + */ + public UnsupportedHttpVersionException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/Base64.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/Base64.java new file mode 100644 index 000000000..40f9801bd --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/Base64.java @@ -0,0 +1,741 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.boye.httpclientandroidlib.androidextra; + +import java.io.UnsupportedEncodingException; + +/** + * Utilities for encoding and decoding the Base64 representation of + * binary data. See RFCs 2045 and 3548. + */ +public class Base64 { + /** + * Default values for encoder/decoder flags. + */ + public static final int DEFAULT = 0; + + /** + * Encoder flag bit to omit the padding '=' characters at the end + * of the output (if any). + */ + public static final int NO_PADDING = 1; + + /** + * Encoder flag bit to omit all line terminators (i.e., the output + * will be on one long line). + */ + public static final int NO_WRAP = 2; + + /** + * Encoder flag bit to indicate lines should be terminated with a + * CRLF pair instead of just an LF. Has no effect if {@code + * NO_WRAP} is specified as well. + */ + public static final int CRLF = 4; + + /** + * Encoder/decoder flag bit to indicate using the "URL and + * filename safe" variant of Base64 (see RFC 3548 section 4) where + * {@code -} and {@code _} are used in place of {@code +} and + * {@code /}. + */ + public static final int URL_SAFE = 8; + + /** + * Flag to pass to {@link Base64OutputStream} to indicate that it + * should not close the output stream it is wrapping when it + * itself is closed. + */ + public static final int NO_CLOSE = 16; + + // -------------------------------------------------------- + // shared code + // -------------------------------------------------------- + + /* package */ static abstract class Coder { + public byte[] output; + public int op; + + /** + * Encode/decode another block of input data. this.output is + * provided by the caller, and must be big enough to hold all + * the coded data. On exit, this.opwill be set to the length + * of the coded data. + * + * @param finish true if this is the final call to process for + * this object. Will finalize the coder state and + * include any final bytes in the output. + * + * @return true if the input so far is good; false if some + * error has been detected in the input stream.. + */ + public abstract boolean process(byte[] input, int offset, int len, boolean finish); + + /** + * @return the maximum number of bytes a call to process() + * could produce for the given number of input bytes. This may + * be an overestimate. + */ + public abstract int maxOutputSize(int len); + } + + // -------------------------------------------------------- + // decoding + // -------------------------------------------------------- + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param str the input String to decode, which is converted to + * bytes using the default charset + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(String str, int flags) { + return decode(str.getBytes(), flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param input the input array to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int flags) { + return decode(input, 0, input.length, flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param input the data to decode + * @param offset the position within the input array at which to start + * @param len the number of bytes of input to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int offset, int len, int flags) { + // Allocate space for the most data the input could represent. + // (It could contain less if it contains whitespace, etc.) + Decoder decoder = new Decoder(flags, new byte[len*3/4]); + + if (!decoder.process(input, offset, len, true)) { + throw new IllegalArgumentException("bad base-64"); + } + + // Maybe we got lucky and allocated exactly enough output space. + if (decoder.op == decoder.output.length) { + return decoder.output; + } + + // Need to shorten the array, so allocate a new one of the + // right size and copy. + byte[] temp = new byte[decoder.op]; + System.arraycopy(decoder.output, 0, temp, 0, decoder.op); + return temp; + } + + /* package */ static class Decoder extends Coder { + /** + * Lookup table for turning bytes into their position in the + * Base64 alphabet. + */ + private static final int DECODE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** + * Decode lookup table for the "web safe" variant (RFC 3548 + * sec. 4) where - and _ replace + and /. + */ + private static final int DECODE_WEBSAFE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** Non-data values in the DECODE arrays. */ + private static final int SKIP = -1; + private static final int EQUALS = -2; + + /** + * States 0-3 are reading through the next input tuple. + * State 4 is having read one '=' and expecting exactly + * one more. + * State 5 is expecting no more data or padding characters + * in the input. + * State 6 is the error state; an error has been detected + * in the input and no future input can "fix" it. + */ + private int state; // state number (0 to 6) + private int value; + + final private int[] alphabet; + + public Decoder(int flags, byte[] output) { + this.output = output; + + alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; + state = 0; + value = 0; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could decode to. + */ + public int maxOutputSize(int len) { + return len * 3/4 + 10; + } + + /** + * Decode another block of input data. + * + * @return true if the state machine is still healthy. false if + * bad base-64 data has been detected in the input stream. + */ + public boolean process(byte[] input, int offset, int len, boolean finish) { + if (this.state == 6) return false; + + int p = offset; + len += offset; + + // Using local variables makes the decoder about 12% + // faster than if we manipulate the member variables in + // the loop. (Even alphabet makes a measurable + // difference, which is somewhat surprising to me since + // the member variable is final.) + int state = this.state; + int value = this.value; + int op = 0; + final byte[] output = this.output; + final int[] alphabet = this.alphabet; + + while (p < len) { + // Try the fast path: we're starting a new tuple and the + // next four bytes of the input stream are all data + // bytes. This corresponds to going through states + // 0-1-2-3-0. We expect to use this method for most of + // the data. + // + // If any of the next four bytes of input are non-data + // (whitespace, etc.), value will end up negative. (All + // the non-data values in decode are small negative + // numbers, so shifting any of them up and or'ing them + // together will result in a value with its top bit set.) + // + // You can remove this whole block and the output should + // be the same, just slower. + if (state == 0) { + while (p+4 <= len && + (value = ((alphabet[input[p] & 0xff] << 18) | + (alphabet[input[p+1] & 0xff] << 12) | + (alphabet[input[p+2] & 0xff] << 6) | + (alphabet[input[p+3] & 0xff]))) >= 0) { + output[op+2] = (byte) value; + output[op+1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + p += 4; + } + if (p >= len) break; + } + + // The fast path isn't available -- either we've read a + // partial tuple, or the next four input bytes aren't all + // data, or whatever. Fall back to the slower state + // machine implementation. + + int d = alphabet[input[p++] & 0xff]; + + switch (state) { + case 0: + if (d >= 0) { + value = d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 1: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 2: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect exactly one more padding character. + output[op++] = (byte) (value >> 4); + state = 4; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 3: + if (d >= 0) { + // Emit the output triple and return to state 0. + value = (value << 6) | d; + output[op+2] = (byte) value; + output[op+1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + state = 0; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect no further data or padding characters. + output[op+1] = (byte) (value >> 2); + output[op] = (byte) (value >> 10); + op += 2; + state = 5; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 4: + if (d == EQUALS) { + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 5: + if (d != SKIP) { + this.state = 6; + return false; + } + break; + } + } + + if (!finish) { + // We're out of input, but a future call could provide + // more. + this.state = state; + this.value = value; + this.op = op; + return true; + } + + // Done reading input. Now figure out where we are left in + // the state machine and finish up. + + switch (state) { + case 0: + // Output length is a multiple of three. Fine. + break; + case 1: + // Read one extra input byte, which isn't enough to + // make another output byte. Illegal. + this.state = 6; + return false; + case 2: + // Read two extra input bytes, enough to emit 1 more + // output byte. Fine. + output[op++] = (byte) (value >> 4); + break; + case 3: + // Read three extra input bytes, enough to emit 2 more + // output bytes. Fine. + output[op++] = (byte) (value >> 10); + output[op++] = (byte) (value >> 2); + break; + case 4: + // Read one padding '=' when we expected 2. Illegal. + this.state = 6; + return false; + case 5: + // Read all the padding '='s we expected and no more. + // Fine. + break; + } + + this.state = state; + this.op = op; + return true; + } + } + + // -------------------------------------------------------- + // encoding + // -------------------------------------------------------- + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int flags) { + try { + return new String(encode(input, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int offset, int len, int flags) { + try { + return new String(encode(input, offset, len, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int flags) { + return encode(input, 0, input.length, flags); + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int offset, int len, int flags) { + Encoder encoder = new Encoder(flags, null); + + // Compute the exact length of the array we will produce. + int output_len = len / 3 * 4; + + // Account for the tail of the data and the padding bytes, if any. + if (encoder.do_padding) { + if (len % 3 > 0) { + output_len += 4; + } + } else { + switch (len % 3) { + case 0: break; + case 1: output_len += 2; break; + case 2: output_len += 3; break; + } + } + + // Account for the newlines, if any. + if (encoder.do_newline && len > 0) { + output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) * + (encoder.do_cr ? 2 : 1); + } + + encoder.output = new byte[output_len]; + encoder.process(input, offset, len, true); + + assert encoder.op == output_len; + + return encoder.output; + } + + /* package */ static class Encoder extends Coder { + /** + * Emit a new line every this many output tuples. Corresponds to + * a 76-character line length (the maximum allowable according to + * RFC 2045). + */ + public static final int LINE_GROUPS = 19; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', + }; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE_WEBSAFE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', + }; + + final private byte[] tail; + /* package */ int tailLen; + private int count; + + final public boolean do_padding; + final public boolean do_newline; + final public boolean do_cr; + final private byte[] alphabet; + + public Encoder(int flags, byte[] output) { + this.output = output; + + do_padding = (flags & NO_PADDING) == 0; + do_newline = (flags & NO_WRAP) == 0; + do_cr = (flags & CRLF) != 0; + alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; + + tail = new byte[2]; + tailLen = 0; + + count = do_newline ? LINE_GROUPS : -1; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could encode to. + */ + public int maxOutputSize(int len) { + return len * 8/5 + 10; + } + + public boolean process(byte[] input, int offset, int len, boolean finish) { + // Using local variables makes the encoder about 9% faster. + final byte[] alphabet = this.alphabet; + final byte[] output = this.output; + int op = 0; + int count = this.count; + + int p = offset; + len += offset; + int v = -1; + + // First we need to concatenate the tail of the previous call + // with any input bytes available now and see if we can empty + // the tail. + + switch (tailLen) { + case 0: + // There was no tail. + break; + + case 1: + if (p+2 <= len) { + // A 1-byte tail with at least 2 bytes of + // input available now. + v = ((tail[0] & 0xff) << 16) | + ((input[p++] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + }; + break; + + case 2: + if (p+1 <= len) { + // A 2-byte tail with at least 1 byte of input. + v = ((tail[0] & 0xff) << 16) | + ((tail[1] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + } + break; + } + + if (v != -1) { + output[op++] = alphabet[(v >> 18) & 0x3f]; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + // At this point either there is no tail, or there are fewer + // than 3 bytes of input available. + + // The main loop, turning 3 input bytes into 4 output bytes on + // each iteration. + while (p+3 <= len) { + v = ((input[p] & 0xff) << 16) | + ((input[p+1] & 0xff) << 8) | + (input[p+2] & 0xff); + output[op] = alphabet[(v >> 18) & 0x3f]; + output[op+1] = alphabet[(v >> 12) & 0x3f]; + output[op+2] = alphabet[(v >> 6) & 0x3f]; + output[op+3] = alphabet[v & 0x3f]; + p += 3; + op += 4; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + if (finish) { + // Finish up the tail of the input. Note that we need to + // consume any bytes in tail before any bytes + // remaining in input; there should be at most two bytes + // total. + + if (p-tailLen == len-1) { + int t = 0; + v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; + tailLen -= t; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (p-tailLen == len-2) { + int t = 0; + v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | + (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); + tailLen -= t; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (do_newline && op > 0 && count != LINE_GROUPS) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + + assert tailLen == 0; + assert p == len; + } else { + // Save the leftovers in tail to be consumed on the next + // call to encodeInternal. + + if (p == len-1) { + tail[tailLen++] = input[p]; + } else if (p == len-2) { + tail[tailLen++] = input[p]; + tail[tailLen++] = input[p+1]; + } + } + + this.op = op; + this.count = count; + + return true; + } + } + + private Base64() { } // don't instantiate +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/HttpClientAndroidLog.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/HttpClientAndroidLog.java new file mode 100644 index 000000000..89758f315 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/androidextra/HttpClientAndroidLog.java @@ -0,0 +1,113 @@ +package ch.boye.httpclientandroidlib.androidextra; + +import android.util.Log; + +public class HttpClientAndroidLog { + + private String logTag; + private boolean debugEnabled; + private boolean errorEnabled; + private boolean traceEnabled; + private boolean warnEnabled; + private boolean infoEnabled; + + public HttpClientAndroidLog(Object tag) { + logTag=tag.toString(); + debugEnabled=false; + errorEnabled=false; + traceEnabled=false; + warnEnabled=false; + infoEnabled=false; + } + + public void enableDebug(boolean enable) { + debugEnabled=enable; + } + + public boolean isDebugEnabled() { + return debugEnabled; + } + + public void debug(Object message) { + if(isDebugEnabled()) + Log.d(logTag, message.toString()); + } + + public void debug(Object message, Throwable t) { + if(isDebugEnabled()) + Log.d(logTag, message.toString(), t); + } + + public void enableError(boolean enable) { + errorEnabled=enable; + } + + public boolean isErrorEnabled() { + return errorEnabled; + } + + public void error(Object message) { + if(isErrorEnabled()) + Log.e(logTag, message.toString()); + } + + public void error(Object message, Throwable t) { + if(isErrorEnabled()) + Log.e(logTag, message.toString(), t); + } + + public void enableWarn(boolean enable) { + warnEnabled=enable; + } + + public boolean isWarnEnabled() { + return warnEnabled; + } + + public void warn(Object message) { + if(isWarnEnabled()) + Log.w(logTag, message.toString()); + } + + public void warn(Object message, Throwable t) { + if(isWarnEnabled()) + Log.w(logTag, message.toString(), t); + } + + public void enableInfo(boolean enable) { + infoEnabled=enable; + } + + public boolean isInfoEnabled() { + return infoEnabled; + } + + public void info(Object message) { + if(isInfoEnabled()) + Log.i(logTag, message.toString()); + } + + public void info(Object message, Throwable t) { + if(isInfoEnabled()) + Log.i(logTag, message.toString(), t); + } + + public void enableTrace(boolean enable) { + traceEnabled=enable; + } + + public boolean isTraceEnabled() { + return traceEnabled; + } + + public void trace(Object message) { + if(isTraceEnabled()) + Log.i(logTag, message.toString()); + } + + public void trace(Object message, Throwable t) { + if(isTraceEnabled()) + Log.i(logTag, message.toString(), t); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/GuardedBy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/GuardedBy.java new file mode 100644 index 000000000..2a61da752 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/GuardedBy.java @@ -0,0 +1,76 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The field or method to which this annotation is applied can only be accessed + * when holding a particular lock, which may be a built-in (synchronization) lock, + * or may be an explicit java.util.concurrent.Lock. + * + * The argument determines which lock guards the annotated field or method: + *

    + *
  • + * this : The intrinsic lock of the object in whose class the field is defined. + *
  • + *
  • + * class-name.this : For inner classes, it may be necessary to disambiguate 'this'; + * the class-name.this designation allows you to specify which 'this' reference is intended + *
  • + *
  • + * itself : For reference fields only; the object to which the field refers. + *
  • + *
  • + * field-name : The lock object is referenced by the (instance or static) field + * specified by field-name. + *
  • + *
  • + * class-name.field-name : The lock object is reference by the static field specified + * by class-name.field-name. + *
  • + *
  • + * method-name() : The lock object is returned by calling the named nil-ary method. + *
  • + *
  • + * class-name.class : The Class object for the specified class should be used as the lock object. + *
  • + *

    + * Based on code developed by Brian Goetz and Tim Peierls and concepts + * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls, + * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea. + */ +@Documented +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.CLASS) // The original version used RUNTIME +public @interface GuardedBy { + String value(); +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/Immutable.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/Immutable.java new file mode 100644 index 000000000..3aa8eb86e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/Immutable.java @@ -0,0 +1,59 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The class to which this annotation is applied is immutable. This means that + * its state cannot be seen to change by callers, which implies that + *

      + *
    • all public fields are final,
    • + *
    • all public final reference fields refer to other immutable objects, and
    • + *
    • constructors and methods do not publish references to any internal state + * which is potentially mutable by the implementation.
    • + *
    + * Immutable objects may still have internal mutable state for purposes of performance + * optimization; some state variables may be lazily computed, so long as they are computed + * from immutable state and that callers cannot tell the difference. + *

    + * Immutable objects are inherently thread-safe; they may be passed between threads or + * published without synchronization. + *

    + * Based on code developed by Brian Goetz and Tim Peierls and concepts + * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls, + * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.CLASS) // The original version used RUNTIME +public @interface Immutable { +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/NotThreadSafe.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/NotThreadSafe.java new file mode 100644 index 000000000..0cab7dda2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/NotThreadSafe.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The class to which this annotation is applied is not thread-safe. + * This annotation primarily exists for clarifying the non-thread-safety of a class + * that might otherwise be assumed to be thread-safe, despite the fact that it is a bad + * idea to assume a class is thread-safe without good reason. + * @see ThreadSafe + *

    + * Based on code developed by Brian Goetz and Tim Peierls and concepts + * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls, + * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.CLASS) // The original version used RUNTIME +public @interface NotThreadSafe { +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/ThreadSafe.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/ThreadSafe.java new file mode 100644 index 000000000..c8b1616d9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/ThreadSafe.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The class to which this annotation is applied is thread-safe. This means that + * no sequences of accesses (reads and writes to public fields, calls to public methods) + * may put the object into an invalid state, regardless of the interleaving of those actions + * by the runtime, and without requiring any additional synchronization or coordination on the + * part of the caller. + * @see NotThreadSafe + *

    + * Based on code developed by Brian Goetz and Tim Peierls and concepts + * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls, + * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.CLASS) // The original version used RUNTIME +public @interface ThreadSafe { +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/package-info.java new file mode 100644 index 000000000..d74e8fef9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/annotation/package-info.java @@ -0,0 +1,34 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Thread-safety annotations based on JCIP-ANNOTATIONS + *
    + * Copyright (c) 2005 Brian Goetz and Tim Peierls. + * See http://www.jcip.net + */ +package ch.boye.httpclientandroidlib.annotation; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AUTH.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AUTH.java new file mode 100644 index 000000000..a0b50db73 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AUTH.java @@ -0,0 +1,64 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Constants and static helpers related to the HTTP authentication. + * + * + * @since 4.0 + */ +@Immutable +public final class AUTH { + + /** + * The www authenticate challange header. + */ + public static final String WWW_AUTH = "WWW-Authenticate"; + + /** + * The www authenticate response header. + */ + public static final String WWW_AUTH_RESP = "Authorization"; + + /** + * The proxy authenticate challange header. + */ + public static final String PROXY_AUTH = "Proxy-Authenticate"; + + /** + * The proxy authenticate response header. + */ + public static final String PROXY_AUTH_RESP = "Proxy-Authorization"; + + private AUTH() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthOption.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthOption.java new file mode 100644 index 000000000..ae69df94d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthOption.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * @since 4.2 + */ +@Immutable +public final class AuthOption { + + private final AuthScheme authScheme; + private final Credentials creds; + + public AuthOption(final AuthScheme authScheme, final Credentials creds) { + super(); + Args.notNull(authScheme, "Auth scheme"); + Args.notNull(creds, "User credentials"); + this.authScheme = authScheme; + this.creds = creds; + } + + public AuthScheme getAuthScheme() { + return this.authScheme; + } + + public Credentials getCredentials() { + return this.creds; + } + + @Override + public String toString() { + return this.authScheme.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthProtocolState.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthProtocolState.java new file mode 100644 index 000000000..081838915 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthProtocolState.java @@ -0,0 +1,33 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +public enum AuthProtocolState { + + UNCHALLENGED, CHALLENGED, HANDSHAKE, FAILURE, SUCCESS + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScheme.java new file mode 100644 index 000000000..a028f182d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScheme.java @@ -0,0 +1,130 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; + +/** + * This interface represents an abstract challenge-response oriented + * authentication scheme. + *

    + * An authentication scheme should be able to support the following + * functions: + *

      + *
    • Parse and process the challenge sent by the target server + * in response to request for a protected resource + *
    • Provide its textual designation + *
    • Provide its parameters, if available + *
    • Provide the realm this authentication scheme is applicable to, + * if available + *
    • Generate authorization string for the given set of credentials + * and the HTTP request in response to the authorization challenge. + *
    + *

    + * Authentication schemes may be stateful involving a series of + * challenge-response exchanges. + *

    + * IMPORTANT: implementations of this interface MUST also implement {@link ContextAwareAuthScheme} + * interface in order to remain API compatible with newer versions of HttpClient. + * + * @since 4.0 + */ + +public interface AuthScheme { + + /** + * Processes the given challenge token. Some authentication schemes + * may involve multiple challenge-response exchanges. Such schemes must be able + * to maintain the state information when dealing with sequential challenges + * + * @param header the challenge header + */ + void processChallenge(final Header header) throws MalformedChallengeException; + + /** + * Returns textual designation of the given authentication scheme. + * + * @return the name of the given authentication scheme + */ + String getSchemeName(); + + /** + * Returns authentication parameter with the given name, if available. + * + * @param name The name of the parameter to be returned + * + * @return the parameter with the given name + */ + String getParameter(final String name); + + /** + * Returns authentication realm. If the concept of an authentication + * realm is not applicable to the given authentication scheme, returns + * null. + * + * @return the authentication realm + */ + String getRealm(); + + /** + * Tests if the authentication scheme is provides authorization on a per + * connection basis instead of usual per request basis + * + * @return true if the scheme is connection based, false + * if the scheme is request based. + */ + boolean isConnectionBased(); + + /** + * Authentication process may involve a series of challenge-response exchanges. + * This method tests if the authorization process has been completed, either + * successfully or unsuccessfully, that is, all the required authorization + * challenges have been processed in their entirety. + * + * @return true if the authentication process has been completed, + * false otherwise. + */ + boolean isComplete(); + + /** + * Produces an authorization string for the given set of {@link Credentials}. + * + * @param credentials The set of credentials to be used for athentication + * @param request The request being authenticated + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * + * @return the authorization string + * + * @deprecated (4.1) Use {@link ContextAwareAuthScheme#authenticate(Credentials, HttpRequest, ch.boye.httpclientandroidlib.protocol.HttpContext)} + */ + @Deprecated + Header authenticate(Credentials credentials, HttpRequest request) + throws AuthenticationException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeFactory.java new file mode 100644 index 000000000..d88a3d06f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeFactory.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * Factory for {@link AuthScheme} implementations. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link AuthSchemeProvider} + */ +@Deprecated +public interface AuthSchemeFactory { + + /** + * Creates an instance of {@link AuthScheme} using given HTTP parameters. + * + * @param params HTTP parameters. + * + * @return auth scheme. + */ + AuthScheme newInstance(HttpParams params); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeProvider.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeProvider.java new file mode 100644 index 000000000..bfaf3f3cf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeProvider.java @@ -0,0 +1,46 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Factory for {@link AuthScheme} implementations. + * + * @since 4.3 + */ +public interface AuthSchemeProvider { + + /** + * Creates an instance of {@link AuthScheme}. + * + * @return auth scheme. + */ + AuthScheme create(HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeRegistry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeRegistry.java new file mode 100644 index 000000000..4a6d15ca8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthSchemeRegistry.java @@ -0,0 +1,155 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.ExecutionContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Authentication scheme registry that can be used to obtain the corresponding + * authentication scheme implementation for a given type of authorization challenge. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.config.Registry} + */ +@ThreadSafe +@Deprecated +public final class AuthSchemeRegistry implements Lookup { + + private final ConcurrentHashMap registeredSchemes; + + public AuthSchemeRegistry() { + super(); + this.registeredSchemes = new ConcurrentHashMap(); + } + + /** + * Registers a {@link AuthSchemeFactory} with the given identifier. If a factory with the + * given name already exists it will be overridden. This name is the same one used to + * retrieve the {@link AuthScheme authentication scheme} from {@link #getAuthScheme}. + * + *

    + * Please note that custom authentication preferences, if used, need to be updated accordingly + * for the new {@link AuthScheme authentication scheme} to take effect. + *

    + * + * @param name the identifier for this scheme + * @param factory the {@link AuthSchemeFactory} class to register + * + * @see #getAuthScheme + */ + public void register( + final String name, + final AuthSchemeFactory factory) { + Args.notNull(name, "Name"); + Args.notNull(factory, "Authentication scheme factory"); + registeredSchemes.put(name.toLowerCase(Locale.ENGLISH), factory); + } + + /** + * Unregisters the class implementing an {@link AuthScheme authentication scheme} with + * the given name. + * + * @param name the identifier of the class to unregister + */ + public void unregister(final String name) { + Args.notNull(name, "Name"); + registeredSchemes.remove(name.toLowerCase(Locale.ENGLISH)); + } + + /** + * Gets the {@link AuthScheme authentication scheme} with the given name. + * + * @param name the {@link AuthScheme authentication scheme} identifier + * @param params the {@link HttpParams HTTP parameters} for the authentication + * scheme. + * + * @return {@link AuthScheme authentication scheme} + * + * @throws IllegalStateException if a scheme with the given name cannot be found + */ + public AuthScheme getAuthScheme(final String name, final HttpParams params) + throws IllegalStateException { + + Args.notNull(name, "Name"); + final AuthSchemeFactory factory = registeredSchemes.get(name.toLowerCase(Locale.ENGLISH)); + if (factory != null) { + return factory.newInstance(params); + } else { + throw new IllegalStateException("Unsupported authentication scheme: " + name); + } + } + + /** + * Obtains a list containing the names of all registered {@link AuthScheme authentication + * schemes} + * + * @return list of registered scheme names + */ + public List getSchemeNames() { + return new ArrayList(registeredSchemes.keySet()); + } + + /** + * Populates the internal collection of registered {@link AuthScheme authentication schemes} + * with the content of the map passed as a parameter. + * + * @param map authentication schemes + */ + public void setItems(final Map map) { + if (map == null) { + return; + } + registeredSchemes.clear(); + registeredSchemes.putAll(map); + } + + public AuthSchemeProvider lookup(final String name) { + return new AuthSchemeProvider() { + + public AuthScheme create(final HttpContext context) { + final HttpRequest request = (HttpRequest) context.getAttribute( + ExecutionContext.HTTP_REQUEST); + return getAuthScheme(name, request.getParams()); + } + + }; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScope.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScope.java new file mode 100644 index 000000000..b6384729d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthScope.java @@ -0,0 +1,302 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * The class represents an authentication scope consisting of a host name, + * a port number, a realm name and an authentication scheme name which + * {@link Credentials Credentials} apply to. + * + * + * @since 4.0 + */ +@Immutable +public class AuthScope { + + /** + * The null value represents any host. In the future versions of + * HttpClient the use of this parameter will be discontinued. + */ + public static final String ANY_HOST = null; + + /** + * The -1 value represents any port. + */ + public static final int ANY_PORT = -1; + + /** + * The null value represents any realm. + */ + public static final String ANY_REALM = null; + + /** + * The null value represents any authentication scheme. + */ + public static final String ANY_SCHEME = null; + + /** + * Default scope matching any host, port, realm and authentication scheme. + * In the future versions of HttpClient the use of this parameter will be + * discontinued. + */ + public static final AuthScope ANY = new AuthScope(ANY_HOST, ANY_PORT, ANY_REALM, ANY_SCHEME); + + /** The authentication scheme the credentials apply to. */ + private final String scheme; + + /** The realm the credentials apply to. */ + private final String realm; + + /** The host the credentials apply to. */ + private final String host; + + /** The port the credentials apply to. */ + private final int port; + + /** Creates a new credentials scope for the given + * host, port, realm, and + * authentication scheme. + * + * @param host the host the credentials apply to. May be set + * to null if credentials are applicable to + * any host. + * @param port the port the credentials apply to. May be set + * to negative value if credentials are applicable to + * any port. + * @param realm the realm the credentials apply to. May be set + * to null if credentials are applicable to + * any realm. + * @param scheme the authentication scheme the credentials apply to. + * May be set to null if credentials are applicable to + * any authentication scheme. + */ + public AuthScope(final String host, final int port, + final String realm, final String scheme) + { + this.host = (host == null) ? ANY_HOST: host.toLowerCase(Locale.ENGLISH); + this.port = (port < 0) ? ANY_PORT: port; + this.realm = (realm == null) ? ANY_REALM: realm; + this.scheme = (scheme == null) ? ANY_SCHEME: scheme.toUpperCase(Locale.ENGLISH); + } + + /** + * @since 4.2 + */ + public AuthScope(final HttpHost host, final String realm, final String schemeName) { + this(host.getHostName(), host.getPort(), realm, schemeName); + } + + /** + * @since 4.2 + */ + public AuthScope(final HttpHost host) { + this(host, ANY_REALM, ANY_SCHEME); + } + + /** Creates a new credentials scope for the given + * host, port, realm, and any + * authentication scheme. + * + * @param host the host the credentials apply to. May be set + * to null if credentials are applicable to + * any host. + * @param port the port the credentials apply to. May be set + * to negative value if credentials are applicable to + * any port. + * @param realm the realm the credentials apply to. May be set + * to null if credentials are applicable to + * any realm. + */ + public AuthScope(final String host, final int port, final String realm) { + this(host, port, realm, ANY_SCHEME); + } + + /** Creates a new credentials scope for the given + * host, port, any realm name, and any + * authentication scheme. + * + * @param host the host the credentials apply to. May be set + * to null if credentials are applicable to + * any host. + * @param port the port the credentials apply to. May be set + * to negative value if credentials are applicable to + * any port. + */ + public AuthScope(final String host, final int port) { + this(host, port, ANY_REALM, ANY_SCHEME); + } + + /** + * Creates a copy of the given credentials scope. + */ + public AuthScope(final AuthScope authscope) { + super(); + Args.notNull(authscope, "Scope"); + this.host = authscope.getHost(); + this.port = authscope.getPort(); + this.realm = authscope.getRealm(); + this.scheme = authscope.getScheme(); + } + + /** + * @return the host + */ + public String getHost() { + return this.host; + } + + /** + * @return the port + */ + public int getPort() { + return this.port; + } + + /** + * @return the realm name + */ + public String getRealm() { + return this.realm; + } + + /** + * @return the scheme type + */ + public String getScheme() { + return this.scheme; + } + + /** + * Tests if the authentication scopes match. + * + * @return the match factor. Negative value signifies no match. + * Non-negative signifies a match. The greater the returned value + * the closer the match. + */ + public int match(final AuthScope that) { + int factor = 0; + if (LangUtils.equals(this.scheme, that.scheme)) { + factor += 1; + } else { + if (this.scheme != ANY_SCHEME && that.scheme != ANY_SCHEME) { + return -1; + } + } + if (LangUtils.equals(this.realm, that.realm)) { + factor += 2; + } else { + if (this.realm != ANY_REALM && that.realm != ANY_REALM) { + return -1; + } + } + if (this.port == that.port) { + factor += 4; + } else { + if (this.port != ANY_PORT && that.port != ANY_PORT) { + return -1; + } + } + if (LangUtils.equals(this.host, that.host)) { + factor += 8; + } else { + if (this.host != ANY_HOST && that.host != ANY_HOST) { + return -1; + } + } + return factor; + } + + /** + * @see java.lang.Object#equals(Object) + */ + @Override + public boolean equals(final Object o) { + if (o == null) { + return false; + } + if (o == this) { + return true; + } + if (!(o instanceof AuthScope)) { + return super.equals(o); + } + final AuthScope that = (AuthScope) o; + return + LangUtils.equals(this.host, that.host) + && this.port == that.port + && LangUtils.equals(this.realm, that.realm) + && LangUtils.equals(this.scheme, that.scheme); + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + if (this.scheme != null) { + buffer.append(this.scheme.toUpperCase(Locale.ENGLISH)); + buffer.append(' '); + } + if (this.realm != null) { + buffer.append('\''); + buffer.append(this.realm); + buffer.append('\''); + } else { + buffer.append(""); + } + if (this.host != null) { + buffer.append('@'); + buffer.append(this.host); + if (this.port >= 0) { + buffer.append(':'); + buffer.append(this.port); + } + } + return buffer.toString(); + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.host); + hash = LangUtils.hashCode(hash, this.port); + hash = LangUtils.hashCode(hash, this.realm); + hash = LangUtils.hashCode(hash, this.scheme); + return hash; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthState.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthState.java new file mode 100644 index 000000000..236f934ac --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/AuthState.java @@ -0,0 +1,235 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.util.Queue; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * This class provides detailed information about the state of the authentication process. + * + * @since 4.0 + */ +@NotThreadSafe +public class AuthState { + + /** Actual state of authentication protocol */ + private AuthProtocolState state; + + /** Actual authentication scheme */ + private AuthScheme authScheme; + + /** Actual authentication scope */ + private AuthScope authScope; + + /** Credentials selected for authentication */ + private Credentials credentials; + + /** Available auth options */ + private Queue authOptions; + + public AuthState() { + super(); + this.state = AuthProtocolState.UNCHALLENGED; + } + + /** + * Resets the auth state. + * + * @since 4.2 + */ + public void reset() { + this.state = AuthProtocolState.UNCHALLENGED; + this.authOptions = null; + this.authScheme = null; + this.authScope = null; + this.credentials = null; + } + + /** + * @since 4.2 + */ + public AuthProtocolState getState() { + return this.state; + } + + /** + * @since 4.2 + */ + public void setState(final AuthProtocolState state) { + this.state = state != null ? state : AuthProtocolState.UNCHALLENGED; + } + + /** + * Returns actual {@link AuthScheme}. May be null. + */ + public AuthScheme getAuthScheme() { + return this.authScheme; + } + + /** + * Returns actual {@link Credentials}. May be null. + */ + public Credentials getCredentials() { + return this.credentials; + } + + /** + * Updates the auth state with {@link AuthScheme} and {@link Credentials}. + * + * @param authScheme auth scheme. May not be null. + * @param credentials user crednetials. May not be null. + * + * @since 4.2 + */ + public void update(final AuthScheme authScheme, final Credentials credentials) { + Args.notNull(authScheme, "Auth scheme"); + Args.notNull(credentials, "Credentials"); + this.authScheme = authScheme; + this.credentials = credentials; + this.authOptions = null; + } + + /** + * Returns available {@link AuthOption}s. May be null. + * + * @since 4.2 + */ + public Queue getAuthOptions() { + return this.authOptions; + } + + /** + * Returns true if {@link AuthOption}s are available, false + * otherwise. + * + * @since 4.2 + */ + public boolean hasAuthOptions() { + return this.authOptions != null && !this.authOptions.isEmpty(); + } + + /** + * Updates the auth state with a queue of {@link AuthOption}s. + * + * @param authOptions a queue of auth options. May not be null or empty. + * + * @since 4.2 + */ + public void update(final Queue authOptions) { + Args.notEmpty(authOptions, "Queue of auth options"); + this.authOptions = authOptions; + this.authScheme = null; + this.credentials = null; + } + + /** + * Invalidates the authentication state by resetting its parameters. + * + * @deprecated (4.2) use {@link #reset()} + */ + @Deprecated + public void invalidate() { + reset(); + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + public boolean isValid() { + return this.authScheme != null; + } + + /** + * Assigns the given {@link AuthScheme authentication scheme}. + * + * @param authScheme the {@link AuthScheme authentication scheme} + * + * @deprecated (4.2) use {@link #update(AuthScheme, Credentials)} + */ + @Deprecated + public void setAuthScheme(final AuthScheme authScheme) { + if (authScheme == null) { + reset(); + return; + } + this.authScheme = authScheme; + } + + /** + * Sets user {@link Credentials} to be used for authentication + * + * @param credentials User credentials + * + * @deprecated (4.2) use {@link #update(AuthScheme, Credentials)} + */ + @Deprecated + public void setCredentials(final Credentials credentials) { + this.credentials = credentials; + } + + /** + * Returns actual {@link AuthScope} if available + * + * @return actual authentication scope if available, null. + * + */ +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals a failure in authentication process + * + * + * @since 4.0 + */ +@Immutable +public class AuthenticationException extends ProtocolException { + + private static final long serialVersionUID = -6794031905674764776L; + + /** + * Creates a new AuthenticationException with a null detail message. + */ + public AuthenticationException() { + super(); + } + + /** + * Creates a new AuthenticationException with the specified message. + * + * @param message the exception detail message + */ + public AuthenticationException(final String message) { + super(message); + } + + /** + * Creates a new AuthenticationException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public AuthenticationException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/BasicUserPrincipal.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/BasicUserPrincipal.java new file mode 100644 index 000000000..e3bf323bb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/BasicUserPrincipal.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.io.Serializable; +import java.security.Principal; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Basic user principal used for HTTP authentication + * + * @since 4.0 + */ +@Immutable +public final class BasicUserPrincipal implements Principal, Serializable { + + private static final long serialVersionUID = -2266305184969850467L; + + private final String username; + + public BasicUserPrincipal(final String username) { + super(); + Args.notNull(username, "User name"); + this.username = username; + } + + public String getName() { + return this.username; + } + + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.username); + return hash; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o instanceof BasicUserPrincipal) { + final BasicUserPrincipal that = (BasicUserPrincipal) o; + if (LangUtils.equals(this.username, that.username)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append("[principal: "); + buffer.append(this.username); + buffer.append("]"); + return buffer.toString(); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ChallengeState.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ChallengeState.java new file mode 100644 index 000000000..8ff59b7bb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ChallengeState.java @@ -0,0 +1,38 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +/** + * Challenge mode (TARGET or PROXY) + * + * @since 4.2 + */ +public enum ChallengeState { + + TARGET, PROXY + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ContextAwareAuthScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ContextAwareAuthScheme.java new file mode 100644 index 000000000..114679573 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/ContextAwareAuthScheme.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * This interface represents an extended authentication scheme + * that requires access to {@link HttpContext} in order to + * generate an authorization string. + * + * TODO: Fix AuthScheme interface in the next major version + * + * @since 4.1 + */ + +public interface ContextAwareAuthScheme extends AuthScheme { + + /** + * Produces an authorization string for the given set of + * {@link Credentials}. + * + * @param credentials The set of credentials to be used for athentication + * @param request The request being authenticated + * @param context HTTP context + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * + * @return the authorization string + */ + Header authenticate( + Credentials credentials, + HttpRequest request, + HttpContext context) throws AuthenticationException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/Credentials.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/Credentials.java new file mode 100644 index 000000000..2c40ee10e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/Credentials.java @@ -0,0 +1,44 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.security.Principal; + +/** + * This interface represents a set of credentials consisting of a security + * principal and a secret (password) that can be used to establish user + * identity + * + * @since 4.0 + */ +public interface Credentials { + + Principal getUserPrincipal(); + + String getPassword(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/InvalidCredentialsException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/InvalidCredentialsException.java new file mode 100644 index 000000000..47d6e6d91 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/InvalidCredentialsException.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Authentication credentials required to respond to a authentication + * challenge are invalid + * + * + * @since 4.0 + */ +@Immutable +public class InvalidCredentialsException extends AuthenticationException { + + private static final long serialVersionUID = -4834003835215460648L; + + /** + * Creates a new InvalidCredentialsException with a null detail message. + */ + public InvalidCredentialsException() { + super(); + } + + /** + * Creates a new InvalidCredentialsException with the specified message. + * + * @param message the exception detail message + */ + public InvalidCredentialsException(final String message) { + super(message); + } + + /** + * Creates a new InvalidCredentialsException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public InvalidCredentialsException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/MalformedChallengeException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/MalformedChallengeException.java new file mode 100644 index 000000000..309bbfb39 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/MalformedChallengeException.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that authentication challenge is in some way invalid or + * illegal in the given context + * + * + * @since 4.0 + */ +@Immutable +public class MalformedChallengeException extends ProtocolException { + + private static final long serialVersionUID = 814586927989932284L; + + /** + * Creates a new MalformedChallengeException with a null detail message. + */ + public MalformedChallengeException() { + super(); + } + + /** + * Creates a new MalformedChallengeException with the specified message. + * + * @param message the exception detail message + */ + public MalformedChallengeException(final String message) { + super(message); + } + + /** + * Creates a new MalformedChallengeException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public MalformedChallengeException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTCredentials.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTCredentials.java new file mode 100644 index 000000000..43693ffa2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTCredentials.java @@ -0,0 +1,177 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.io.Serializable; +import java.security.Principal; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * {@link Credentials} implementation for Microsoft Windows platforms that includes + * Windows specific attributes such as name of the domain the user belongs to. + * + * @since 4.0 + */ +@Immutable +public class NTCredentials implements Credentials, Serializable { + + private static final long serialVersionUID = -7385699315228907265L; + + /** The user principal */ + private final NTUserPrincipal principal; + + /** Password */ + private final String password; + + /** The host the authentication request is originating from. */ + private final String workstation; + + /** + * The constructor with the fully qualified username and password combined + * string argument. + * + * @param usernamePassword the domain/username:password formed string + */ + public NTCredentials(final String usernamePassword) { + super(); + Args.notNull(usernamePassword, "Username:password string"); + final String username; + final int atColon = usernamePassword.indexOf(':'); + if (atColon >= 0) { + username = usernamePassword.substring(0, atColon); + this.password = usernamePassword.substring(atColon + 1); + } else { + username = usernamePassword; + this.password = null; + } + final int atSlash = username.indexOf('/'); + if (atSlash >= 0) { + this.principal = new NTUserPrincipal( + username.substring(0, atSlash).toUpperCase(Locale.ENGLISH), + username.substring(atSlash + 1)); + } else { + this.principal = new NTUserPrincipal( + null, + username.substring(atSlash + 1)); + } + this.workstation = null; + } + + /** + * Constructor. + * @param userName The user name. This should not include the domain to authenticate with. + * For example: "user" is correct whereas "DOMAIN\\user" is not. + * @param password The password. + * @param workstation The workstation the authentication request is originating from. + * Essentially, the computer name for this machine. + * @param domain The domain to authenticate within. + */ + public NTCredentials( + final String userName, + final String password, + final String workstation, + final String domain) { + super(); + Args.notNull(userName, "User name"); + this.principal = new NTUserPrincipal(domain, userName); + this.password = password; + if (workstation != null) { + this.workstation = workstation.toUpperCase(Locale.ENGLISH); + } else { + this.workstation = null; + } + } + + public Principal getUserPrincipal() { + return this.principal; + } + + public String getUserName() { + return this.principal.getUsername(); + } + + public String getPassword() { + return this.password; + } + + /** + * Retrieves the name to authenticate with. + * + * @return String the domain these credentials are intended to authenticate with. + */ + public String getDomain() { + return this.principal.getDomain(); + } + + /** + * Retrieves the workstation name of the computer originating the request. + * + * @return String the workstation the user is logged into. + */ + public String getWorkstation() { + return this.workstation; + } + + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.principal); + hash = LangUtils.hashCode(hash, this.workstation); + return hash; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o instanceof NTCredentials) { + final NTCredentials that = (NTCredentials) o; + if (LangUtils.equals(this.principal, that.principal) + && LangUtils.equals(this.workstation, that.workstation)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append("[principal: "); + buffer.append(this.principal); + buffer.append("][workstation: "); + buffer.append(this.workstation); + buffer.append("]"); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTUserPrincipal.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTUserPrincipal.java new file mode 100644 index 000000000..13789ef4f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/NTUserPrincipal.java @@ -0,0 +1,113 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.io.Serializable; +import java.security.Principal; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Microsoft Windows specific user principal implementation. + * + * @since 4.0 + */ +@Immutable +public class NTUserPrincipal implements Principal, Serializable { + + private static final long serialVersionUID = -6870169797924406894L; + + private final String username; + private final String domain; + private final String ntname; + + public NTUserPrincipal( + final String domain, + final String username) { + super(); + Args.notNull(username, "User name"); + this.username = username; + if (domain != null) { + this.domain = domain.toUpperCase(Locale.ENGLISH); + } else { + this.domain = null; + } + if (this.domain != null && this.domain.length() > 0) { + final StringBuilder buffer = new StringBuilder(); + buffer.append(this.domain); + buffer.append('\\'); + buffer.append(this.username); + this.ntname = buffer.toString(); + } else { + this.ntname = this.username; + } + } + + public String getName() { + return this.ntname; + } + + public String getDomain() { + return this.domain; + } + + public String getUsername() { + return this.username; + } + + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.username); + hash = LangUtils.hashCode(hash, this.domain); + return hash; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o instanceof NTUserPrincipal) { + final NTUserPrincipal that = (NTUserPrincipal) o; + if (LangUtils.equals(this.username, that.username) + && LangUtils.equals(this.domain, that.domain)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return this.ntname; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/UsernamePasswordCredentials.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/UsernamePasswordCredentials.java new file mode 100644 index 000000000..5cdeeb61e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/UsernamePasswordCredentials.java @@ -0,0 +1,120 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.auth; + +import java.io.Serializable; +import java.security.Principal; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Simple {@link Credentials} implementation based on a user name / password + * pair. + * + * @since 4.0 + */ +@Immutable +public class UsernamePasswordCredentials implements Credentials, Serializable { + + private static final long serialVersionUID = 243343858802739403L; + + private final BasicUserPrincipal principal; + private final String password; + + /** + * The constructor with the username and password combined string argument. + * + * @param usernamePassword the username:password formed string + * @see #toString + */ + public UsernamePasswordCredentials(final String usernamePassword) { + super(); + Args.notNull(usernamePassword, "Username:password string"); + final int atColon = usernamePassword.indexOf(':'); + if (atColon >= 0) { + this.principal = new BasicUserPrincipal(usernamePassword.substring(0, atColon)); + this.password = usernamePassword.substring(atColon + 1); + } else { + this.principal = new BasicUserPrincipal(usernamePassword); + this.password = null; + } + } + + + /** + * The constructor with the username and password arguments. + * + * @param userName the user name + * @param password the password + */ + public UsernamePasswordCredentials(final String userName, final String password) { + super(); + Args.notNull(userName, "Username"); + this.principal = new BasicUserPrincipal(userName); + this.password = password; + } + + public Principal getUserPrincipal() { + return this.principal; + } + + public String getUserName() { + return this.principal.getName(); + } + + public String getPassword() { + return password; + } + + @Override + public int hashCode() { + return this.principal.hashCode(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o instanceof UsernamePasswordCredentials) { + final UsernamePasswordCredentials that = (UsernamePasswordCredentials) o; + if (LangUtils.equals(this.principal, that.principal)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return this.principal.toString(); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/package-info.java new file mode 100644 index 000000000..93992fcbf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client HTTP authentication APIs. + */ +package ch.boye.httpclientandroidlib.auth; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthPNames.java new file mode 100644 index 000000000..ee64850ac --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthPNames.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.auth.params; + +/** + * Parameter names for HTTP authentication classes. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig} + * and constructor parameters of + * {@link ch.boye.httpclientandroidlib.auth.AuthSchemeProvider}s. +*/ +@Deprecated +public interface AuthPNames { + + /** + * Defines the charset to be used when encoding + * {@link ch.boye.httpclientandroidlib.auth.Credentials}. + *

    + * This parameter expects a value of type {@link String}. + */ + public static final String CREDENTIAL_CHARSET = "http.auth.credential-charset"; + + /** + * Defines the order of preference for supported + * {@link ch.boye.httpclientandroidlib.auth.AuthScheme}s when authenticating with + * the target host. + *

    + * This parameter expects a value of type {@link java.util.Collection}. The + * collection is expected to contain {@link String} instances representing + * a name of an authentication scheme as returned by + * {@link ch.boye.httpclientandroidlib.auth.AuthScheme#getSchemeName()}. + */ + public static final String TARGET_AUTH_PREF = "http.auth.target-scheme-pref"; + + /** + * Defines the order of preference for supported + * {@link ch.boye.httpclientandroidlib.auth.AuthScheme}s when authenticating with the + * proxy host. + *

    + * This parameter expects a value of type {@link java.util.Collection}. The + * collection is expected to contain {@link String} instances representing + * a name of an authentication scheme as returned by + * {@link ch.boye.httpclientandroidlib.auth.AuthScheme#getSchemeName()}. + */ + public static final String PROXY_AUTH_PREF = "http.auth.proxy-scheme-pref"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParamBean.java new file mode 100644 index 000000000..e35d7ae28 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParamBean.java @@ -0,0 +1,55 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.auth.params; + +import ch.boye.httpclientandroidlib.params.HttpAbstractParamBean; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate HTTP authentication parameters + * using Java Beans conventions. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig} + * and constructor parameters of + * {@link ch.boye.httpclientandroidlib.auth.AuthSchemeProvider}s. + */ +@Deprecated +public class AuthParamBean extends HttpAbstractParamBean { + + public AuthParamBean (final HttpParams params) { + super(params); + } + + public void setCredentialCharset (final String charset) { + AuthParams.setCredentialCharset(params, charset); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParams.java new file mode 100644 index 000000000..cc7a3d32c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/AuthParams.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.auth.params; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * An adaptor for manipulating HTTP authentication parameters + * in {@link HttpParams}. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig} + * and constructor parameters of + * {@link ch.boye.httpclientandroidlib.auth.AuthSchemeProvider}s. + */ +@Immutable +@Deprecated +public final class AuthParams { + + private AuthParams() { + super(); + } + + /** + * Obtains the charset for encoding + * {@link ch.boye.httpclientandroidlib.auth.Credentials}.If not configured, + * {@link HTTP#DEFAULT_PROTOCOL_CHARSET}is used instead. + * + * @return The charset + */ + public static String getCredentialCharset(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + String charset = (String) params.getParameter + (AuthPNames.CREDENTIAL_CHARSET); + if (charset == null) { + charset = HTTP.DEF_PROTOCOL_CHARSET.name(); + } + return charset; + } + + + /** + * Sets the charset to be used when encoding + * {@link ch.boye.httpclientandroidlib.auth.Credentials}. + * + * @param charset The charset + */ + public static void setCredentialCharset(final HttpParams params, final String charset) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(AuthPNames.CREDENTIAL_CHARSET, charset); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/package-info.java new file mode 100644 index 000000000..7b406b259 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/auth/params/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Deprecated. + * @deprecated (4.3). + */ +package ch.boye.httpclientandroidlib.auth.params; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthCache.java new file mode 100644 index 000000000..3fd0a473d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthCache.java @@ -0,0 +1,49 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.auth.AuthScheme; + +/** + * Abstract {@link AuthScheme} cache. Initialized {@link AuthScheme} objects + * from this cache can be used to preemptively authenticate against known + * hosts. + * + * @since 4.1 + */ +public interface AuthCache { + + void put(HttpHost host, AuthScheme authScheme); + + AuthScheme get(HttpHost host); + + void remove(HttpHost host); + + void clear(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationHandler.java new file mode 100644 index 000000000..24b30b54f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationHandler.java @@ -0,0 +1,101 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import java.util.Map; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** +/** + * A handler for determining if an HTTP response represents an authentication + * challenge that was sent back to the client as a result of authentication + * failure. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link AuthenticationStrategy} + */ +@Deprecated +public interface AuthenticationHandler { + + /** + * Determines if the given HTTP response response represents + * an authentication challenge that was sent back as a result + * of authentication failure + * @param response HTTP response. + * @param context HTTP context. + * @return true if user authentication is required, + * false otherwise. + */ + boolean isAuthenticationRequested( + HttpResponse response, + HttpContext context); + + /** + * Extracts from the given HTTP response a collection of authentication + * challenges, each of which represents an authentication scheme supported + * by the authentication host. + * + * @param response HTTP response. + * @param context HTTP context. + * @return a collection of challenges keyed by names of corresponding + * authentication schemes. + * @throws MalformedChallengeException if one of the authentication + * challenges is not valid or malformed. + */ + Map getChallenges( + HttpResponse response, + HttpContext context) throws MalformedChallengeException; + + /** + * Selects one authentication challenge out of all available and + * creates and generates {@link AuthScheme} instance capable of + * processing that challenge. + * @param challenges collection of challenges. + * @param response HTTP response. + * @param context HTTP context. + * @return authentication scheme to use for authentication. + * @throws AuthenticationException if an authentication scheme + * could not be selected. + */ + AuthScheme selectScheme( + Map challenges, + HttpResponse response, + HttpContext context) throws AuthenticationException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationStrategy.java new file mode 100644 index 000000000..2aa1fb536 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/AuthenticationStrategy.java @@ -0,0 +1,130 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import java.util.Map; +import java.util.Queue; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.auth.AuthOption; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** +/** + * A handler for determining if an HTTP response represents an authentication challenge that was + * sent back to the client as a result of authentication failure. + *

    + * Implementations of this interface must be thread-safe. Access to shared data must be + * synchronized as methods of this interface may be executed from multiple threads. + * + * @since 4.2 + */ +public interface AuthenticationStrategy { + + /** + * Determines if the given HTTP response response represents + * an authentication challenge that was sent back as a result + * of authentication failure. + * + * @param authhost authentication host. + * @param response HTTP response. + * @param context HTTP context. + * @return true if user authentication is required, + * false otherwise. + */ + boolean isAuthenticationRequested( + HttpHost authhost, + HttpResponse response, + HttpContext context); + + /** + * Extracts from the given HTTP response a collection of authentication + * challenges, each of which represents an authentication scheme supported + * by the authentication host. + * + * @param authhost authentication host. + * @param response HTTP response. + * @param context HTTP context. + * @return a collection of challenges keyed by names of corresponding + * authentication schemes. + * @throws MalformedChallengeException if one of the authentication + * challenges is not valid or malformed. + */ + Map getChallenges( + HttpHost authhost, + HttpResponse response, + HttpContext context) throws MalformedChallengeException; + + /** + * Selects one authentication challenge out of all available and + * creates and generates {@link AuthOption} instance capable of + * processing that challenge. + * + * @param challenges collection of challenges. + * @param authhost authentication host. + * @param response HTTP response. + * @param context HTTP context. + * @return authentication auth schemes that can be used for authentication. Can be empty. + * @throws MalformedChallengeException if one of the authentication + * challenges is not valid or malformed. + */ + Queue select( + Map challenges, + HttpHost authhost, + HttpResponse response, + HttpContext context) throws MalformedChallengeException; + + /** + * Callback invoked in case of successful authentication. + * + * @param authhost authentication host. + * @param authScheme authentication scheme used. + * @param context HTTP context. + */ + void authSucceeded( + HttpHost authhost, + AuthScheme authScheme, + HttpContext context); + + /** + * Callback invoked in case of unsuccessful authentication. + * + * @param authhost authentication host. + * @param authScheme authentication scheme used. + * @param context HTTP context. + */ + void authFailed( + HttpHost authhost, + AuthScheme authScheme, + HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/BackoffManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/BackoffManager.java new file mode 100644 index 000000000..afce4f277 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/BackoffManager.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; + +/** + * Represents a controller that dynamically adjusts the size + * of an available connection pool based on feedback from + * using the connections. + * + * @since 4.2 + * + */ +public interface BackoffManager { + + /** + * Called when we have decided that the result of + * using a connection should be interpreted as a + * backoff signal. + */ + public void backOff(HttpRoute route); + + /** + * Called when we have determined that the result of + * using a connection has succeeded and that we may + * probe for more connections. + */ + public void probe(HttpRoute route); +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CircularRedirectException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CircularRedirectException.java new file mode 100644 index 000000000..22d301885 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CircularRedirectException.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals a circular redirect + * + * + * @since 4.0 + */ +@Immutable +public class CircularRedirectException extends RedirectException { + + private static final long serialVersionUID = 6830063487001091803L; + + /** + * Creates a new CircularRedirectException with a null detail message. + */ + public CircularRedirectException() { + super(); + } + + /** + * Creates a new CircularRedirectException with the specified detail message. + * + * @param message The exception detail message + */ + public CircularRedirectException(final String message) { + super(message); + } + + /** + * Creates a new CircularRedirectException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public CircularRedirectException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ClientProtocolException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ClientProtocolException.java new file mode 100644 index 000000000..3b807ae71 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ClientProtocolException.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals an error in the HTTP protocol. + * + * @since 4.0 + */ +@Immutable +public class ClientProtocolException extends IOException { + + private static final long serialVersionUID = -5596590843227115865L; + + public ClientProtocolException() { + super(); + } + + public ClientProtocolException(final String s) { + super(s); + } + + public ClientProtocolException(final Throwable cause) { + initCause(cause); + } + + public ClientProtocolException(final String message, final Throwable cause) { + super(message); + initCause(cause); + } + + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ConnectionBackoffStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ConnectionBackoffStrategy.java new file mode 100644 index 000000000..1f30329cf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ConnectionBackoffStrategy.java @@ -0,0 +1,64 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.HttpResponse; + +/** + * When managing a dynamic number of connections for a given route, this + * strategy assesses whether a given request execution outcome should + * result in a backoff signal or not, based on either examining the + * Throwable that resulted or by examining the resulting + * response (e.g. for its status code). + * + * @since 4.2 + * + */ +public interface ConnectionBackoffStrategy { + + /** + * Determines whether seeing the given Throwable as + * a result of request execution should result in a backoff + * signal. + * @param t the Throwable that happened + * @return true if a backoff signal should be + * given + */ + boolean shouldBackoff(Throwable t); + + /** + * Determines whether receiving the given {@link HttpResponse} as + * a result of request execution should result in a backoff + * signal. Implementations MUST restrict themselves to examining + * the response header and MUST NOT consume any of the response + * body, if any. + * @param resp the HttpResponse that was received + * @return true if a backoff signal should be + * given + */ + boolean shouldBackoff(HttpResponse resp); +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CookieStore.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CookieStore.java new file mode 100644 index 000000000..683109ac4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CookieStore.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import java.util.Date; +import java.util.List; + +import ch.boye.httpclientandroidlib.cookie.Cookie; + +/** + * This interface represents an abstract store for {@link Cookie} + * objects. + * + * @since 4.0 + */ +public interface CookieStore { + + /** + * Adds an {@link Cookie}, replacing any existing equivalent cookies. + * If the given cookie has already expired it will not be added, but existing + * values will still be removed. + * + * @param cookie the {@link Cookie cookie} to be added + */ + void addCookie(Cookie cookie); + + /** + * Returns all cookies contained in this store. + * + * @return all cookies + */ + List getCookies(); + + /** + * Removes all of {@link Cookie}s in this store that have expired by + * the specified {@link java.util.Date}. + * + * @return true if any cookies were purged. + */ + boolean clearExpired(Date date); + + /** + * Clears all cookies. + */ + void clear(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CredentialsProvider.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CredentialsProvider.java new file mode 100644 index 000000000..dc790399c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/CredentialsProvider.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.Credentials; + +/** + * Abstract credentials provider that maintains a collection of user + * credentials. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + */ +public interface CredentialsProvider { + + /** + * Sets the {@link Credentials credentials} for the given authentication + * scope. Any previous credentials for the given scope will be overwritten. + * + * @param authscope the {@link AuthScope authentication scope} + * @param credentials the authentication {@link Credentials credentials} + * for the given scope. + * + * @see #getCredentials(AuthScope) + */ + void setCredentials(AuthScope authscope, Credentials credentials); + + /** + * Get the {@link Credentials credentials} for the given authentication scope. + * + * @param authscope the {@link AuthScope authentication scope} + * @return the credentials + * + * @see #setCredentials(AuthScope, Credentials) + */ + Credentials getCredentials(AuthScope authscope); + + /** + * Clears all credentials. + */ + void clear(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpClient.java new file mode 100644 index 000000000..4cc87fcfe --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpClient.java @@ -0,0 +1,258 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +import java.io.IOException; + +/** + * This interface represents only the most basic contract for HTTP request + * execution. It imposes no restrictions or particular details on the request + * execution process and leaves the specifics of state management, + * authentication and redirect handling up to individual implementations. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +public interface HttpClient { + + + /** + * Obtains the parameters for this client. + * These parameters will become defaults for all requests being + * executed with this client, and for the parameters of + * dependent objects in this client. + * + * @return the default parameters + * + * @deprecated (4.3) use + * {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}. + */ + @Deprecated + HttpParams getParams(); + + /** + * Obtains the connection manager used by this client. + * + * @return the connection manager + * + * @deprecated (4.3) use + * {@link ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder}. + */ + @Deprecated + ClientConnectionManager getConnectionManager(); + + /** + * Executes HTTP request using the default context. + * + * @param request the request to execute + * + * @return the response to the request. This is always a final response, + * never an intermediate response with an 1xx status code. + * Whether redirects or authentication challenges will be returned + * or handled automatically depends on the implementation and + * configuration of this client. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + HttpResponse execute(HttpUriRequest request) + throws IOException, ClientProtocolException; + + /** + * Executes HTTP request using the given context. + * + * @param request the request to execute + * @param context the context to use for the execution, or + * null to use the default context + * + * @return the response to the request. This is always a final response, + * never an intermediate response with an 1xx status code. + * Whether redirects or authentication challenges will be returned + * or handled automatically depends on the implementation and + * configuration of this client. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + HttpResponse execute(HttpUriRequest request, HttpContext context) + throws IOException, ClientProtocolException; + + /** + * Executes HTTP request using the default context. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * + * @return the response to the request. This is always a final response, + * never an intermediate response with an 1xx status code. + * Whether redirects or authentication challenges will be returned + * or handled automatically depends on the implementation and + * configuration of this client. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + HttpResponse execute(HttpHost target, HttpRequest request) + throws IOException, ClientProtocolException; + + /** + * Executes HTTP request using the given context. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param context the context to use for the execution, or + * null to use the default context + * + * @return the response to the request. This is always a final response, + * never an intermediate response with an 1xx status code. + * Whether redirects or authentication challenges will be returned + * or handled automatically depends on the implementation and + * configuration of this client. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + HttpResponse execute(HttpHost target, HttpRequest request, + HttpContext context) + throws IOException, ClientProtocolException; + + /** + * Executes HTTP request using the default context and processes the + * response using the given response handler. + *

    + * Implementing classes are required to ensure that the content entity + * associated with the response is fully consumed and the underlying + * connection is released back to the connection manager automatically + * in all cases relieving individual {@link ResponseHandler}s from + * having to manage resource deallocation internally. + * + * @param request the request to execute + * @param responseHandler the response handler + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + T execute( + HttpUriRequest request, + ResponseHandler responseHandler) + throws IOException, ClientProtocolException; + + /** + * Executes HTTP request using the given context and processes the + * response using the given response handler. + *

    + * Implementing classes are required to ensure that the content entity + * associated with the response is fully consumed and the underlying + * connection is released back to the connection manager automatically + * in all cases relieving individual {@link ResponseHandler}s from + * having to manage resource deallocation internally. + * + * @param request the request to execute + * @param responseHandler the response handler + * @param context the context to use for the execution, or + * null to use the default context + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + T execute( + HttpUriRequest request, + ResponseHandler responseHandler, + HttpContext context) + throws IOException, ClientProtocolException; + + /** + * Executes HTTP request to the target using the default context and + * processes the response using the given response handler. + *

    + * Implementing classes are required to ensure that the content entity + * associated with the response is fully consumed and the underlying + * connection is released back to the connection manager automatically + * in all cases relieving individual {@link ResponseHandler}s from + * having to manage resource deallocation internally. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param responseHandler the response handler + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + T execute( + HttpHost target, + HttpRequest request, + ResponseHandler responseHandler) + throws IOException, ClientProtocolException; + + /** + * Executes HTTP request to the target using the given context and + * processes the response using the given response handler. + *

    + * Implementing classes are required to ensure that the content entity + * associated with the response is fully consumed and the underlying + * connection is released back to the connection manager automatically + * in all cases relieving individual {@link ResponseHandler}s from + * having to manage resource deallocation internally. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param responseHandler the response handler + * @param context the context to use for the execution, or + * null to use the default context + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + T execute( + HttpHost target, + HttpRequest request, + ResponseHandler responseHandler, + HttpContext context) + throws IOException, ClientProtocolException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpRequestRetryHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpRequestRetryHandler.java new file mode 100644 index 000000000..8bbb11c49 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpRequestRetryHandler.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A handler for determining if an HttpRequest should be retried after a + * recoverable exception during execution. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + */ +public interface HttpRequestRetryHandler { + + /** + * Determines if a method should be retried after an IOException + * occurs during execution. + * + * @param exception the exception that occurred + * @param executionCount the number of times this method has been + * unsuccessfully executed + * @param context the context for the request execution + * + * @return true if the method should be retried, false + * otherwise + */ + boolean retryRequest(IOException exception, int executionCount, HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpResponseException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpResponseException.java new file mode 100644 index 000000000..68500c6ac --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/HttpResponseException.java @@ -0,0 +1,52 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals a non 2xx HTTP response. + * + * @since 4.0 + */ +@Immutable +public class HttpResponseException extends ClientProtocolException { + + private static final long serialVersionUID = -7186627969477257933L; + + private final int statusCode; + + public HttpResponseException(final int statusCode, final String s) { + super(s); + this.statusCode = statusCode; + } + + public int getStatusCode() { + return this.statusCode; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/NonRepeatableRequestException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/NonRepeatableRequestException.java new file mode 100644 index 000000000..6d9dce208 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/NonRepeatableRequestException.java @@ -0,0 +1,72 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals failure to retry the request due to non-repeatable request + * entity. + * + * + * @since 4.0 + */ +@Immutable +public class NonRepeatableRequestException extends ProtocolException { + + private static final long serialVersionUID = 82685265288806048L; + + /** + * Creates a new NonRepeatableEntityException with a null detail message. + */ + public NonRepeatableRequestException() { + super(); + } + + /** + * Creates a new NonRepeatableEntityException with the specified detail message. + * + * @param message The exception detail message + */ + public NonRepeatableRequestException(final String message) { + super(message); + } + + /** + * Creates a new NonRepeatableEntityException with the specified detail message. + * + * @param message The exception detail message + * @param cause the cause + */ + public NonRepeatableRequestException(final String message, final Throwable cause) { + super(message, cause); + } + + + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectException.java new file mode 100644 index 000000000..e187db789 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectException.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals violation of HTTP specification caused by an invalid redirect + * + * + * @since 4.0 + */ +@Immutable +public class RedirectException extends ProtocolException { + + private static final long serialVersionUID = 4418824536372559326L; + + /** + * Creates a new RedirectException with a null detail message. + */ + public RedirectException() { + super(); + } + + /** + * Creates a new RedirectException with the specified detail message. + * + * @param message The exception detail message + */ + public RedirectException(final String message) { + super(message); + } + + /** + * Creates a new RedirectException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public RedirectException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectHandler.java new file mode 100644 index 000000000..f2dcd842b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectHandler.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A handler for determining if an HTTP request should be redirected to + * a new location in response to an HTTP response received from the target + * server. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + * + * @deprecated (4.1) use {@link RedirectStrategy} + */ +@Deprecated +public interface RedirectHandler { + + /** + * Determines if a request should be redirected to a new location + * given the response from the target server. + * + * @param response the response received from the target server + * @param context the context for the request execution + * + * @return true if the request should be redirected, false + * otherwise + */ + boolean isRedirectRequested(HttpResponse response, HttpContext context); + + /** + * Determines the location request is expected to be redirected to + * given the response from the target server and the current request + * execution context. + * + * @param response the response received from the target server + * @param context the context for the request execution + * + * @return redirect URI + */ + URI getLocationURI(HttpResponse response, HttpContext context) + throws ProtocolException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectStrategy.java new file mode 100644 index 000000000..ad2499c37 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RedirectStrategy.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A strategy for determining if an HTTP request should be redirected to + * a new location in response to an HTTP response received from the target + * server. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.1 + */ +public interface RedirectStrategy { + + /** + * Determines if a request should be redirected to a new location + * given the response from the target server. + * + * @param request the executed request + * @param response the response received from the target server + * @param context the context for the request execution + * + * @return true if the request should be redirected, false + * otherwise + */ + boolean isRedirected( + HttpRequest request, + HttpResponse response, + HttpContext context) throws ProtocolException; + + /** + * Determines the redirect location given the response from the target + * server and the current request execution context and generates a new + * request to be sent to the location. + * + * @param request the executed request + * @param response the response received from the target server + * @param context the context for the request execution + * + * @return redirected request + */ + HttpUriRequest getRedirect( + HttpRequest request, + HttpResponse response, + HttpContext context) throws ProtocolException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RequestDirector.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RequestDirector.java new file mode 100644 index 000000000..ebaeb74a0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/RequestDirector.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A client-side request director. + * The director decides which steps are necessary to execute a request. + * It establishes connections and optionally processes redirects and + * authentication challenges. The director may therefore generate and + * send a sequence of requests in order to execute one initial request. + * + * @since 4.0 + * + * @deprecated (4.3) No longer used + */ +@Deprecated +public interface RequestDirector { + + + /** + * Executes a request. + *
    Note: + * For the time being, a new director is instantiated for each request. + * This is the same behavior as for HttpMethodDirector + * in HttpClient 3. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param context the context for executing the request + * + * @return the final response to the request. + * This is never an intermediate response with status code 1xx. + * + * @throws HttpException in case of a problem + * @throws IOException in case of an IO problem + * or if the connection was aborted + */ + HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) + throws HttpException, IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ResponseHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ResponseHandler.java new file mode 100644 index 000000000..6fa02b650 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ResponseHandler.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpResponse; + +/** + * Handler that encapsulates the process of generating a response object + * from a {@link HttpResponse}. + * + * + * @since 4.0 + */ +public interface ResponseHandler { + + /** + * Processes an {@link HttpResponse} and returns some value + * corresponding to that response. + * + * @param response The response to process + * @return A value determined by the response + * + * @throws ClientProtocolException in case of an http protocol error + * @throws IOException in case of a problem or the connection was aborted + */ + T handleResponse(HttpResponse response) throws ClientProtocolException, IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ServiceUnavailableRetryStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ServiceUnavailableRetryStrategy.java new file mode 100644 index 000000000..7a08dab65 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/ServiceUnavailableRetryStrategy.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Strategy interface that allows API users to plug in their own logic to + * control whether or not a retry should automatically be done, how many times + * it should be retried and so on. + * + * @since 4.2 + */ +public interface ServiceUnavailableRetryStrategy { + + /** + * Determines if a method should be retried given the response from the target server. + * + * @param response the response from the target server + * @param executionCount the number of times this method has been + * unsuccessfully executed + * @param context the context for the request execution + + * @return true if the method should be retried, false + * otherwise + */ + boolean retryRequest(HttpResponse response, int executionCount, HttpContext context); + + /** + * @return The interval between the subsequent auto-retries. + */ + long getRetryInterval(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/UserTokenHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/UserTokenHandler.java new file mode 100644 index 000000000..1e15ef7c6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/UserTokenHandler.java @@ -0,0 +1,58 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A handler for determining if the given execution context is user specific + * or not. The token object returned by this handler is expected to uniquely + * identify the current user if the context is user specific or to be + * null if the context does not contain any resources or details + * specific to the current user. + *

    + * The user token will be used to ensure that user specific resources will not + * be shared with or reused by other users. + * + * @since 4.0 + */ +public interface UserTokenHandler { + + /** + * The token object returned by this method is expected to uniquely + * identify the current user if the context is user specific or to be + * null if it is not. + * + * @param context the execution context + * + * @return user token that uniquely identifies the user or + * null if the context is not user specific. + */ + Object getUserToken(HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/CacheResponseStatus.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/CacheResponseStatus.java new file mode 100644 index 000000000..d000839af --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/CacheResponseStatus.java @@ -0,0 +1,55 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +/** + * This enumeration represents the various ways a response can be generated + * by the {@link ch.boye.httpclientandroidlib.impl.client.cache.CachingHttpClient}; + * if a request is executed with an {@link ch.boye.httpclientandroidlib.protocol.HttpContext} + * then a parameter with one of these values will be registered in the + * context under the key + * {@link ch.boye.httpclientandroidlib.impl.client.cache.CachingHttpClient#CACHE_RESPONSE_STATUS}. + */ +public enum CacheResponseStatus { + + /** The response was generated directly by the caching module. */ + CACHE_MODULE_RESPONSE, + + /** A response was generated from the cache with no requests sent + * upstream. + */ + CACHE_HIT, + + /** The response came from an upstream server. */ + CACHE_MISS, + + /** The response was generated from the cache after validating the + * entry with the origin server. + */ + VALIDATED; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HeaderConstants.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HeaderConstants.java new file mode 100644 index 000000000..22f34dcff --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HeaderConstants.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Records static constants for various HTTP header names. + * @since 4.1 + */ +@Immutable +public class HeaderConstants { + + public static final String GET_METHOD = "GET"; + public static final String HEAD_METHOD = "HEAD"; + public static final String OPTIONS_METHOD = "OPTIONS"; + public static final String PUT_METHOD = "PUT"; + public static final String DELETE_METHOD = "DELETE"; + public static final String TRACE_METHOD = "TRACE"; + + public static final String LAST_MODIFIED = "Last-Modified"; + public static final String IF_MATCH = "If-Match"; + public static final String IF_RANGE = "If-Range"; + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + public static final String IF_NONE_MATCH = "If-None-Match"; + + public static final String PRAGMA = "Pragma"; + public static final String MAX_FORWARDS = "Max-Forwards"; + public static final String ETAG = "ETag"; + public static final String EXPIRES = "Expires"; + public static final String AGE = "Age"; + public static final String VARY = "Vary"; + public static final String ALLOW = "Allow"; + public static final String VIA = "Via"; + public static final String PUBLIC = "public"; + public static final String PRIVATE = "private"; + + public static final String CACHE_CONTROL = "Cache-Control"; + public static final String CACHE_CONTROL_NO_STORE = "no-store"; + public static final String CACHE_CONTROL_NO_CACHE = "no-cache"; + public static final String CACHE_CONTROL_MAX_AGE = "max-age"; + public static final String CACHE_CONTROL_MAX_STALE = "max-stale"; + public static final String CACHE_CONTROL_MIN_FRESH = "min-fresh"; + public static final String CACHE_CONTROL_MUST_REVALIDATE = "must-revalidate"; + public static final String CACHE_CONTROL_PROXY_REVALIDATE = "proxy-revalidate"; + public static final String STALE_IF_ERROR = "stale-if-error"; + public static final String STALE_WHILE_REVALIDATE = "stale-while-revalidate"; + + public static final String WARNING = "Warning"; + public static final String RANGE = "Range"; + public static final String CONTENT_RANGE = "Content-Range"; + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + public static final String AUTHORIZATION = "Authorization"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheContext.java new file mode 100644 index 000000000..54edfa12a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheContext.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * @since 4.3 + */ +@NotThreadSafe +public class HttpCacheContext extends HttpClientContext { + + /** + * This is the name under which the {@link CacheResponseStatus} of a request + * (for example, whether it resulted in a cache hit) will be recorded if an + * {@link HttpContext} is provided during execution. + */ + public static final String CACHE_RESPONSE_STATUS = "http.cache.response.status"; + + public static HttpCacheContext adapt(final HttpContext context) { + if (context instanceof HttpCacheContext) { + return (HttpCacheContext) context; + } else { + return new HttpCacheContext(context); + } + } + + public static HttpCacheContext create() { + return new HttpCacheContext(new BasicHttpContext()); + } + + public HttpCacheContext(final HttpContext context) { + super(context); + } + + public HttpCacheContext() { + super(); + } + + public CacheResponseStatus getCacheResponseStatus() { + return getAttribute(CACHE_RESPONSE_STATUS, CacheResponseStatus.class); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntry.java new file mode 100644 index 000000000..d03786279 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntry.java @@ -0,0 +1,263 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.message.HeaderGroup; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Structure used to store an {@link ch.boye.httpclientandroidlib.HttpResponse} in a cache. + * Some entries can optionally depend on system resources that may require + * explicit deallocation. In such a case {@link #getResource()} should return + * a non null instance of {@link Resource} that must be deallocated by calling + * {@link Resource#dispose()} method when no longer used. + * + * @since 4.1 + */ +@Immutable +public class HttpCacheEntry implements Serializable { + + private static final long serialVersionUID = -6300496422359477413L; + + private final Date requestDate; + private final Date responseDate; + private final StatusLine statusLine; + private final HeaderGroup responseHeaders; + private final Resource resource; + private final Map variantMap; + private final Date date; + + /** + * Create a new {@link HttpCacheEntry} with variants. + * @param requestDate + * Date/time when the request was made (Used for age + * calculations) + * @param responseDate + * Date/time that the response came back (Used for age + * calculations) + * @param statusLine + * HTTP status line from origin response + * @param responseHeaders + * Header[] from original HTTP Response + * @param resource representing origin response body + * @param variantMap describing cache entries that are variants + * of this parent entry; this maps a "variant key" (derived + * from the varying request headers) to a "cache key" (where + * in the cache storage the particular variant is located) + */ + public HttpCacheEntry( + final Date requestDate, + final Date responseDate, + final StatusLine statusLine, + final Header[] responseHeaders, + final Resource resource, + final Map variantMap) { + super(); + Args.notNull(requestDate, "Request date"); + Args.notNull(responseDate, "Response date"); + Args.notNull(statusLine, "Status line"); + Args.notNull(responseHeaders, "Response headers"); + this.requestDate = requestDate; + this.responseDate = responseDate; + this.statusLine = statusLine; + this.responseHeaders = new HeaderGroup(); + this.responseHeaders.setHeaders(responseHeaders); + this.resource = resource; + this.variantMap = variantMap != null + ? new HashMap(variantMap) + : null; + this.date = parseDate(); + } + + /** + * Create a new {@link HttpCacheEntry}. + * + * @param requestDate + * Date/time when the request was made (Used for age + * calculations) + * @param responseDate + * Date/time that the response came back (Used for age + * calculations) + * @param statusLine + * HTTP status line from origin response + * @param responseHeaders + * Header[] from original HTTP Response + * @param resource representing origin response body + */ + public HttpCacheEntry(final Date requestDate, final Date responseDate, final StatusLine statusLine, + final Header[] responseHeaders, final Resource resource) { + this(requestDate, responseDate, statusLine, responseHeaders, resource, + new HashMap()); + } + + /** + * Find the "Date" response header and parse it into a java.util.Date + * @return the Date value of the header or null if the header is not present + */ + private Date parseDate() { + final Header dateHdr = getFirstHeader(HTTP.DATE_HEADER); + if (dateHdr == null) { + return null; + } + return DateUtils.parseDate(dateHdr.getValue()); + } + + /** + * Returns the {@link StatusLine} from the origin + * {@link ch.boye.httpclientandroidlib.HttpResponse}. + */ + public StatusLine getStatusLine() { + return this.statusLine; + } + + /** + * Returns the {@link ProtocolVersion} from the origin + * {@link ch.boye.httpclientandroidlib.HttpResponse}. + */ + public ProtocolVersion getProtocolVersion() { + return this.statusLine.getProtocolVersion(); + } + + /** + * Gets the reason phrase from the origin + * {@link ch.boye.httpclientandroidlib.HttpResponse}, for example, "Not Modified". + */ + public String getReasonPhrase() { + return this.statusLine.getReasonPhrase(); + } + + /** + * Returns the HTTP response code from the origin + * {@link ch.boye.httpclientandroidlib.HttpResponse}. + */ + public int getStatusCode() { + return this.statusLine.getStatusCode(); + } + + /** + * Returns the time the associated origin request was initiated by the + * caching module. + * @return {@link Date} + */ + public Date getRequestDate() { + return requestDate; + } + + /** + * Returns the time the origin response was received by the caching module. + * @return {@link Date} + */ + public Date getResponseDate() { + return responseDate; + } + + /** + * Returns all the headers that were on the origin response. + */ + public Header[] getAllHeaders() { + return responseHeaders.getAllHeaders(); + } + + /** + * Returns the first header from the origin response with the given + * name. + */ + public Header getFirstHeader(final String name) { + return responseHeaders.getFirstHeader(name); + } + + /** + * Gets all the headers with the given name that were on the origin + * response. + */ + public Header[] getHeaders(final String name) { + return responseHeaders.getHeaders(name); + } + + /** + * Gets the Date value of the "Date" header or null if the header is missing or cannot be + * parsed. + * + * @since 4.3 + */ + public Date getDate() { + return date; + } + + /** + * Returns the {@link Resource} containing the origin response body. + */ + public Resource getResource() { + return this.resource; + } + + /** + * Indicates whether the origin response indicated the associated + * resource had variants (i.e. that the Vary header was set on the + * origin response). + * @return {@code true} if this cached response was a variant + */ + public boolean hasVariants() { + return getFirstHeader(HeaderConstants.VARY) != null; + } + + /** + * Returns an index about where in the cache different variants for + * a given resource are stored. This maps "variant keys" to "cache keys", + * where the variant key is derived from the varying request headers, + * and the cache key is the location in the + * {@link ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage} where that + * particular variant is stored. The first variant returned is used as + * the "parent" entry to hold this index of the other variants. + */ + public Map getVariantMap() { + return Collections.unmodifiableMap(variantMap); + } + + /** + * Provides a string representation of this instance suitable for + * human consumption. + */ + @Override + public String toString() { + return "[request date=" + this.requestDate + "; response date=" + this.responseDate + + "; statusLine=" + this.statusLine + "]"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializationException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializationException.java new file mode 100644 index 000000000..e74f64db6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializationException.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import java.io.IOException; + +/** + * Thrown if serialization or deserialization of an {@link HttpCacheEntry} + * fails. + */ +public class HttpCacheEntrySerializationException extends IOException { + + private static final long serialVersionUID = 9219188365878433519L; + + public HttpCacheEntrySerializationException(final String message) { + super(); + } + + public HttpCacheEntrySerializationException(final String message, final Throwable cause) { + super(message); + initCause(cause); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializer.java new file mode 100644 index 000000000..2903fb74d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheEntrySerializer.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Used by some {@link HttpCacheStorage} implementations to serialize + * {@link HttpCacheEntry} instances to a byte representation before + * storage. + */ +public interface HttpCacheEntrySerializer { + + /** + * Serializes the given entry to a byte representation on the + * given {@link OutputStream}. + * @throws IOException + */ + void writeTo(HttpCacheEntry entry, OutputStream os) throws IOException; + + /** + * Deserializes a byte representation of a cache entry by reading + * from the given {@link InputStream}. + * @throws IOException + */ + HttpCacheEntry readFrom(InputStream is) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheInvalidator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheInvalidator.java new file mode 100644 index 000000000..f8e70968b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheInvalidator.java @@ -0,0 +1,58 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; + +/** + * Given a particular HttpRequest, flush any cache entries that this request + * would invalidate. + * + * @since 4.3 + */ +public interface HttpCacheInvalidator { + + /** + * Remove cache entries from the cache that are no longer fresh or have been + * invalidated in some way. + * + * @param host + * The backend host we are talking to + * @param req + * The HttpRequest to that host + */ + void flushInvalidatedCacheEntries(HttpHost host, HttpRequest req); + + /** + * Flushes entries that were invalidated by the given response received for + * the given host/request pair. + */ + void flushInvalidatedCacheEntries(HttpHost host, HttpRequest request, HttpResponse response); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheStorage.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheStorage.java new file mode 100644 index 000000000..b0759d195 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheStorage.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import java.io.IOException; + +/** + * New storage backends should implement this {@link HttpCacheStorage} + * interface. They can then be plugged into the existing + * {@link ch.boye.httpclientandroidlib.impl.client.cache.CachingHttpClient} + * implementation. + * + * @since 4.1 + */ +public interface HttpCacheStorage { + + /** + * Store a given cache entry under the given key. + * @param key where in the cache to store the entry + * @param entry cached response to store + * @throws IOException + */ + void putEntry(String key, HttpCacheEntry entry) throws IOException; + + /** + * Retrieves the cache entry stored under the given key + * or null if no entry exists under that key. + * @param key cache key + * @return an {@link HttpCacheEntry} or {@code null} if no + * entry exists + * @throws IOException + */ + HttpCacheEntry getEntry(String key) throws IOException; + + /** + * Deletes/invalidates/removes any cache entries currently + * stored under the given key. + * @param key + * @throws IOException + */ + void removeEntry(String key) throws IOException; + + /** + * Atomically applies the given callback to update an existing cache + * entry under a given key. + * @param key indicates which entry to modify + * @param callback performs the update; see + * {@link HttpCacheUpdateCallback} for details, but roughly the + * callback expects to be handed the current entry and will return + * the new value for the entry. + * @throws IOException + * @throws HttpCacheUpdateException + */ + void updateEntry( + String key, HttpCacheUpdateCallback callback) throws IOException, HttpCacheUpdateException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateCallback.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateCallback.java new file mode 100644 index 000000000..abc810fe8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateCallback.java @@ -0,0 +1,52 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import java.io.IOException; + +/** + * Used for atomically updating entries in a {@link HttpCacheStorage} + * implementation. The current entry (if any) is fed into an implementation + * of this interface, and the new, possibly updated entry (if any) + * should be returned. + */ +public interface HttpCacheUpdateCallback { + + /** + * Returns the new cache entry that should replace an existing one. + * + * @param existing + * the cache entry currently in-place in the cache, possibly + * null if nonexistent + * @return the cache entry that should replace it, again, + * possibly null if the entry should be deleted + * + * @since 4.1 + */ + HttpCacheEntry update(HttpCacheEntry existing) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateException.java new file mode 100644 index 000000000..278fe3afb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/HttpCacheUpdateException.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +/** + * Signals that {@link HttpCacheStorage} encountered an error performing an + * update operation. + * + * @since 4.1 + */ +public class HttpCacheUpdateException extends Exception { + + private static final long serialVersionUID = 823573584868632876L; + + public HttpCacheUpdateException(final String message) { + super(message); + } + + public HttpCacheUpdateException(final String message, final Throwable cause) { + super(message); + initCause(cause); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/InputLimit.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/InputLimit.java new file mode 100644 index 000000000..45ef3b26d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/InputLimit.java @@ -0,0 +1,76 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * Used to limiting the size of an incoming response body of + * unknown size that is optimistically being read in anticipation + * of caching it. + * @since 4.1 + */ +@NotThreadSafe // reached +public class InputLimit { + + private final long value; + private boolean reached; + + /** + * Create a limit for how many bytes of a response body to + * read. + * @param value maximum length in bytes + */ + public InputLimit(final long value) { + super(); + this.value = value; + this.reached = false; + } + + /** + * Returns the current maximum limit that was set on + * creation. + */ + public long getValue() { + return this.value; + } + + /** + * Used to report that the limit has been reached. + */ + public void reached() { + this.reached = true; + } + + /** + * Returns {@code true} if the input limit has been reached. + */ + public boolean isReached() { + return this.reached; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/Resource.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/Resource.java new file mode 100644 index 000000000..86811e85c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/Resource.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; + +/** + * Represents a disposable system resource used for handling + * cached response bodies. + * + * @since 4.1 + */ +public interface Resource extends Serializable { + + /** + * Returns an {@link InputStream} from which the response + * body can be read. + * @throws IOException + */ + InputStream getInputStream() throws IOException; + + /** + * Returns the length in bytes of the response body. + */ + long length(); + + /** + * Indicates the system no longer needs to keep this + * response body and any system resources associated with + * it may be reclaimed. + */ + void dispose(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/ResourceFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/ResourceFactory.java new file mode 100644 index 000000000..0583d2605 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/ResourceFactory.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.cache; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Generates {@link Resource} instances for handling cached + * HTTP response bodies. + * + * @since 4.1 + */ +public interface ResourceFactory { + + /** + * Creates a {@link Resource} from a given response body. + * @param requestId a unique identifier for this particular + * response body + * @param instream the original {@link InputStream} + * containing the response body of the origin HTTP response. + * @param limit maximum number of bytes to consume of the + * response body; if this limit is reached before the + * response body is fully consumed, mark the limit has + * having been reached and return a {@code Resource} + * containing the data read to that point. + * @return a {@code Resource} containing however much of + * the response body was successfully read. + * @throws IOException + */ + Resource generate(String requestId, InputStream instream, InputLimit limit) throws IOException; + + /** + * Clones an existing {@link Resource}. + * @param requestId unique identifier provided to associate + * with the cloned response body. + * @param resource the original response body to clone. + * @return the {@code Resource} copy + * @throws IOException + */ + Resource copy(String requestId, Resource resource) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/package.html b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/package.html new file mode 100644 index 000000000..58a1e3ff3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/cache/package.html @@ -0,0 +1,78 @@ + + + + + + + +

    +This package consists largely of constants and interfaces that are +necessary for building new storage backends for the +{@link org.apache.http.impl.client.cache.CachingHttpClient} or for +those clients wanting to get a little more behavioral information +out of the cache module (for example, whether a particular response +was a cache hit or not). Developers that simply want to instantiate +and make use of the caching module will be better off looking at +the {@code CachingHttpClient} documentation itself. +

    +

    +The classes in this package can be divided into two main groups: +reference constants and interfaces needed for storage backends. In +the former group, +{@link org.apache.http.client.cache.HeaderConstants} contains a list +of HTTP header names encoded as static fields, and the +{@link org.apache.http.client.cache.CacheResponseStatus} enumeration +values are set in an {@link org.apache.http.protocol.HttpContext} by +the {@code CachingHttpClient} to indicate how the request was +processed by the caching module itself. +

    +

    +New storage backends will need to implement the +{@link org.apache.http.client.cache.HttpCacheStorage} +interface; they can then be passed to one of the {@code CachingHttpClient} +constructors, which will happily make use of the new storage mechanism. +The {@link org.apache.http.client.cache.HttpCacheEntry} class shows the +datastructure for a cache entry that must be stored by the +{@code HttpCacheStorage}. +There is, in addition, the notion of a +{@link org.apache.http.client.cache.Resource} and an associated +{@link org.apache.http.client.cache.ResourceFactory}, which are used for +managing the handling of cached response bodies. The default implementation +used by the {@code CachingHttpClient} stores response bodies in memory; +alternative implementations might involve storing these in a filesystem. A new +{@code ResourceFactory} can be provided along with a {@code HttpCacheStorage} +in one of the constructors to the {@code CachingHttpClient}. Finally, some +of the additional storage backends we provide, like the +{@link org.apache.http.impl.client.cache.ehcache.EhcacheHttpCacheStorage} and +{@link org.apache.http.impl.client.cache.memcached.MemcachedHttpCacheStorage}, +can be provided with different serializers for the cache entry metadata; +developers wanting to experiment with different serialization techniques +should implement the +{@link org.apache.http.client.cache.HttpCacheEntrySerializer} interface. +

    + + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/AuthSchemes.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/AuthSchemes.java new file mode 100644 index 000000000..a8b4db5e0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/AuthSchemes.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.config; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Standard authentication schemes supported by HttpClient. + * + * @since 4.3 + */ +@Immutable +public final class AuthSchemes { + + /** + * Basic authentication scheme as defined in RFC2617 (considered inherently + * insecure, but most widely supported) + */ + public static final String BASIC = "Basic"; + + /** + * Digest authentication scheme as defined in RFC2617. + */ + public static final String DIGEST = "Digest"; + + /** + * The NTLM scheme is a proprietary Microsoft Windows Authentication + * protocol (considered to be the most secure among currently supported + * authentication schemes). + */ + public static final String NTLM = "NTLM"; + + /** + * SPNEGO Authentication scheme. + */ + public static final String SPNEGO = "negotiate"; + + /** + * Kerberos Authentication scheme. + */ + public static final String KERBEROS = "Kerberos"; + + private AuthSchemes() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/CookieSpecs.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/CookieSpecs.java new file mode 100644 index 000000000..132c6606f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/CookieSpecs.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.config; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Standard cookie specifications supported by HttpClient. + * + * @since 4.3 + */ +@Immutable +public final class CookieSpecs { + + /** + * The policy that provides high degree of compatibility + * with common cookie management of popular HTTP agents. + */ + public static final String BROWSER_COMPATIBILITY = "compatibility"; + + /** + * The Netscape cookie draft compliant policy. + */ + public static final String NETSCAPE = "netscape"; + + /** + * The RFC 2965 compliant policy (standard). + */ + public static final String STANDARD = "standard"; + + /** + * The default 'best match' policy. + */ + public static final String BEST_MATCH = "best-match"; + + /** + * The policy that ignores cookies. + */ + public static final String IGNORE_COOKIES = "ignoreCookies"; + + private CookieSpecs() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/RequestConfig.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/RequestConfig.java new file mode 100644 index 000000000..e861f5e68 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/RequestConfig.java @@ -0,0 +1,442 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.config; + +import java.net.InetAddress; +import java.util.Collection; + +import ch.boye.httpclientandroidlib.HttpHost; + +public class RequestConfig implements Cloneable { + + public static final RequestConfig DEFAULT = new Builder().build(); + + private final boolean expectContinueEnabled; + private final HttpHost proxy; + private final InetAddress localAddress; + private final boolean staleConnectionCheckEnabled; + private final String cookieSpec; + private final boolean redirectsEnabled; + private final boolean relativeRedirectsAllowed; + private final boolean circularRedirectsAllowed; + private final int maxRedirects; + private final boolean authenticationEnabled; + private final Collection targetPreferredAuthSchemes; + private final Collection proxyPreferredAuthSchemes; + private final int connectionRequestTimeout; + private final int connectTimeout; + private final int socketTimeout; + + RequestConfig( + final boolean expectContinueEnabled, + final HttpHost proxy, + final InetAddress localAddress, + final boolean staleConnectionCheckEnabled, + final String cookieSpec, + final boolean redirectsEnabled, + final boolean relativeRedirectsAllowed, + final boolean circularRedirectsAllowed, + final int maxRedirects, + final boolean authenticationEnabled, + final Collection targetPreferredAuthSchemes, + final Collection proxyPreferredAuthSchemes, + final int connectionRequestTimeout, + final int connectTimeout, + final int socketTimeout) { + super(); + this.expectContinueEnabled = expectContinueEnabled; + this.proxy = proxy; + this.localAddress = localAddress; + this.staleConnectionCheckEnabled = staleConnectionCheckEnabled; + this.cookieSpec = cookieSpec; + this.redirectsEnabled = redirectsEnabled; + this.relativeRedirectsAllowed = relativeRedirectsAllowed; + this.circularRedirectsAllowed = circularRedirectsAllowed; + this.maxRedirects = maxRedirects; + this.authenticationEnabled = authenticationEnabled; + this.targetPreferredAuthSchemes = targetPreferredAuthSchemes; + this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes; + this.connectionRequestTimeout = connectionRequestTimeout; + this.connectTimeout = connectTimeout; + this.socketTimeout = socketTimeout; + } + + /** + * Determines whether the 'Expect: 100-Continue' handshake is enabled + * for entity enclosing methods. The purpose of the 'Expect: 100-Continue' + * handshake is to allow a client that is sending a request message with + * a request body to determine if the origin server is willing to + * accept the request (based on the request headers) before the client + * sends the request body. + *

    + * The use of the 'Expect: 100-continue' handshake can result in + * a noticeable performance improvement for entity enclosing requests + * (such as POST and PUT) that require the target server's + * authentication. + *

    + * 'Expect: 100-continue' handshake should be used with caution, as it + * may cause problems with HTTP servers and proxies that do not support + * HTTP/1.1 protocol. + *

    + * Default: false + */ + public boolean isExpectContinueEnabled() { + return expectContinueEnabled; + } + + /** + * Returns HTTP proxy to be used for request execution. + *

    + * Default: null + */ + public HttpHost getProxy() { + return proxy; + } + + /** + * Returns local address to be used for request execution. + *

    + * On machines with multiple network interfaces, this parameter + * can be used to select the network interface from which the + * connection originates. + *

    + * Default: null + */ + public InetAddress getLocalAddress() { + return localAddress; + } + + /** + * Determines whether stale connection check is to be used. The stale + * connection check can cause up to 30 millisecond overhead per request and + * should be used only when appropriate. For performance critical + * operations this check should be disabled. + *

    + * Default: true + */ + public boolean isStaleConnectionCheckEnabled() { + return staleConnectionCheckEnabled; + } + + /** + * Determines the name of the cookie specification to be used for HTTP state + * management. + *

    + * Default: null + */ + public String getCookieSpec() { + return cookieSpec; + } + + /** + * Determines whether redirects should be handled automatically. + *

    + * Default: true + */ + public boolean isRedirectsEnabled() { + return redirectsEnabled; + } + + /** + * Determines whether relative redirects should be rejected. HTTP specification + * requires the location value be an absolute URI. + *

    + * Default: true + */ + public boolean isRelativeRedirectsAllowed() { + return relativeRedirectsAllowed; + } + + /** + * Determines whether circular redirects (redirects to the same location) should + * be allowed. The HTTP spec is not sufficiently clear whether circular redirects + * are permitted, therefore optionally they can be enabled + *

    + * Default: false + */ + public boolean isCircularRedirectsAllowed() { + return circularRedirectsAllowed; + } + + /** + * Returns the maximum number of redirects to be followed. The limit on number + * of redirects is intended to prevent infinite loops. + *

    + * Default: 50 + */ + public int getMaxRedirects() { + return maxRedirects; + } + + /** + * Determines whether authentication should be handled automatically. + *

    + * Default: true + */ + public boolean isAuthenticationEnabled() { + return authenticationEnabled; + } + + /** + * Determines the order of preference for supported authentication schemes + * when authenticating with the target host. + *

    + * Default: null + */ + public Collection getTargetPreferredAuthSchemes() { + return targetPreferredAuthSchemes; + } + + /** + * Determines the order of preference for supported authentication schemes + * when authenticating with the proxy host. + *

    + * Default: null + */ + public Collection getProxyPreferredAuthSchemes() { + return proxyPreferredAuthSchemes; + } + + /** + * Returns the timeout in milliseconds used when requesting a connection + * from the connection manager. A timeout value of zero is interpreted + * as an infinite timeout. + *

    + * A timeout value of zero is interpreted as an infinite timeout. + * A negative value is interpreted as undefined (system default). + *

    + * Default: -1 + */ + public int getConnectionRequestTimeout() { + return connectionRequestTimeout; + } + + /** + * Determines the timeout in milliseconds until a connection is established. + * A timeout value of zero is interpreted as an infinite timeout. + *

    + * A timeout value of zero is interpreted as an infinite timeout. + * A negative value is interpreted as undefined (system default). + *

    + * Default: -1 + */ + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * Defines the socket timeout (SO_TIMEOUT) in milliseconds, + * which is the timeout for waiting for data or, put differently, + * a maximum period inactivity between two consecutive data packets). + *

    + * A timeout value of zero is interpreted as an infinite timeout. + * A negative value is interpreted as undefined (system default). + *

    + * Default: -1 + */ + public int getSocketTimeout() { + return socketTimeout; + } + + @Override + protected RequestConfig clone() throws CloneNotSupportedException { + return (RequestConfig) super.clone(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(", expectContinueEnabled=").append(expectContinueEnabled); + builder.append(", proxy=").append(proxy); + builder.append(", localAddress=").append(localAddress); + builder.append(", staleConnectionCheckEnabled=").append(staleConnectionCheckEnabled); + builder.append(", cookieSpec=").append(cookieSpec); + builder.append(", redirectsEnabled=").append(redirectsEnabled); + builder.append(", relativeRedirectsAllowed=").append(relativeRedirectsAllowed); + builder.append(", maxRedirects=").append(maxRedirects); + builder.append(", circularRedirectsAllowed=").append(circularRedirectsAllowed); + builder.append(", authenticationEnabled=").append(authenticationEnabled); + builder.append(", targetPreferredAuthSchemes=").append(targetPreferredAuthSchemes); + builder.append(", proxyPreferredAuthSchemes=").append(proxyPreferredAuthSchemes); + builder.append(", connectionRequestTimeout=").append(connectionRequestTimeout); + builder.append(", connectTimeout=").append(connectTimeout); + builder.append(", socketTimeout=").append(socketTimeout); + builder.append("]"); + return builder.toString(); + } + + public static RequestConfig.Builder custom() { + return new Builder(); + } + + public static RequestConfig.Builder copy(final RequestConfig config) { + return new Builder() + .setExpectContinueEnabled(config.isExpectContinueEnabled()) + .setProxy(config.getProxy()) + .setLocalAddress(config.getLocalAddress()) + .setStaleConnectionCheckEnabled(config.isStaleConnectionCheckEnabled()) + .setCookieSpec(config.getCookieSpec()) + .setRedirectsEnabled(config.isRedirectsEnabled()) + .setRelativeRedirectsAllowed(config.isRelativeRedirectsAllowed()) + .setCircularRedirectsAllowed(config.isCircularRedirectsAllowed()) + .setMaxRedirects(config.getMaxRedirects()) + .setAuthenticationEnabled(config.isAuthenticationEnabled()) + .setTargetPreferredAuthSchemes(config.getTargetPreferredAuthSchemes()) + .setProxyPreferredAuthSchemes(config.getProxyPreferredAuthSchemes()) + .setConnectionRequestTimeout(config.getConnectionRequestTimeout()) + .setConnectTimeout(config.getConnectTimeout()) + .setSocketTimeout(config.getSocketTimeout()); + } + + public static class Builder { + + private boolean expectContinueEnabled; + private HttpHost proxy; + private InetAddress localAddress; + private boolean staleConnectionCheckEnabled; + private String cookieSpec; + private boolean redirectsEnabled; + private boolean relativeRedirectsAllowed; + private boolean circularRedirectsAllowed; + private int maxRedirects; + private boolean authenticationEnabled; + private Collection targetPreferredAuthSchemes; + private Collection proxyPreferredAuthSchemes; + private int connectionRequestTimeout; + private int connectTimeout; + private int socketTimeout; + + Builder() { + super(); + this.staleConnectionCheckEnabled = true; + this.redirectsEnabled = true; + this.maxRedirects = 50; + this.relativeRedirectsAllowed = true; + this.authenticationEnabled = true; + this.connectionRequestTimeout = -1; + this.connectTimeout = -1; + this.socketTimeout = -1; + } + + public Builder setExpectContinueEnabled(final boolean expectContinueEnabled) { + this.expectContinueEnabled = expectContinueEnabled; + return this; + } + + public Builder setProxy(final HttpHost proxy) { + this.proxy = proxy; + return this; + } + + public Builder setLocalAddress(final InetAddress localAddress) { + this.localAddress = localAddress; + return this; + } + + public Builder setStaleConnectionCheckEnabled(final boolean staleConnectionCheckEnabled) { + this.staleConnectionCheckEnabled = staleConnectionCheckEnabled; + return this; + } + + public Builder setCookieSpec(final String cookieSpec) { + this.cookieSpec = cookieSpec; + return this; + } + + public Builder setRedirectsEnabled(final boolean redirectsEnabled) { + this.redirectsEnabled = redirectsEnabled; + return this; + } + + public Builder setRelativeRedirectsAllowed(final boolean relativeRedirectsAllowed) { + this.relativeRedirectsAllowed = relativeRedirectsAllowed; + return this; + } + + public Builder setCircularRedirectsAllowed(final boolean circularRedirectsAllowed) { + this.circularRedirectsAllowed = circularRedirectsAllowed; + return this; + } + + public Builder setMaxRedirects(final int maxRedirects) { + this.maxRedirects = maxRedirects; + return this; + } + + public Builder setAuthenticationEnabled(final boolean authenticationEnabled) { + this.authenticationEnabled = authenticationEnabled; + return this; + } + + public Builder setTargetPreferredAuthSchemes(final Collection targetPreferredAuthSchemes) { + this.targetPreferredAuthSchemes = targetPreferredAuthSchemes; + return this; + } + + public Builder setProxyPreferredAuthSchemes(final Collection proxyPreferredAuthSchemes) { + this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes; + return this; + } + + public Builder setConnectionRequestTimeout(final int connectionRequestTimeout) { + this.connectionRequestTimeout = connectionRequestTimeout; + return this; + } + + public Builder setConnectTimeout(final int connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + public Builder setSocketTimeout(final int socketTimeout) { + this.socketTimeout = socketTimeout; + return this; + } + + public RequestConfig build() { + return new RequestConfig( + expectContinueEnabled, + proxy, + localAddress, + staleConnectionCheckEnabled, + cookieSpec, + redirectsEnabled, + relativeRedirectsAllowed, + circularRedirectsAllowed, + maxRedirects, + authenticationEnabled, + targetPreferredAuthSchemes, + proxyPreferredAuthSchemes, + connectionRequestTimeout, + connectTimeout, + socketTimeout); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/package-info.java new file mode 100644 index 000000000..a7af2d46b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/config/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client configuration APIs. + */ +package ch.boye.httpclientandroidlib.client.config; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DecompressingEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DecompressingEntity.java new file mode 100644 index 000000000..9adec5edb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DecompressingEntity.java @@ -0,0 +1,105 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Common base class for decompressing {@link HttpEntity} implementations. + * + * @since 4.1 + */ +abstract class DecompressingEntity extends HttpEntityWrapper { + + /** + * Default buffer size. + */ + private static final int BUFFER_SIZE = 1024 * 2; + + /** + * {@link #getContent()} method must return the same {@link InputStream} + * instance when DecompressingEntity is wrapping a streaming entity. + */ + private InputStream content; + + /** + * Creates a new {@link DecompressingEntity}. + * + * @param wrapped + * the non-null {@link HttpEntity} to be wrapped + */ + public DecompressingEntity(final HttpEntity wrapped) { + super(wrapped); + } + + abstract InputStream decorate(final InputStream wrapped) throws IOException; + + private InputStream getDecompressingStream() throws IOException { + final InputStream in = wrappedEntity.getContent(); + return new LazyDecompressingInputStream(in, this); + } + + /** + * {@inheritDoc} + */ + @Override + public InputStream getContent() throws IOException { + if (wrappedEntity.isStreaming()) { + if (content == null) { + content = getDecompressingStream(); + } + return content; + } else { + return getDecompressingStream(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + final InputStream instream = getContent(); + try { + final byte[] buffer = new byte[BUFFER_SIZE]; + int l; + while ((l = instream.read(buffer)) != -1) { + outstream.write(buffer, 0, l); + } + } finally { + instream.close(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateDecompressingEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateDecompressingEntity.java new file mode 100644 index 000000000..4bce8b5b5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateDecompressingEntity.java @@ -0,0 +1,96 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.entity; + +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; + +/** + * {@link ch.boye.httpclientandroidlib.entity.HttpEntityWrapper} responsible for handling + * deflate Content Coded responses. In RFC2616 terms, deflate + * means a zlib stream as defined in RFC1950. Some server + * implementations have misinterpreted RFC2616 to mean that a + * deflate stream as defined in RFC1951 should be used + * (or maybe they did that since that's how IE behaves?). It's confusing + * that deflate in HTTP 1.1 means zlib streams + * rather than deflate streams. We handle both types in here, + * since that's what is seen on the internet. Moral - prefer + * gzip! + * + * @see GzipDecompressingEntity + * + * @since 4.1 + */ +public class DeflateDecompressingEntity extends DecompressingEntity { + + /** + * Creates a new {@link DeflateDecompressingEntity} which will wrap the specified + * {@link HttpEntity}. + * + * @param entity + * a non-null {@link HttpEntity} to be wrapped + */ + public DeflateDecompressingEntity(final HttpEntity entity) { + super(entity); + } + + /** + * Returns the non-null InputStream that should be returned to by all requests to + * {@link #getContent()}. + * + * @return a non-null InputStream + * @throws IOException if there was a problem + */ + @Override + InputStream decorate(final InputStream wrapped) throws IOException { + return new DeflateInputStream(wrapped); + } + + /** + * {@inheritDoc} + */ + @Override + public Header getContentEncoding() { + + /* This HttpEntityWrapper has dealt with the Content-Encoding. */ + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public long getContentLength() { + + /* Length of inflated content is unknown. */ + return -1; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateInputStream.java new file mode 100644 index 000000000..392a28a74 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/DeflateInputStream.java @@ -0,0 +1,228 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** Deflate input stream. This class includes logic needed for various Rfc's in order +* to reasonably implement the "deflate" compression style. +*/ +public class DeflateInputStream extends InputStream +{ + private InputStream sourceStream; + + public DeflateInputStream(final InputStream wrapped) + throws IOException + { + /* + * A zlib stream will have a header. + * + * CMF | FLG [| DICTID ] | ...compressed data | ADLER32 | + * + * * CMF is one byte. + * + * * FLG is one byte. + * + * * DICTID is four bytes, and only present if FLG.FDICT is set. + * + * Sniff the content. Does it look like a zlib stream, with a CMF, etc? c.f. RFC1950, + * section 2.2. http://tools.ietf.org/html/rfc1950#page-4 + * + * We need to see if it looks like a proper zlib stream, or whether it is just a deflate + * stream. RFC2616 calls zlib streams deflate. Confusing, isn't it? That's why some servers + * implement deflate Content-Encoding using deflate streams, rather than zlib streams. + * + * We could start looking at the bytes, but to be honest, someone else has already read + * the RFCs and implemented that for us. So we'll just use the JDK libraries and exception + * handling to do this. If that proves slow, then we could potentially change this to check + * the first byte - does it look like a CMF? What about the second byte - does it look like + * a FLG, etc. + */ + + /* We read a small buffer to sniff the content. */ + final byte[] peeked = new byte[6]; + + final PushbackInputStream pushback = new PushbackInputStream(wrapped, peeked.length); + + final int headerLength = pushback.read(peeked); + + if (headerLength == -1) { + throw new IOException("Unable to read the response"); + } + + /* We try to read the first uncompressed byte. */ + final byte[] dummy = new byte[1]; + + final Inflater inf = new Inflater(); + + try { + int n; + while ((n = inf.inflate(dummy)) == 0) { + if (inf.finished()) { + + /* Not expecting this, so fail loudly. */ + throw new IOException("Unable to read the response"); + } + + if (inf.needsDictionary()) { + + /* Need dictionary - then it must be zlib stream with DICTID part? */ + break; + } + + if (inf.needsInput()) { + inf.setInput(peeked); + } + } + + if (n == -1) { + throw new IOException("Unable to read the response"); + } + + /* + * We read something without a problem, so it's a valid zlib stream. Just need to reset + * and return an unused InputStream now. + */ + pushback.unread(peeked, 0, headerLength); + sourceStream = new DeflateStream(pushback, new Inflater()); + } catch (final DataFormatException e) { + + /* Presume that it's an RFC1951 deflate stream rather than RFC1950 zlib stream and try + * again. */ + pushback.unread(peeked, 0, headerLength); + sourceStream = new DeflateStream(pushback, new Inflater(true)); + } finally { + inf.end(); + } + + } + + /** Read a byte. + */ + @Override + public int read() + throws IOException + { + return sourceStream.read(); + } + + /** Read lots of bytes. + */ + @Override + public int read(final byte[] b) + throws IOException + { + return sourceStream.read(b); + } + + /** Read lots of specific bytes. + */ + @Override + public int read(final byte[] b, final int off, final int len) + throws IOException + { + return sourceStream.read(b,off,len); + } + + /** Skip + */ + @Override + public long skip(final long n) + throws IOException + { + return sourceStream.skip(n); + } + + /** Get available. + */ + @Override + public int available() + throws IOException + { + return sourceStream.available(); + } + + /** Mark. + */ + @Override + public void mark(final int readLimit) + { + sourceStream.mark(readLimit); + } + + /** Reset. + */ + @Override + public void reset() + throws IOException + { + sourceStream.reset(); + } + + /** Check if mark is supported. + */ + @Override + public boolean markSupported() + { + return sourceStream.markSupported(); + } + + /** Close. + */ + @Override + public void close() + throws IOException + { + sourceStream.close(); + } + + static class DeflateStream extends InflaterInputStream { + + private boolean closed = false; + + public DeflateStream(final InputStream in, final Inflater inflater) { + super(in, inflater); + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + closed = true; + inf.end(); + super.close(); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/EntityBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/EntityBuilder.java new file mode 100644 index 000000000..ebec48a13 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/EntityBuilder.java @@ -0,0 +1,342 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.entity; + +import java.io.File; +import java.io.InputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.entity.AbstractHttpEntity; +import ch.boye.httpclientandroidlib.entity.BasicHttpEntity; +import ch.boye.httpclientandroidlib.entity.ByteArrayEntity; +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.FileEntity; +import ch.boye.httpclientandroidlib.entity.InputStreamEntity; +import ch.boye.httpclientandroidlib.entity.SerializableEntity; +import ch.boye.httpclientandroidlib.entity.StringEntity; + +/** + * Builder for {@link HttpEntity} instances. + *

    + * Several setter methods of this builder are mutually exclusive. In case of multiple invocations + * of the following methods only the last one will have effect: + *

      + *
    • {@link #setText(String)}
    • + *
    • {@link #setBinary(byte[])}
    • + *
    • {@link #setStream(java.io.InputStream)}
    • + *
    • {@link #setSerializable(java.io.Serializable)}
    • + *
    • {@link #setParameters(java.util.List)}
    • + *
    • {@link #setParameters(ch.boye.httpclientandroidlib.NameValuePair...)}
    • + *
    • {@link #setFile(java.io.File)}
    • + *
    + * + * @since 4.3 + */ +@NotThreadSafe +public class EntityBuilder { + + private String text; + private byte[] binary; + private InputStream stream; + private List parameters; + private Serializable serializable; + private File file; + private ContentType contentType; + private String contentEncoding; + private boolean chunked; + private boolean gzipCompress; + + EntityBuilder() { + super(); + } + + public static EntityBuilder create() { + return new EntityBuilder(); + } + + private void clearContent() { + this.text = null; + this.binary = null; + this.stream = null; + this.parameters = null; + this.serializable = null; + this.file = null; + } + + /** + * Returns entity content as a string if set using {@link #setText(String)} method. + */ + public String getText() { + return text; + } + + /** + * Sets entity content as a string. This method is mutually exclusive with + * {@link #setBinary(byte[])}, + * {@link #setStream(java.io.InputStream)} , + * {@link #setSerializable(java.io.Serializable)} , + * {@link #setParameters(java.util.List)}, + * {@link #setParameters(ch.boye.httpclientandroidlib.NameValuePair...)} + * {@link #setFile(java.io.File)} methods. + */ + public EntityBuilder setText(final String text) { + clearContent(); + this.text = text; + return this; + } + + /** + * Returns entity content as a byte array if set using + * {@link #setBinary(byte[])} method. + */ + public byte[] getBinary() { + return binary; + } + + /** + * Sets entity content as a byte array. This method is mutually exclusive with + * {@link #setText(String)}, + * {@link #setStream(java.io.InputStream)} , + * {@link #setSerializable(java.io.Serializable)} , + * {@link #setParameters(java.util.List)}, + * {@link #setParameters(ch.boye.httpclientandroidlib.NameValuePair...)} + * {@link #setFile(java.io.File)} methods. + */ + public EntityBuilder setBinary(final byte[] binary) { + clearContent(); + this.binary = binary; + return this; + } + + /** + * Returns entity content as a {@link InputStream} if set using + * {@link #setStream(java.io.InputStream)} method. + */ + public InputStream getStream() { + return stream; + } + + /** + * Sets entity content as a {@link InputStream}. This method is mutually exclusive with + * {@link #setText(String)}, + * {@link #setBinary(byte[])}, + * {@link #setSerializable(java.io.Serializable)} , + * {@link #setParameters(java.util.List)}, + * {@link #setParameters(ch.boye.httpclientandroidlib.NameValuePair...)} + * {@link #setFile(java.io.File)} methods. + */ + public EntityBuilder setStream(final InputStream stream) { + clearContent(); + this.stream = stream; + return this; + } + + /** + * Returns entity content as a parameter list if set using + * {@link #setParameters(java.util.List)} or + * {@link #setParameters(ch.boye.httpclientandroidlib.NameValuePair...)} methods. + */ + public List getParameters() { + return parameters; + } + + /** + * Sets entity content as a parameter list. This method is mutually exclusive with + * {@link #setText(String)}, + * {@link #setBinary(byte[])}, + * {@link #setStream(java.io.InputStream)} , + * {@link #setSerializable(java.io.Serializable)} , + * {@link #setFile(java.io.File)} methods. + */ + public EntityBuilder setParameters(final List parameters) { + clearContent(); + this.parameters = parameters; + return this; + } + + /** + * Sets entity content as a parameter list. This method is mutually exclusive with + * {@link #setText(String)}, + * {@link #setBinary(byte[])}, + * {@link #setStream(java.io.InputStream)} , + * {@link #setSerializable(java.io.Serializable)} , + * {@link #setFile(java.io.File)} methods. + */ + public EntityBuilder setParameters(final NameValuePair... parameters) { + return setParameters(Arrays.asList(parameters)); + } + + /** + * Returns entity content as a {@link Serializable} if set using + * {@link #setSerializable(java.io.Serializable)} method. + */ + public Serializable getSerializable() { + return serializable; + } + + /** + * Sets entity content as a {@link Serializable}. This method is mutually exclusive with + * {@link #setText(String)}, + * {@link #setBinary(byte[])}, + * {@link #setStream(java.io.InputStream)} , + * {@link #setParameters(java.util.List)}, + * {@link #setParameters(ch.boye.httpclientandroidlib.NameValuePair...)} + * {@link #setFile(java.io.File)} methods. + */ + public EntityBuilder setSerializable(final Serializable serializable) { + clearContent(); + this.serializable = serializable; + return this; + } + + /** + * Returns entity content as a {@link File} if set using + * {@link #setFile(java.io.File)} method. + */ + public File getFile() { + return file; + } + + /** + * Sets entity content as a {@link File}. This method is mutually exclusive with + * {@link #setText(String)}, + * {@link #setBinary(byte[])}, + * {@link #setStream(java.io.InputStream)} , + * {@link #setParameters(java.util.List)}, + * {@link #setParameters(ch.boye.httpclientandroidlib.NameValuePair...)} + * {@link #setSerializable(java.io.Serializable)} methods. + */ + public EntityBuilder setFile(final File file) { + clearContent(); + this.file = file; + return this; + } + + /** + * Returns {@link ContentType} of the entity, if set. + */ + public ContentType getContentType() { + return contentType; + } + + /** + * Sets {@link ContentType} of the entity. + */ + public EntityBuilder setContentType(final ContentType contentType) { + this.contentType = contentType; + return this; + } + + /** + * Returns content encoding of the entity, if set. + */ + public String getContentEncoding() { + return contentEncoding; + } + + /** + * Sets content encoding of the entity. + */ + public EntityBuilder setContentEncoding(final String contentEncoding) { + this.contentEncoding = contentEncoding; + return this; + } + + /** + * Returns true if entity is to be chunk coded, false otherwise. + */ + public boolean isChunked() { + return chunked; + } + + /** + * Makes entity chunk coded. + */ + public EntityBuilder chunked() { + this.chunked = true; + return this; + } + + /** + * Returns true if entity is to be GZIP compressed, false otherwise. + */ + public boolean isGzipCompress() { + return gzipCompress; + } + + /** + * Makes entity GZIP compressed. + */ + public EntityBuilder gzipCompress() { + this.gzipCompress = true; + return this; + } + + private ContentType getContentOrDefault(final ContentType def) { + return this.contentType != null ? this.contentType : def; + } + + /** + * Creates new instance of {@link HttpEntity} based on the current state. + */ + public HttpEntity build() { + final AbstractHttpEntity e; + if (this.text != null) { + e = new StringEntity(this.text, getContentOrDefault(ContentType.DEFAULT_TEXT)); + } else if (this.binary != null) { + e = new ByteArrayEntity(this.binary, getContentOrDefault(ContentType.DEFAULT_BINARY)); + } else if (this.stream != null) { + e = new InputStreamEntity(this.stream, 1, getContentOrDefault(ContentType.DEFAULT_BINARY)); + } else if (this.parameters != null) { + e = new UrlEncodedFormEntity(this.parameters, + this.contentType != null ? this.contentType.getCharset() : null); + } else if (this.serializable != null) { + e = new SerializableEntity(this.serializable); + e.setContentType(ContentType.DEFAULT_BINARY.toString()); + } else if (this.file != null) { + e = new FileEntity(this.file, getContentOrDefault(ContentType.DEFAULT_BINARY)); + } else { + e = new BasicHttpEntity(); + } + if (e.getContentType() != null && this.contentType != null) { + e.setContentType(this.contentType.toString()); + } + e.setContentEncoding(this.contentEncoding); + e.setChunked(this.chunked); + if (this.gzipCompress) { + return new GzipCompressingEntity(e); + } + return e; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipCompressingEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipCompressingEntity.java new file mode 100644 index 000000000..89925a2b9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipCompressingEntity.java @@ -0,0 +1,113 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.entity; + +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.GZIPOutputStream; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper; +import ch.boye.httpclientandroidlib.message.BasicHeader; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Wrapping entity that compresses content when {@link #writeTo writing}. + * + * + * @since 4.0 + */ +public class GzipCompressingEntity extends HttpEntityWrapper { + + private static final String GZIP_CODEC = "gzip"; + + public GzipCompressingEntity(final HttpEntity entity) { + super(entity); + } + + @Override + public Header getContentEncoding() { + return new BasicHeader(HTTP.CONTENT_ENCODING, GZIP_CODEC); + } + + @Override + public long getContentLength() { + return -1; + } + + @Override + public boolean isChunked() { + // force content chunking + return true; + } + + @Override + public InputStream getContent() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + final GZIPOutputStream gzip = new GZIPOutputStream(outstream); + wrappedEntity.writeTo(gzip); + // Only close output stream if the wrapped entity has been + // successfully written out + gzip.close(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipDecompressingEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipDecompressingEntity.java new file mode 100644 index 000000000..a3dd5b259 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/GzipDecompressingEntity.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; + +/** + * {@link ch.boye.httpclientandroidlib.entity.HttpEntityWrapper} for handling gzip + * Content Coded responses. + * + * @since 4.1 + */ +public class GzipDecompressingEntity extends DecompressingEntity { + + /** + * Creates a new {@link GzipDecompressingEntity} which will wrap the specified + * {@link HttpEntity}. + * + * @param entity + * the non-null {@link HttpEntity} to be wrapped + */ + public GzipDecompressingEntity(final HttpEntity entity) { + super(entity); + } + + @Override + InputStream decorate(final InputStream wrapped) throws IOException { + return new GZIPInputStream(wrapped); + } + + /** + * {@inheritDoc} + */ + @Override + public Header getContentEncoding() { + + /* This HttpEntityWrapper has dealt with the Content-Encoding. */ + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public long getContentLength() { + + /* length of ungzipped content is not known */ + return -1; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/LazyDecompressingInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/LazyDecompressingInputStream.java new file mode 100644 index 000000000..60215ff15 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/LazyDecompressingInputStream.java @@ -0,0 +1,105 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.entity; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Lazy init InputStream wrapper. + */ +@NotThreadSafe +class LazyDecompressingInputStream extends InputStream { + + private final InputStream wrappedStream; + + private final DecompressingEntity decompressingEntity; + + private InputStream wrapperStream; + + public LazyDecompressingInputStream( + final InputStream wrappedStream, + final DecompressingEntity decompressingEntity) { + this.wrappedStream = wrappedStream; + this.decompressingEntity = decompressingEntity; + } + + private void initWrapper() throws IOException { + if (wrapperStream == null) { + wrapperStream = decompressingEntity.decorate(wrappedStream); + } + } + + @Override + public int read() throws IOException { + initWrapper(); + return wrapperStream.read(); + } + + @Override + public int read(final byte[] b) throws IOException { + initWrapper(); + return wrapperStream.read(b); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + initWrapper(); + return wrapperStream.read(b, off, len); + } + + @Override + public long skip(final long n) throws IOException { + initWrapper(); + return wrapperStream.skip(n); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public int available() throws IOException { + initWrapper(); + return wrapperStream.available(); + } + + @Override + public void close() throws IOException { + try { + if (wrapperStream != null) { + wrapperStream.close(); + } + } finally { + wrappedStream.close(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/UrlEncodedFormEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/UrlEncodedFormEntity.java new file mode 100644 index 000000000..855155927 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/UrlEncodedFormEntity.java @@ -0,0 +1,107 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.entity; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.List; + +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.utils.URLEncodedUtils; +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.StringEntity; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * An entity composed of a list of url-encoded pairs. + * This is typically useful while sending an HTTP POST request. + * + * @since 4.0 + */ +@NotThreadSafe // AbstractHttpEntity is not thread-safe +public class UrlEncodedFormEntity extends StringEntity { + + /** + * Constructs a new {@link UrlEncodedFormEntity} with the list + * of parameters in the specified encoding. + * + * @param parameters list of name/value pairs + * @param charset encoding the name/value pairs be encoded with + * @throws UnsupportedEncodingException if the encoding isn't supported + */ + public UrlEncodedFormEntity ( + final List parameters, + final String charset) throws UnsupportedEncodingException { + super(URLEncodedUtils.format(parameters, + charset != null ? charset : HTTP.DEF_CONTENT_CHARSET.name()), + ContentType.create(URLEncodedUtils.CONTENT_TYPE, charset)); + } + + /** + * Constructs a new {@link UrlEncodedFormEntity} with the list + * of parameters in the specified encoding. + * + * @param parameters iterable collection of name/value pairs + * @param charset encoding the name/value pairs be encoded with + * + * @since 4.2 + */ + public UrlEncodedFormEntity ( + final Iterable parameters, + final Charset charset) { + super(URLEncodedUtils.format(parameters, + charset != null ? charset : HTTP.DEF_CONTENT_CHARSET), + ContentType.create(URLEncodedUtils.CONTENT_TYPE, charset)); + } + + /** + * Constructs a new {@link UrlEncodedFormEntity} with the list + * of parameters with the default encoding of {@link HTTP#DEFAULT_CONTENT_CHARSET} + * + * @param parameters list of name/value pairs + * @throws UnsupportedEncodingException if the default encoding isn't supported + */ + public UrlEncodedFormEntity ( + final List parameters) throws UnsupportedEncodingException { + this(parameters, (Charset) null); + } + + /** + * Constructs a new {@link UrlEncodedFormEntity} with the list + * of parameters with the default encoding of {@link HTTP#DEFAULT_CONTENT_CHARSET} + * + * @param parameters iterable collection of name/value pairs + * + * @since 4.2 + */ + public UrlEncodedFormEntity ( + final Iterable parameters) { + this(parameters, null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/package-info.java new file mode 100644 index 000000000..703f80d58 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/entity/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client specific HTTP entity implementations. + */ +package ch.boye.httpclientandroidlib.client.entity; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbortableHttpRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbortableHttpRequest.java new file mode 100644 index 000000000..92715c26a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbortableHttpRequest.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ConnectionReleaseTrigger; + +import java.io.IOException; + + +/** + * Interface representing an HTTP request that can be aborted by shutting + * down the underlying HTTP connection. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link HttpExecutionAware} + */ +@Deprecated +public interface AbortableHttpRequest { + + /** + * Sets the {@link ch.boye.httpclientandroidlib.conn.ClientConnectionRequest} + * callback that can be used to abort a long-lived request for a connection. + * If the request is already aborted, throws an {@link IOException}. + * + * @see ch.boye.httpclientandroidlib.conn.ClientConnectionManager + */ + void setConnectionRequest(ClientConnectionRequest connRequest) throws IOException; + + /** + * Sets the {@link ConnectionReleaseTrigger} callback that can + * be used to abort an active connection. + * Typically, this will be the + * {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection} itself. + * If the request is already aborted, throws an {@link IOException}. + */ + void setReleaseTrigger(ConnectionReleaseTrigger releaseTrigger) throws IOException; + + /** + * Aborts this http request. Any active execution of this method should + * return immediately. If the request has not started, it will abort after + * the next execution. Aborting this request will cause all subsequent + * executions with this request to fail. + * + * @see ch.boye.httpclientandroidlib.client.HttpClient#execute(HttpUriRequest) + * @see ch.boye.httpclientandroidlib.client.HttpClient#execute(ch.boye.httpclientandroidlib.HttpHost, + * ch.boye.httpclientandroidlib.HttpRequest) + * @see ch.boye.httpclientandroidlib.client.HttpClient#execute(HttpUriRequest, + * ch.boye.httpclientandroidlib.protocol.HttpContext) + * @see ch.boye.httpclientandroidlib.client.HttpClient#execute(ch.boye.httpclientandroidlib.HttpHost, + * ch.boye.httpclientandroidlib.HttpRequest, ch.boye.httpclientandroidlib.protocol.HttpContext) + */ + void abort(); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbstractExecutionAwareRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbstractExecutionAwareRequest.java new file mode 100644 index 000000000..2525769c3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/AbstractExecutionAwareRequest.java @@ -0,0 +1,131 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.methods; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.client.utils.CloneUtils; +import ch.boye.httpclientandroidlib.concurrent.Cancellable; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ConnectionReleaseTrigger; +import ch.boye.httpclientandroidlib.message.AbstractHttpMessage; + +@SuppressWarnings("deprecation") +public abstract class AbstractExecutionAwareRequest extends AbstractHttpMessage implements + HttpExecutionAware, AbortableHttpRequest, Cloneable, HttpRequest { + + private final AtomicBoolean aborted; + private final AtomicReference cancellableRef; + + protected AbstractExecutionAwareRequest() { + super(); + this.aborted = new AtomicBoolean(false); + this.cancellableRef = new AtomicReference(null); + } + + @Deprecated + public void setConnectionRequest(final ClientConnectionRequest connRequest) { + setCancellable(new Cancellable() { + + public boolean cancel() { + connRequest.abortRequest(); + return true; + } + + }); + } + + @Deprecated + public void setReleaseTrigger(final ConnectionReleaseTrigger releaseTrigger) { + setCancellable(new Cancellable() { + + public boolean cancel() { + try { + releaseTrigger.abortConnection(); + return true; + } catch (final IOException ex) { + return false; + } + } + + }); + } + + public void abort() { + if (this.aborted.compareAndSet(false, true)) { + final Cancellable cancellable = this.cancellableRef.getAndSet(null); + if (cancellable != null) { + cancellable.cancel(); + } + } + } + + public boolean isAborted() { + return this.aborted.get(); + } + + /** + * @since 4.2 + */ + public void setCancellable(final Cancellable cancellable) { + if (!this.aborted.get()) { + this.cancellableRef.set(cancellable); + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + final AbstractExecutionAwareRequest clone = (AbstractExecutionAwareRequest) super.clone(); + clone.headergroup = CloneUtils.cloneObject(this.headergroup); + clone.params = CloneUtils.cloneObject(this.params); + return clone; + } + + /** + * @since 4.2 + */ + public void completed() { + this.cancellableRef.set(null); + } + + /** + * Resets internal state of the request making it reusable. + * + * @since 4.2 + */ + public void reset() { + final Cancellable cancellable = this.cancellableRef.getAndSet(null); + if (cancellable != null) { + cancellable.cancel(); + } + this.aborted.set(false); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/CloseableHttpResponse.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/CloseableHttpResponse.java new file mode 100644 index 000000000..471a11219 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/CloseableHttpResponse.java @@ -0,0 +1,40 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.io.Closeable; + +import ch.boye.httpclientandroidlib.HttpResponse; + +/** + * Extended version of the {@link HttpResponse} interface that also extends {@link Closeable}. + * + * @since 4.3 + */ +public interface CloseableHttpResponse extends HttpResponse, Closeable { +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/Configurable.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/Configurable.java new file mode 100644 index 000000000..74f014f20 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/Configurable.java @@ -0,0 +1,44 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import ch.boye.httpclientandroidlib.client.config.RequestConfig; + +/** + * Configuration interface for HTTP requests. + * + * @since 4.3 + */ +public interface Configurable { + + /** + * Returns actual request configuration. + */ + RequestConfig getConfig(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpDelete.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpDelete.java new file mode 100644 index 000000000..20baf651c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpDelete.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * HTTP DELETE method + *

    + * The HTTP DELETE method is defined in section 9.7 of + * RFC2616: + *

    + * The DELETE method requests that the origin server delete the resource + * identified by the Request-URI. [...] The client cannot + * be guaranteed that the operation has been carried out, even if the + * status code returned from the origin server indicates that the action + * has been completed successfully. + *
    + * + * @since 4.0 + */ +@NotThreadSafe // HttpRequestBase is @NotThreadSafe +public class HttpDelete extends HttpRequestBase { + + public final static String METHOD_NAME = "DELETE"; + + + public HttpDelete() { + super(); + } + + public HttpDelete(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpDelete(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpEntityEnclosingRequestBase.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpEntityEnclosingRequestBase.java new file mode 100644 index 000000000..cbc035ade --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpEntityEnclosingRequestBase.java @@ -0,0 +1,76 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.utils.CloneUtils; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * Basic implementation of an entity enclosing HTTP request + * that can be modified + * + * @since 4.0 + */ +@NotThreadSafe // HttpRequestBase is @NotThreadSafe +public abstract class HttpEntityEnclosingRequestBase + extends HttpRequestBase implements HttpEntityEnclosingRequest { + + private HttpEntity entity; + + public HttpEntityEnclosingRequestBase() { + super(); + } + + public HttpEntity getEntity() { + return this.entity; + } + + public void setEntity(final HttpEntity entity) { + this.entity = entity; + } + + public boolean expectContinue() { + final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE); + return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue()); + } + + @Override + public Object clone() throws CloneNotSupportedException { + final HttpEntityEnclosingRequestBase clone = + (HttpEntityEnclosingRequestBase) super.clone(); + if (this.entity != null) { + clone.entity = CloneUtils.cloneObject(this.entity); + } + return clone; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpExecutionAware.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpExecutionAware.java new file mode 100644 index 000000000..6dab381d3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpExecutionAware.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import ch.boye.httpclientandroidlib.concurrent.Cancellable; + +/** + * Interface to be implemented by any object that wishes to be notified of + * blocking I/O operations that could be cancelled. + * + * @since 4.3 + */ +public interface HttpExecutionAware { + + boolean isAborted(); + + /** + * Sets {@link Cancellable} for the ongoing operation. + */ + void setCancellable(Cancellable cancellable); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpGet.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpGet.java new file mode 100644 index 000000000..33de1a8dc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpGet.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * HTTP GET method. + *

    + * The HTTP GET method is defined in section 9.3 of + * RFC2616: + *

    + * The GET method means retrieve whatever information (in the form of an + * entity) is identified by the Request-URI. If the Request-URI refers + * to a data-producing process, it is the produced data which shall be + * returned as the entity in the response and not the source text of the + * process, unless that text happens to be the output of the process. + *
    + *

    + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpGet extends HttpRequestBase { + + public final static String METHOD_NAME = "GET"; + + public HttpGet() { + super(); + } + + public HttpGet(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpGet(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpHead.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpHead.java new file mode 100644 index 000000000..58ab5b00b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpHead.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * HTTP HEAD method. + *

    + * The HTTP HEAD method is defined in section 9.4 of + * RFC2616: + *

    + * The HEAD method is identical to GET except that the server MUST NOT + * return a message-body in the response. The metainformation contained + * in the HTTP headers in response to a HEAD request SHOULD be identical + * to the information sent in response to a GET request. This method can + * be used for obtaining metainformation about the entity implied by the + * request without transferring the entity-body itself. This method is + * often used for testing hypertext links for validity, accessibility, + * and recent modification. + *
    + *

    + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpHead extends HttpRequestBase { + + public final static String METHOD_NAME = "HEAD"; + + public HttpHead() { + super(); + } + + public HttpHead(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpHead(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpOptions.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpOptions.java new file mode 100644 index 000000000..af3ee251b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpOptions.java @@ -0,0 +1,100 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; +import java.util.HashSet; +import java.util.Set; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * HTTP OPTIONS method. + *

    + * The HTTP OPTIONS method is defined in section 9.2 of + * RFC2616: + *

    + * The OPTIONS method represents a request for information about the + * communication options available on the request/response chain + * identified by the Request-URI. This method allows the client to + * determine the options and/or requirements associated with a resource, + * or the capabilities of a server, without implying a resource action + * or initiating a resource retrieval. + *
    + *

    + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpOptions extends HttpRequestBase { + + public final static String METHOD_NAME = "OPTIONS"; + + public HttpOptions() { + super(); + } + + public HttpOptions(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpOptions(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + + public Set getAllowedMethods(final HttpResponse response) { + Args.notNull(response, "HTTP response"); + + final HeaderIterator it = response.headerIterator("Allow"); + final Set methods = new HashSet(); + while (it.hasNext()) { + final Header header = it.nextHeader(); + final HeaderElement[] elements = header.getElements(); + for (final HeaderElement element : elements) { + methods.add(element.getName()); + } + } + return methods; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPatch.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPatch.java new file mode 100644 index 000000000..8cfd29fbb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPatch.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * HTTP PATCH method. + *

    + * The HTTP PATCH method is defined in RF5789:

    The PATCH + * method requests that a set of changes described in the request entity be + * applied to the resource identified by the Request- URI. Differs from the PUT + * method in the way the server processes the enclosed entity to modify the + * resource identified by the Request-URI. In a PUT request, the enclosed entity + * origin server, and the client is requesting that the stored version be + * replaced. With PATCH, however, the enclosed entity contains a set of + * instructions describing how a resource currently residing on the origin + * server should be modified to produce a new version.
    + *

    + * + * @since 4.2 + */ +@NotThreadSafe +public class HttpPatch extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "PATCH"; + + public HttpPatch() { + super(); + } + + public HttpPatch(final URI uri) { + super(); + setURI(uri); + } + + public HttpPatch(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPost.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPost.java new file mode 100644 index 000000000..f70538b80 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPost.java @@ -0,0 +1,84 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * HTTP POST method. + *

    + * The HTTP POST method is defined in section 9.5 of + * RFC2616: + *

    + * The POST method is used to request that the origin server accept the entity + * enclosed in the request as a new subordinate of the resource identified by + * the Request-URI in the Request-Line. POST is designed to allow a uniform + * method to cover the following functions: + *
      + *
    • Annotation of existing resources
    • + *
    • Posting a message to a bulletin board, newsgroup, mailing list, or + * similar group of articles
    • + *
    • Providing a block of data, such as the result of submitting a form, + * to a data-handling process
    • + *
    • Extending a database through an append operation
    • + *
    + *
    + *

    + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpPost extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "POST"; + + public HttpPost() { + super(); + } + + public HttpPost(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpPost(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPut.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPut.java new file mode 100644 index 000000000..4aab04b23 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpPut.java @@ -0,0 +1,76 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * HTTP PUT method. + *

    + * The HTTP PUT method is defined in section 9.6 of + * RFC2616: + *

    + * The PUT method requests that the enclosed entity be stored under the + * supplied Request-URI. If the Request-URI refers to an already + * existing resource, the enclosed entity SHOULD be considered as a + * modified version of the one residing on the origin server. + *
    + *

    + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpPut extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "PUT"; + + public HttpPut() { + super(); + } + + public HttpPut(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpPut(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestBase.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestBase.java new file mode 100644 index 000000000..da3b01ef5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestBase.java @@ -0,0 +1,124 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.message.BasicRequestLine; +import ch.boye.httpclientandroidlib.params.HttpProtocolParams; + +/** + * Base implementation of {@link HttpUriRequest}. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public abstract class HttpRequestBase extends AbstractExecutionAwareRequest + implements HttpUriRequest, Configurable { + + private ProtocolVersion version; + private URI uri; + private RequestConfig config; + + public abstract String getMethod(); + + /** + * @since 4.3 + */ + public void setProtocolVersion(final ProtocolVersion version) { + this.version = version; + } + + public ProtocolVersion getProtocolVersion() { + return version != null ? version : HttpProtocolParams.getVersion(getParams()); + } + + /** + * Returns the original request URI. + *

    + * Please note URI remains unchanged in the course of request execution and + * is not updated if the request is redirected to another location. + */ + public URI getURI() { + return this.uri; + } + + public RequestLine getRequestLine() { + final String method = getMethod(); + final ProtocolVersion ver = getProtocolVersion(); + final URI uri = getURI(); + String uritext = null; + if (uri != null) { + uritext = uri.toASCIIString(); + } + if (uritext == null || uritext.length() == 0) { + uritext = "/"; + } + return new BasicRequestLine(method, uritext, ver); + } + + + public RequestConfig getConfig() { + return config; + } + + public void setConfig(final RequestConfig config) { + this.config = config; + } + + public void setURI(final URI uri) { + this.uri = uri; + } + + /** + * @since 4.2 + */ + public void started() { + } + + /** + * A convenience method to simplify migration from HttpClient 3.1 API. This method is + * equivalent to {@link #reset()}. + * + * @since 4.2 + */ + public void releaseConnection() { + reset(); + } + + @Override + public String toString() { + return getMethod() + " " + getURI() + " " + getProtocolVersion(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestWrapper.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestWrapper.java new file mode 100644 index 000000000..72b7e2c55 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpRequestWrapper.java @@ -0,0 +1,171 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.message.AbstractHttpMessage; +import ch.boye.httpclientandroidlib.message.BasicRequestLine; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * A wrapper class for {@link HttpRequest} that can be used to change properties of the current + * request without modifying the original object. + * + * @since 4.3 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public class HttpRequestWrapper extends AbstractHttpMessage implements HttpUriRequest { + + private final HttpRequest original; + private final String method; + private ProtocolVersion version; + private URI uri; + + private HttpRequestWrapper(final HttpRequest request) { + super(); + this.original = request; + this.version = this.original.getRequestLine().getProtocolVersion(); + this.method = this.original.getRequestLine().getMethod(); + if (request instanceof HttpUriRequest) { + this.uri = ((HttpUriRequest) request).getURI(); + } else { + this.uri = null; + } + setHeaders(request.getAllHeaders()); + } + + public ProtocolVersion getProtocolVersion() { + return this.version != null ? this.version : this.original.getProtocolVersion(); + } + + public void setProtocolVersion(final ProtocolVersion version) { + this.version = version; + } + + public URI getURI() { + return this.uri; + } + + public void setURI(final URI uri) { + this.uri = uri; + } + + public String getMethod() { + return method; + } + + public void abort() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public boolean isAborted() { + return false; + } + + public RequestLine getRequestLine() { + String requestUri = null; + if (this.uri != null) { + requestUri = this.uri.toASCIIString(); + } else { + requestUri = this.original.getRequestLine().getUri(); + } + if (requestUri == null || requestUri.length() == 0) { + requestUri = "/"; + } + return new BasicRequestLine(this.method, requestUri, getProtocolVersion()); + } + + public HttpRequest getOriginal() { + return this.original; + } + + @Override + public String toString() { + return getRequestLine() + " " + this.headergroup; + } + + static class HttpEntityEnclosingRequestWrapper extends HttpRequestWrapper + implements HttpEntityEnclosingRequest { + + private HttpEntity entity; + + public HttpEntityEnclosingRequestWrapper(final HttpEntityEnclosingRequest request) { + super(request); + this.entity = request.getEntity(); + } + + public HttpEntity getEntity() { + return this.entity; + } + + public void setEntity(final HttpEntity entity) { + this.entity = entity; + } + + public boolean expectContinue() { + final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE); + return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue()); + } + + } + + public static HttpRequestWrapper wrap(final HttpRequest request) { + if (request == null) { + return null; + } + if (request instanceof HttpEntityEnclosingRequest) { + return new HttpEntityEnclosingRequestWrapper((HttpEntityEnclosingRequest) request); + } else { + return new HttpRequestWrapper(request); + } + } + + /** + * @deprecated (4.3) use + * {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}. + */ + @Override + @Deprecated + public HttpParams getParams() { + if (this.params == null) { + this.params = original.getParams().copy(); + } + return this.params; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpTrace.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpTrace.java new file mode 100644 index 000000000..1118ea181 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpTrace.java @@ -0,0 +1,79 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * HTTP TRACE method. + *

    + * The HTTP TRACE method is defined in section 9.6 of + * RFC2616: + *

    + * The TRACE method is used to invoke a remote, application-layer loop- + * back of the request message. The final recipient of the request + * SHOULD reflect the message received back to the client as the + * entity-body of a 200 (OK) response. The final recipient is either the + * origin server or the first proxy or gateway to receive a Max-Forwards + * value of zero (0) in the request (see section 14.31). A TRACE request + * MUST NOT include an entity. + *
    + *

    + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpTrace extends HttpRequestBase { + + public final static String METHOD_NAME = "TRACE"; + + public HttpTrace() { + super(); + } + + public HttpTrace(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public HttpTrace(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpUriRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpUriRequest.java new file mode 100644 index 000000000..8ab14ef85 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/HttpUriRequest.java @@ -0,0 +1,84 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.HttpRequest; + +/** + * Extended version of the {@link HttpRequest} interface that provides + * convenience methods to access request properties such as request URI + * and method type. + * + * @since 4.0 + */ +public interface HttpUriRequest extends HttpRequest { + + /** + * Returns the HTTP method this request uses, such as GET, + * PUT, POST, or other. + */ + String getMethod(); + + /** + * Returns the URI this request uses, such as + * http://example.org/path/to/file. + *
    + * Note that the URI may be absolute URI (as above) or may be a relative URI. + *

    + * Implementations are encouraged to return + * the URI that was initially requested. + *

    + *

    + * To find the final URI after any redirects have been processed, + * please see the section entitled + * HTTP execution context + * in the + * HttpClient Tutorial + *

    + */ + URI getURI(); + + /** + * Aborts execution of the request. + * + * @throws UnsupportedOperationException if the abort operation + * is not supported / cannot be implemented. + */ + void abort() throws UnsupportedOperationException; + + /** + * Tests if the request execution has been aborted. + * + * @return true if the request execution has been aborted, + * false otherwise. + */ + boolean isAborted(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/RequestBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/RequestBuilder.java new file mode 100644 index 000000000..3a45dcd2f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/RequestBuilder.java @@ -0,0 +1,351 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.methods; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.entity.UrlEncodedFormEntity; +import ch.boye.httpclientandroidlib.client.utils.URIBuilder; +import ch.boye.httpclientandroidlib.message.BasicHeader; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; +import ch.boye.httpclientandroidlib.message.HeaderGroup; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Builder for {@link HttpUriRequest} instances. + *

    + * Please note that this class treats parameters differently depending on composition + * of the request: if the request has a content entity explicitly set with + * {@link #setEntity(ch.boye.httpclientandroidlib.HttpEntity)} or it is not an entity enclosing method + * (such as POST or PUT), parameters will be added to the query component of the request URI. + * Otherwise, parameters will be added as a URL encoded {@link UrlEncodedFormEntity entity}. + * + * @since 4.3 + */ +@NotThreadSafe +public class RequestBuilder { + + private String method; + private ProtocolVersion version; + private URI uri; + private HeaderGroup headergroup; + private HttpEntity entity; + private LinkedList parameters; + private RequestConfig config; + + RequestBuilder(final String method) { + super(); + this.method = method; + } + + RequestBuilder() { + this(null); + } + + public static RequestBuilder create(final String method) { + Args.notBlank(method, "HTTP method"); + return new RequestBuilder(method); + } + + public static RequestBuilder get() { + return new RequestBuilder(HttpGet.METHOD_NAME); + } + + public static RequestBuilder head() { + return new RequestBuilder(HttpHead.METHOD_NAME); + } + + public static RequestBuilder post() { + return new RequestBuilder(HttpPost.METHOD_NAME); + } + + public static RequestBuilder put() { + return new RequestBuilder(HttpPut.METHOD_NAME); + } + + public static RequestBuilder delete() { + return new RequestBuilder(HttpDelete.METHOD_NAME); + } + + public static RequestBuilder trace() { + return new RequestBuilder(HttpTrace.METHOD_NAME); + } + + public static RequestBuilder options() { + return new RequestBuilder(HttpOptions.METHOD_NAME); + } + + public static RequestBuilder copy(final HttpRequest request) { + Args.notNull(request, "HTTP request"); + return new RequestBuilder().doCopy(request); + } + + private RequestBuilder doCopy(final HttpRequest request) { + if (request == null) { + return this; + } + method = request.getRequestLine().getMethod(); + version = request.getRequestLine().getProtocolVersion(); + if (request instanceof HttpUriRequest) { + uri = ((HttpUriRequest) request).getURI(); + } else { + uri = URI.create(request.getRequestLine().getUri()); + } + if (headergroup == null) { + headergroup = new HeaderGroup(); + } + headergroup.clear(); + headergroup.setHeaders(request.getAllHeaders()); + if (request instanceof HttpEntityEnclosingRequest) { + entity = ((HttpEntityEnclosingRequest) request).getEntity(); + } else { + entity = null; + } + if (request instanceof Configurable) { + this.config = ((Configurable) request).getConfig(); + } else { + this.config = null; + } + this.parameters = null; + return this; + } + + public String getMethod() { + return method; + } + + public ProtocolVersion getVersion() { + return version; + } + + public RequestBuilder setVersion(final ProtocolVersion version) { + this.version = version; + return this; + } + + public URI getUri() { + return uri; + } + + public RequestBuilder setUri(final URI uri) { + this.uri = uri; + return this; + } + + public RequestBuilder setUri(final String uri) { + this.uri = uri != null ? URI.create(uri) : null; + return this; + } + + public Header getFirstHeader(final String name) { + return headergroup != null ? headergroup.getFirstHeader(name) : null; + } + + public Header getLastHeader(final String name) { + return headergroup != null ? headergroup.getLastHeader(name) : null; + } + + public Header[] getHeaders(final String name) { + return headergroup != null ? headergroup.getHeaders(name) : null; + } + + public RequestBuilder addHeader(final Header header) { + if (headergroup == null) { + headergroup = new HeaderGroup(); + } + headergroup.addHeader(header); + return this; + } + + public RequestBuilder addHeader(final String name, final String value) { + if (headergroup == null) { + headergroup = new HeaderGroup(); + } + this.headergroup.addHeader(new BasicHeader(name, value)); + return this; + } + + public RequestBuilder removeHeader(final Header header) { + if (headergroup == null) { + headergroup = new HeaderGroup(); + } + headergroup.removeHeader(header); + return this; + } + + public RequestBuilder removeHeaders(final String name) { + if (name == null || headergroup == null) { + return this; + } + for (final HeaderIterator i = headergroup.iterator(); i.hasNext(); ) { + final Header header = i.nextHeader(); + if (name.equalsIgnoreCase(header.getName())) { + i.remove(); + } + } + return this; + } + + public RequestBuilder setHeader(final Header header) { + if (headergroup == null) { + headergroup = new HeaderGroup(); + } + this.headergroup.updateHeader(header); + return this; + } + + public RequestBuilder setHeader(final String name, final String value) { + if (headergroup == null) { + headergroup = new HeaderGroup(); + } + this.headergroup.updateHeader(new BasicHeader(name, value)); + return this; + } + + public HttpEntity getEntity() { + return entity; + } + + public RequestBuilder setEntity(final HttpEntity entity) { + this.entity = entity; + return this; + } + + public List getParameters() { + return parameters != null ? new ArrayList(parameters) : + new ArrayList(); + } + + public RequestBuilder addParameter(final NameValuePair nvp) { + Args.notNull(nvp, "Name value pair"); + if (parameters == null) { + parameters = new LinkedList(); + } + parameters.add(nvp); + return this; + } + + public RequestBuilder addParameter(final String name, final String value) { + return addParameter(new BasicNameValuePair(name, value)); + } + + public RequestBuilder addParameters(final NameValuePair... nvps) { + for (final NameValuePair nvp: nvps) { + addParameter(nvp); + } + return this; + } + + public RequestConfig getConfig() { + return config; + } + + public RequestBuilder setConfig(final RequestConfig config) { + this.config = config; + return this; + } + + public HttpUriRequest build() { + final HttpRequestBase result; + URI uri = this.uri != null ? this.uri : URI.create("/"); + HttpEntity entity = this.entity; + if (parameters != null && !parameters.isEmpty()) { + if (entity == null && (HttpPost.METHOD_NAME.equalsIgnoreCase(method) + || HttpPut.METHOD_NAME.equalsIgnoreCase(method))) { + entity = new UrlEncodedFormEntity(parameters, HTTP.DEF_CONTENT_CHARSET); + } else { + try { + uri = new URIBuilder(uri).addParameters(parameters).build(); + } catch (final URISyntaxException ex) { + // should never happen + } + } + } + if (entity == null) { + result = new InternalRequest(method); + } else { + final InternalEntityEclosingRequest request = new InternalEntityEclosingRequest(method); + request.setEntity(entity); + result = request; + } + result.setProtocolVersion(this.version); + result.setURI(uri); + if (this.headergroup != null) { + result.setHeaders(this.headergroup.getAllHeaders()); + } + result.setConfig(this.config); + return result; + } + + static class InternalRequest extends HttpRequestBase { + + private final String method; + + InternalRequest(final String method) { + super(); + this.method = method; + } + + @Override + public String getMethod() { + return this.method; + } + + } + + static class InternalEntityEclosingRequest extends HttpEntityEnclosingRequestBase { + + private final String method; + + InternalEntityEclosingRequest(final String method) { + super(); + this.method = method; + } + + @Override + public String getMethod() { + return this.method; + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/package-info.java new file mode 100644 index 000000000..d8266b2e3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/methods/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Standard HTTP method implementations. + */ +package ch.boye.httpclientandroidlib.client.methods; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/package-info.java new file mode 100644 index 000000000..2d49a2b99 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client HTTP communication APIs. + */ +package ch.boye.httpclientandroidlib.client; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AllClientPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AllClientPNames.java new file mode 100644 index 000000000..aaf591571 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AllClientPNames.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.params; + +import ch.boye.httpclientandroidlib.auth.params.AuthPNames; +import ch.boye.httpclientandroidlib.conn.params.ConnConnectionPNames; +import ch.boye.httpclientandroidlib.conn.params.ConnManagerPNames; +import ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames; +import ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.CoreProtocolPNames; + +/** + * Collected parameter names for the HttpClient module. + * This interface combines the parameter definitions of the HttpClient + * module and all dependency modules or informational units. + * It does not define additional parameter names, but references + * other interfaces defining parameter names. + *
    + * This interface is meant as a navigation aid for developers. + * When referring to parameter names, you should use the interfaces + * in which the respective constants are actually defined. + * + * @since 4.0 + * + * @deprecated (4.3) use + * {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}, + * {@link ch.boye.httpclientandroidlib.config.ConnectionConfig}, + * {@link ch.boye.httpclientandroidlib.config.SocketConfig} + */ +@Deprecated +public interface AllClientPNames extends + CoreConnectionPNames, CoreProtocolPNames, + ClientPNames, AuthPNames, CookieSpecPNames, + ConnConnectionPNames, ConnManagerPNames, ConnRoutePNames { + + // no additional definitions +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AuthPolicy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AuthPolicy.java new file mode 100644 index 000000000..b677ee02f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/AuthPolicy.java @@ -0,0 +1,79 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.params; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Standard authentication schemes supported by HttpClient. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.AuthSchemes}. + */ +@Deprecated +@Immutable +public final class AuthPolicy { + + private AuthPolicy() { + super(); + } + + /** + * The NTLM scheme is a proprietary Microsoft Windows Authentication + * protocol (considered to be the most secure among currently supported + * authentication schemes). + */ + public static final String NTLM = "NTLM"; + + /** + * Digest authentication scheme as defined in RFC2617. + */ + public static final String DIGEST = "Digest"; + + /** + * Basic authentication scheme as defined in RFC2617 (considered inherently + * insecure, but most widely supported) + */ + public static final String BASIC = "Basic"; + + /** + * SPNEGO Authentication scheme. + * + * @since 4.1 + */ + public static final String SPNEGO = "negotiate"; + + /** + * Kerberos Authentication scheme. + * + * @since 4.2 + */ + public static final String KERBEROS = "Kerberos"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientPNames.java new file mode 100644 index 000000000..796ee40fb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientPNames.java @@ -0,0 +1,133 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.params; + +/** + * Parameter names for HTTP client parameters. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}. + */ +@Deprecated +public interface ClientPNames { + + public static final String CONNECTION_MANAGER_FACTORY_CLASS_NAME = "http.connection-manager.factory-class-name"; + + /** + * Defines whether redirects should be handled automatically + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + */ + public static final String HANDLE_REDIRECTS = "http.protocol.handle-redirects"; + + /** + * Defines whether relative redirects should be rejected. HTTP specification + * requires the location value be an absolute URI. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + */ + public static final String REJECT_RELATIVE_REDIRECT = "http.protocol.reject-relative-redirect"; + + /** + * Defines the maximum number of redirects to be followed. + * The limit on number of redirects is intended to prevent infinite loops. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + */ + public static final String MAX_REDIRECTS = "http.protocol.max-redirects"; + + /** + * Defines whether circular redirects (redirects to the same location) should be allowed. + * The HTTP spec is not sufficiently clear whether circular redirects are permitted, + * therefore optionally they can be enabled + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + */ + public static final String ALLOW_CIRCULAR_REDIRECTS = "http.protocol.allow-circular-redirects"; + + /** + * Defines whether authentication should be handled automatically. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + */ + public static final String HANDLE_AUTHENTICATION = "http.protocol.handle-authentication"; + + /** + * Defines the name of the cookie specification to be used for HTTP state management. + *

    + * This parameter expects a value of type {@link String}. + *

    + */ + public static final String COOKIE_POLICY = "http.protocol.cookie-policy"; + + /** + * Defines the virtual host to be used in the Host + * request header instead of the physical host. + *

    + * This parameter expects a value of type {@link ch.boye.httpclientandroidlib.HttpHost}. + *

    + * If a port is not provided, it will be derived from the request URL. + */ + public static final String VIRTUAL_HOST = "http.virtual-host"; + + /** + * Defines the request headers to be sent per default with each request. + *

    + * This parameter expects a value of type {@link java.util.Collection}. The + * collection is expected to contain {@link ch.boye.httpclientandroidlib.Header}s. + *

    + */ + public static final String DEFAULT_HEADERS = "http.default-headers"; + + /** + * Defines the default host. The default value will be used if the target host is + * not explicitly specified in the request URI. + *

    + * This parameter expects a value of type {@link ch.boye.httpclientandroidlib.HttpHost}. + *

    + */ + public static final String DEFAULT_HOST = "http.default-host"; + + /** + * Defines the timeout in milliseconds used when retrieving an instance of + * {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection} from the + * {@link ch.boye.httpclientandroidlib.conn.ClientConnectionManager}. + *

    + * This parameter expects a value of type {@link Long}. + *

    + * @since 4.2 + */ + public static final String CONN_MANAGER_TIMEOUT = "http.conn-manager.timeout"; + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientParamBean.java new file mode 100644 index 000000000..315129d23 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/ClientParamBean.java @@ -0,0 +1,106 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.params; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.HttpAbstractParamBean; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate HTTP client parameters using + * Java Beans conventions. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}. + */ +@Deprecated +@NotThreadSafe +public class ClientParamBean extends HttpAbstractParamBean { + + public ClientParamBean (final HttpParams params) { + super(params); + } + + /** + * @deprecated (4.2) do not use. + */ + @Deprecated + public void setConnectionManagerFactoryClassName (final String factory) { + params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME, factory); + } + + public void setHandleRedirects (final boolean handle) { + params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, handle); + } + + public void setRejectRelativeRedirect (final boolean reject) { + params.setBooleanParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, reject); + } + + public void setMaxRedirects (final int maxRedirects) { + params.setIntParameter(ClientPNames.MAX_REDIRECTS, maxRedirects); + } + + public void setAllowCircularRedirects (final boolean allow) { + params.setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, allow); + } + + public void setHandleAuthentication (final boolean handle) { + params.setBooleanParameter(ClientPNames.HANDLE_AUTHENTICATION, handle); + } + + public void setCookiePolicy (final String policy) { + params.setParameter(ClientPNames.COOKIE_POLICY, policy); + } + + public void setVirtualHost (final HttpHost host) { + params.setParameter(ClientPNames.VIRTUAL_HOST, host); + } + + public void setDefaultHeaders (final Collection

    headers) { + params.setParameter(ClientPNames.DEFAULT_HEADERS, headers); + } + + public void setDefaultHost (final HttpHost host) { + params.setParameter(ClientPNames.DEFAULT_HOST, host); + } + + /** + * @since 4.2 + */ + public void setConnectionManagerTimeout(final long timeout) { + params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, timeout); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/CookiePolicy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/CookiePolicy.java new file mode 100644 index 000000000..5c6353b80 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/CookiePolicy.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.params; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Standard cookie specifications supported by HttpClient. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.CookieSpecs}. + */ +@Deprecated +@Immutable +public final class CookiePolicy { + + /** + * The policy that provides high degree of compatibilty + * with common cookie management of popular HTTP agents. + */ + public static final String BROWSER_COMPATIBILITY = "compatibility"; + + /** + * The Netscape cookie draft compliant policy. + */ + public static final String NETSCAPE = "netscape"; + + /** + * The RFC 2109 compliant policy. + */ + public static final String RFC_2109 = "rfc2109"; + + /** + * The RFC 2965 compliant policy. + */ + public static final String RFC_2965 = "rfc2965"; + + /** + * The default 'best match' policy. + */ + public static final String BEST_MATCH = "best-match"; + + /** + * The policy that ignores cookies. + * + * @since 4.1-beta1 + */ + public static final String IGNORE_COOKIES = "ignoreCookies"; + + private CookiePolicy() { + super(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParamConfig.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParamConfig.java new file mode 100644 index 000000000..515d92ca5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParamConfig.java @@ -0,0 +1,88 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.params; + +import java.net.InetAddress; +import java.util.Collection; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.auth.params.AuthPNames; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.CoreProtocolPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * @deprecated (4.3) provided for compatibility with {@link HttpParams}. Do not use. + * + * @since 4.3 + */ +@Deprecated +public final class HttpClientParamConfig { + + private HttpClientParamConfig() { + } + + @SuppressWarnings("unchecked") + public static RequestConfig getRequestConfig(final HttpParams params) { + return RequestConfig.custom() + .setSocketTimeout(params.getIntParameter( + CoreConnectionPNames.SO_TIMEOUT, 0)) + .setStaleConnectionCheckEnabled(params.getBooleanParameter( + CoreConnectionPNames.STALE_CONNECTION_CHECK, true)) + .setConnectTimeout(params.getIntParameter( + CoreConnectionPNames.CONNECTION_TIMEOUT, 0)) + .setExpectContinueEnabled(params.getBooleanParameter( + CoreProtocolPNames.USE_EXPECT_CONTINUE, false)) + .setProxy((HttpHost) params.getParameter( + ConnRoutePNames.DEFAULT_PROXY)) + .setLocalAddress((InetAddress) params.getParameter( + ConnRoutePNames.LOCAL_ADDRESS)) + .setProxyPreferredAuthSchemes((Collection) params.getParameter( + AuthPNames.PROXY_AUTH_PREF)) + .setTargetPreferredAuthSchemes((Collection) params.getParameter( + AuthPNames.TARGET_AUTH_PREF)) + .setAuthenticationEnabled(params.getBooleanParameter( + ClientPNames.HANDLE_AUTHENTICATION, true)) + .setCircularRedirectsAllowed(params.getBooleanParameter( + ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false)) + .setConnectionRequestTimeout((int) params.getLongParameter( + ClientPNames.CONN_MANAGER_TIMEOUT, 0)) + .setCookieSpec((String) params.getParameter( + ClientPNames.COOKIE_POLICY)) + .setMaxRedirects(params.getIntParameter( + ClientPNames.MAX_REDIRECTS, 50)) + .setRedirectsEnabled(params.getBooleanParameter( + ClientPNames.HANDLE_REDIRECTS, true)) + .setRelativeRedirectsAllowed(!params.getBooleanParameter( + ClientPNames.REJECT_RELATIVE_REDIRECT, false)) + .build(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParams.java new file mode 100644 index 000000000..b2b2a573e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/HttpClientParams.java @@ -0,0 +1,116 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.params; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * An adaptor for manipulating HTTP client parameters in {@link HttpParams}. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig} + */ +@Deprecated +@Immutable +public class HttpClientParams { + + private HttpClientParams() { + super(); + } + + public static boolean isRedirecting(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getBooleanParameter + (ClientPNames.HANDLE_REDIRECTS, true); + } + + public static void setRedirecting(final HttpParams params, final boolean value) { + Args.notNull(params, "HTTP parameters"); + params.setBooleanParameter + (ClientPNames.HANDLE_REDIRECTS, value); + } + + public static boolean isAuthenticating(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getBooleanParameter + (ClientPNames.HANDLE_AUTHENTICATION, true); + } + + public static void setAuthenticating(final HttpParams params, final boolean value) { + Args.notNull(params, "HTTP parameters"); + params.setBooleanParameter + (ClientPNames.HANDLE_AUTHENTICATION, value); + } + + public static String getCookiePolicy(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + final String cookiePolicy = (String) + params.getParameter(ClientPNames.COOKIE_POLICY); + if (cookiePolicy == null) { + return CookiePolicy.BEST_MATCH; + } + return cookiePolicy; + } + + public static void setCookiePolicy(final HttpParams params, final String cookiePolicy) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(ClientPNames.COOKIE_POLICY, cookiePolicy); + } + + /** + * Set the parameter {@code ClientPNames.CONN_MANAGER_TIMEOUT}. + * + * @since 4.2 + */ + public static void setConnectionManagerTimeout(final HttpParams params, final long timeout) { + Args.notNull(params, "HTTP parameters"); + params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, timeout); + } + + /** + * Get the connectiion manager timeout value. + * This is defined by the parameter {@code ClientPNames.CONN_MANAGER_TIMEOUT}. + * Failing that it uses the parameter {@code CoreConnectionPNames.CONNECTION_TIMEOUT} + * which defaults to 0 if not defined. + * + * @since 4.2 + * @return the timeout value + */ + public static long getConnectionManagerTimeout(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + final Long timeout = (Long) params.getParameter(ClientPNames.CONN_MANAGER_TIMEOUT); + if (timeout != null) { + return timeout.longValue(); + } + return HttpConnectionParams.getConnectionTimeout(params); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/package-info.java new file mode 100644 index 000000000..3450f368e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/params/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Deprecated. + * @deprecated (4.3). + */ +package ch.boye.httpclientandroidlib.client.params; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContext.java new file mode 100644 index 000000000..1409362f0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContext.java @@ -0,0 +1,132 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +/** + * {@link ch.boye.httpclientandroidlib.protocol.HttpContext} attribute names for + * client side HTTP protocol processing. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link HttpClientContext}. + */ +@Deprecated +public interface ClientContext { + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.conn.routing.RouteInfo} + * object that represents the actual connection route. + * + * @since 4.3 + */ + public static final String ROUTE = "http.route"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.conn.scheme.Scheme} + * object that represents the actual protocol scheme registry. + */ + @Deprecated + public static final String SCHEME_REGISTRY = "http.scheme-registry"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.config.Lookup} object that represents + * the actual {@link ch.boye.httpclientandroidlib.cookie.CookieSpecRegistry} registry. + */ + public static final String COOKIESPEC_REGISTRY = "http.cookiespec-registry"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.cookie.CookieSpec} + * object that represents the actual cookie specification. + */ + public static final String COOKIE_SPEC = "http.cookie-spec"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.cookie.CookieOrigin} + * object that represents the actual details of the origin server. + */ + public static final String COOKIE_ORIGIN = "http.cookie-origin"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.CookieStore} + * object that represents the actual cookie store. + */ + public static final String COOKIE_STORE = "http.cookie-store"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.CredentialsProvider} + * object that represents the actual credentials provider. + */ + public static final String CREDS_PROVIDER = "http.auth.credentials-provider"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.AuthCache} object + * that represents the auth scheme cache. + */ + public static final String AUTH_CACHE = "http.auth.auth-cache"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.auth.AuthState} + * object that represents the actual target authentication state. + */ + public static final String TARGET_AUTH_STATE = "http.auth.target-scope"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.auth.AuthState} + * object that represents the actual proxy authentication state. + */ + public static final String PROXY_AUTH_STATE = "http.auth.proxy-scope"; + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + public static final String AUTH_SCHEME_PREF = "http.auth.scheme-pref"; + + /** + * Attribute name of a {@link java.lang.Object} object that represents + * the actual user identity such as user {@link java.security.Principal}. + */ + public static final String USER_TOKEN = "http.user-token"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.config.Lookup} object that represents + * the actual {@link ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry} registry. + */ + public static final String AUTHSCHEME_REGISTRY = "http.authscheme-registry"; + + public static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.config.RequestConfig} object that + * represents the actual request configuration. + * + * @since 4.3 + */ + public static final String REQUEST_CONFIG = "http.request-config"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContextConfigurer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContextConfigurer.java new file mode 100644 index 000000000..29628a83d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ClientContextConfigurer.java @@ -0,0 +1,72 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.cookie.CookieSpecRegistry; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Configuration facade for {@link HttpContext} instances. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link HttpClientContext} + */ +@NotThreadSafe +@Deprecated +public class ClientContextConfigurer implements ClientContext { + + private final HttpContext context; + + public ClientContextConfigurer (final HttpContext context) { + Args.notNull(context, "HTTP context"); + this.context = context; + } + + public void setCookieSpecRegistry(final CookieSpecRegistry registry) { + this.context.setAttribute(COOKIESPEC_REGISTRY, registry); + } + + public void setAuthSchemeRegistry(final AuthSchemeRegistry registry) { + this.context.setAttribute(AUTHSCHEME_REGISTRY, registry); + } + + public void setCookieStore(final CookieStore store) { + this.context.setAttribute(COOKIE_STORE, store); + } + + public void setCredentialsProvider(final CredentialsProvider provider) { + this.context.setAttribute(CREDS_PROVIDER, provider); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/HttpClientContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/HttpClientContext.java new file mode 100644 index 000000000..7b73e8d64 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/HttpClientContext.java @@ -0,0 +1,249 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.net.URI; +import java.util.List; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.client.AuthCache; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteInfo; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpCoreContext; + +/** + * Adaptor class that provides convenience type safe setters and getters + * for common {@link HttpContext} attributes used in the course + * of HTTP request execution. + * + * @since 4.3 + */ +@NotThreadSafe +public class HttpClientContext extends HttpCoreContext { + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.conn.routing.RouteInfo} + * object that represents the actual connection route. + */ + public static final String HTTP_ROUTE = "http.route"; + + /** + * Attribute name of a {@link List} object that represents a collection of all + * redirect locations received in the process of request execution. + */ + public static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.config.Lookup} object that represents + * the actual {@link CookieSpecProvider} registry. + */ + public static final String COOKIESPEC_REGISTRY = "http.cookiespec-registry"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.cookie.CookieSpec} + * object that represents the actual cookie specification. + */ + public static final String COOKIE_SPEC = "http.cookie-spec"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.cookie.CookieOrigin} + * object that represents the actual details of the origin server. + */ + public static final String COOKIE_ORIGIN = "http.cookie-origin"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.CookieStore} + * object that represents the actual cookie store. + */ + public static final String COOKIE_STORE = "http.cookie-store"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.CredentialsProvider} + * object that represents the actual credentials provider. + */ + public static final String CREDS_PROVIDER = "http.auth.credentials-provider"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.AuthCache} object + * that represents the auth scheme cache. + */ + public static final String AUTH_CACHE = "http.auth.auth-cache"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.auth.AuthState} + * object that represents the actual target authentication state. + */ + public static final String TARGET_AUTH_STATE = "http.auth.target-scope"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.auth.AuthState} + * object that represents the actual proxy authentication state. + */ + public static final String PROXY_AUTH_STATE = "http.auth.proxy-scope"; + + /** + * Attribute name of a {@link java.lang.Object} object that represents + * the actual user identity such as user {@link java.security.Principal}. + */ + public static final String USER_TOKEN = "http.user-token"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.config.Lookup} object that represents + * the actual {@link AuthSchemeProvider} registry. + */ + public static final String AUTHSCHEME_REGISTRY = "http.authscheme-registry"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.client.config.RequestConfig} object that + * represents the actual request configuration. + */ + public static final String REQUEST_CONFIG = "http.request-config"; + + public static HttpClientContext adapt(final HttpContext context) { + if (context instanceof HttpClientContext) { + return (HttpClientContext) context; + } else { + return new HttpClientContext(context); + } + } + + public static HttpClientContext create() { + return new HttpClientContext(new BasicHttpContext()); + } + + public HttpClientContext(final HttpContext context) { + super(context); + } + + public HttpClientContext() { + super(); + } + + public RouteInfo getHttpRoute() { + return getAttribute(HTTP_ROUTE, HttpRoute.class); + } + + @SuppressWarnings("unchecked") + public List getRedirectLocations() { + return getAttribute(REDIRECT_LOCATIONS, List.class); + } + + public CookieStore getCookieStore() { + return getAttribute(COOKIE_STORE, CookieStore.class); + } + + public void setCookieStore(final CookieStore cookieStore) { + setAttribute(COOKIE_STORE, cookieStore); + } + + public CookieSpec getCookieSpec() { + return getAttribute(COOKIE_SPEC, CookieSpec.class); + } + + public CookieOrigin getCookieOrigin() { + return getAttribute(COOKIE_ORIGIN, CookieOrigin.class); + } + + @SuppressWarnings("unchecked") + private Lookup getLookup(final String name, final Class clazz) { + return getAttribute(name, Lookup.class); + } + + public Lookup getCookieSpecRegistry() { + return getLookup(COOKIESPEC_REGISTRY, CookieSpecProvider.class); + } + + public void setCookieSpecRegistry(final Lookup lookup) { + setAttribute(COOKIESPEC_REGISTRY, lookup); + } + + public Lookup getAuthSchemeRegistry() { + return getLookup(AUTHSCHEME_REGISTRY, AuthSchemeProvider.class); + } + + public void setAuthSchemeRegistry(final Lookup lookup) { + setAttribute(AUTHSCHEME_REGISTRY, lookup); + } + + public CredentialsProvider getCredentialsProvider() { + return getAttribute(CREDS_PROVIDER, CredentialsProvider.class); + } + + public void setCredentialsProvider(final CredentialsProvider credentialsProvider) { + setAttribute(CREDS_PROVIDER, credentialsProvider); + } + + public AuthCache getAuthCache() { + return getAttribute(AUTH_CACHE, AuthCache.class); + } + + public void setAuthCache(final AuthCache authCache) { + setAttribute(AUTH_CACHE, authCache); + } + + public AuthState getTargetAuthState() { + return getAttribute(TARGET_AUTH_STATE, AuthState.class); + } + + public AuthState getProxyAuthState() { + return getAttribute(PROXY_AUTH_STATE, AuthState.class); + } + + public T getUserToken(final Class clazz) { + return getAttribute(USER_TOKEN, clazz); + } + + public Object getUserToken() { + return getAttribute(USER_TOKEN); + } + + public void setUserToken(final Object obj) { + setAttribute(USER_TOKEN, obj); + } + + public RequestConfig getRequestConfig() { + final RequestConfig config = getAttribute(REQUEST_CONFIG, RequestConfig.class); + return config != null ? config : RequestConfig.DEFAULT; + } + + public void setRequestConfig(final RequestConfig config) { + setAttribute(REQUEST_CONFIG, config); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAcceptEncoding.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAcceptEncoding.java new file mode 100644 index 000000000..1c0d642d3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAcceptEncoding.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Class responsible for handling Content Encoding requests in HTTP. + *

    + * Instances of this class are stateless, therefore they're thread-safe and immutable. + * + * @see "http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5" + * + * @since 4.1 + */ +@Immutable +public class RequestAcceptEncoding implements HttpRequestInterceptor { + + /** + * Adds the header {@code "Accept-Encoding: gzip,deflate"} to the request. + */ + public void process( + final HttpRequest request, + final HttpContext context) throws HttpException, IOException { + + /* Signal support for Accept-Encoding transfer encodings. */ + if (!request.containsHeader("Accept-Encoding")) { + request.addHeader("Accept-Encoding", "gzip,deflate"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAddCookies.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAddCookies.java new file mode 100644 index 000000000..ad2768043 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAddCookies.java @@ -0,0 +1,204 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.client.config.CookieSpecs; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.conn.routing.RouteInfo; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.cookie.SetCookie2; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.TextUtils; + +/** + * Request interceptor that matches cookies available in the current + * {@link CookieStore} to the request being executed and generates + * corresponding Cookie request headers. + * + * @since 4.0 + */ +@Immutable +public class RequestAddCookies implements HttpRequestInterceptor { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public RequestAddCookies() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase("CONNECT")) { + return; + } + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + // Obtain cookie store + final CookieStore cookieStore = clientContext.getCookieStore(); + if (cookieStore == null) { + this.log.debug("Cookie store not specified in HTTP context"); + return; + } + + // Obtain the registry of cookie specs + final Lookup registry = clientContext.getCookieSpecRegistry(); + if (registry == null) { + this.log.debug("CookieSpec registry not specified in HTTP context"); + return; + } + + // Obtain the target host, possibly virtual (required) + final HttpHost targetHost = clientContext.getTargetHost(); + if (targetHost == null) { + this.log.debug("Target host not set in the context"); + return; + } + + // Obtain the route (required) + final RouteInfo route = clientContext.getHttpRoute(); + if (route == null) { + this.log.debug("Connection route not set in the context"); + return; + } + + final RequestConfig config = clientContext.getRequestConfig(); + String policy = config.getCookieSpec(); + if (policy == null) { + policy = CookieSpecs.BEST_MATCH; + } + if (this.log.isDebugEnabled()) { + this.log.debug("CookieSpec selected: " + policy); + } + + URI requestURI = null; + if (request instanceof HttpUriRequest) { + requestURI = ((HttpUriRequest) request).getURI(); + } else { + try { + requestURI = new URI(request.getRequestLine().getUri()); + } catch (final URISyntaxException ignore) { + } + } + final String path = requestURI != null ? requestURI.getPath() : null; + final String hostName = targetHost.getHostName(); + int port = targetHost.getPort(); + if (port < 0) { + port = route.getTargetHost().getPort(); + } + + final CookieOrigin cookieOrigin = new CookieOrigin( + hostName, + port >= 0 ? port : 0, + !TextUtils.isEmpty(path) ? path : "/", + route.isSecure()); + + // Get an instance of the selected cookie policy + final CookieSpecProvider provider = registry.lookup(policy); + if (provider == null) { + throw new HttpException("Unsupported cookie policy: " + policy); + } + final CookieSpec cookieSpec = provider.create(clientContext); + // Get all cookies available in the HTTP state + final List cookies = new ArrayList(cookieStore.getCookies()); + // Find cookies matching the given origin + final List matchedCookies = new ArrayList(); + final Date now = new Date(); + for (final Cookie cookie : cookies) { + if (!cookie.isExpired(now)) { + if (cookieSpec.match(cookie, cookieOrigin)) { + if (this.log.isDebugEnabled()) { + this.log.debug("Cookie " + cookie + " match " + cookieOrigin); + } + matchedCookies.add(cookie); + } + } else { + if (this.log.isDebugEnabled()) { + this.log.debug("Cookie " + cookie + " expired"); + } + } + } + // Generate Cookie request headers + if (!matchedCookies.isEmpty()) { + final List

    headers = cookieSpec.formatCookies(matchedCookies); + for (final Header header : headers) { + request.addHeader(header); + } + } + + final int ver = cookieSpec.getVersion(); + if (ver > 0) { + boolean needVersionHeader = false; + for (final Cookie cookie : matchedCookies) { + if (ver != cookie.getVersion() || !(cookie instanceof SetCookie2)) { + needVersionHeader = true; + } + } + + if (needVersionHeader) { + final Header header = cookieSpec.getVersionHeader(); + if (header != null) { + // Advertise cookie version support + request.addHeader(header); + } + } + } + + // Stick the CookieSpec and CookieOrigin instances to the HTTP context + // so they could be obtained by the response interceptor + context.setAttribute(HttpClientContext.COOKIE_SPEC, cookieSpec); + context.setAttribute(HttpClientContext.COOKIE_ORIGIN, cookieOrigin); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthCache.java new file mode 100644 index 000000000..08102cfc8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthCache.java @@ -0,0 +1,147 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthProtocolState; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.client.AuthCache; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.conn.routing.RouteInfo; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Request interceptor that can preemptively authenticate against known hosts, + * if there is a cached {@link AuthScheme} instance in the local + * {@link AuthCache} associated with the given target or proxy host. + * + * @since 4.1 + */ +@Immutable +public class RequestAuthCache implements HttpRequestInterceptor { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public RequestAuthCache() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + final AuthCache authCache = clientContext.getAuthCache(); + if (authCache == null) { + this.log.debug("Auth cache not set in the context"); + return; + } + + final CredentialsProvider credsProvider = clientContext.getCredentialsProvider(); + if (credsProvider == null) { + this.log.debug("Credentials provider not set in the context"); + return; + } + + final RouteInfo route = clientContext.getHttpRoute(); + if (route == null) { + this.log.debug("Route info not set in the context"); + return; + } + + HttpHost target = clientContext.getTargetHost(); + if (target == null) { + this.log.debug("Target host not set in the context"); + return; + } + + if (target.getPort() < 0) { + target = new HttpHost( + target.getHostName(), + route.getTargetHost().getPort(), + target.getSchemeName()); + } + + final AuthState targetState = clientContext.getTargetAuthState(); + if (targetState != null && targetState.getState() == AuthProtocolState.UNCHALLENGED) { + final AuthScheme authScheme = authCache.get(target); + if (authScheme != null) { + doPreemptiveAuth(target, authScheme, targetState, credsProvider); + } + } + + final HttpHost proxy = route.getProxyHost(); + final AuthState proxyState = clientContext.getProxyAuthState(); + if (proxy != null && proxyState != null && proxyState.getState() == AuthProtocolState.UNCHALLENGED) { + final AuthScheme authScheme = authCache.get(proxy); + if (authScheme != null) { + doPreemptiveAuth(proxy, authScheme, proxyState, credsProvider); + } + } + } + + private void doPreemptiveAuth( + final HttpHost host, + final AuthScheme authScheme, + final AuthState authState, + final CredentialsProvider credsProvider) { + final String schemeName = authScheme.getSchemeName(); + if (this.log.isDebugEnabled()) { + this.log.debug("Re-using cached '" + schemeName + "' auth scheme for " + host); + } + + final AuthScope authScope = new AuthScope(host, AuthScope.ANY_REALM, schemeName); + final Credentials creds = credsProvider.getCredentials(authScope); + + if (creds != null) { + if ("BASIC".equalsIgnoreCase(authScheme.getSchemeName())) { + authState.setState(AuthProtocolState.CHALLENGED); + } else { + authState.setState(AuthProtocolState.SUCCESS); + } + authState.update(authScheme, creds); + } else { + this.log.debug("No credentials for preemptive authentication"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthenticationBase.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthenticationBase.java new file mode 100644 index 000000000..19e07054b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestAuthenticationBase.java @@ -0,0 +1,126 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.util.Queue; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.auth.AuthOption; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Asserts; + +@Deprecated +abstract class RequestAuthenticationBase implements HttpRequestInterceptor { + + final HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public RequestAuthenticationBase() { + super(); + } + + void process( + final AuthState authState, + final HttpRequest request, + final HttpContext context) { + AuthScheme authScheme = authState.getAuthScheme(); + Credentials creds = authState.getCredentials(); + switch (authState.getState()) { + case FAILURE: + return; + case SUCCESS: + ensureAuthScheme(authScheme); + if (authScheme.isConnectionBased()) { + return; + } + break; + case CHALLENGED: + final Queue authOptions = authState.getAuthOptions(); + if (authOptions != null) { + while (!authOptions.isEmpty()) { + final AuthOption authOption = authOptions.remove(); + authScheme = authOption.getAuthScheme(); + creds = authOption.getCredentials(); + authState.update(authScheme, creds); + if (this.log.isDebugEnabled()) { + this.log.debug("Generating response to an authentication challenge using " + + authScheme.getSchemeName() + " scheme"); + } + try { + final Header header = authenticate(authScheme, creds, request, context); + request.addHeader(header); + break; + } catch (final AuthenticationException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn(authScheme + " authentication error: " + ex.getMessage()); + } + } + } + return; + } else { + ensureAuthScheme(authScheme); + } + } + if (authScheme != null) { + try { + final Header header = authenticate(authScheme, creds, request, context); + request.addHeader(header); + } catch (final AuthenticationException ex) { + if (this.log.isErrorEnabled()) { + this.log.error(authScheme + " authentication error: " + ex.getMessage()); + } + } + } + } + + private void ensureAuthScheme(final AuthScheme authScheme) { + Asserts.notNull(authScheme, "Auth scheme"); + } + + private Header authenticate( + final AuthScheme authScheme, + final Credentials creds, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + Asserts.notNull(authScheme, "Auth scheme"); + if (authScheme instanceof ContextAwareAuthScheme) { + return ((ContextAwareAuthScheme) authScheme).authenticate(creds, request, context); + } else { + return authScheme.authenticate(creds, request); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestClientConnControl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestClientConnControl.java new file mode 100644 index 000000000..714b2859b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestClientConnControl.java @@ -0,0 +1,92 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.routing.RouteInfo; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * This protocol interceptor is responsible for adding Connection + * or Proxy-Connection headers to the outgoing requests, which + * is essential for managing persistence of HTTP/1.0 connections. + * + * @since 4.0 + */ +@Immutable +public class RequestClientConnControl implements HttpRequestInterceptor { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private static final String PROXY_CONN_DIRECTIVE = "Proxy-Connection"; + + public RequestClientConnControl() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase("CONNECT")) { + request.setHeader(PROXY_CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE); + return; + } + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + // Obtain the client connection (required) + final RouteInfo route = clientContext.getHttpRoute(); + if (route == null) { + this.log.debug("Connection route not set in the context"); + return; + } + + if (route.getHopCount() == 1 || route.isTunnelled()) { + if (!request.containsHeader(HTTP.CONN_DIRECTIVE)) { + request.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE); + } + } + if (route.getHopCount() == 2 && !route.isTunnelled()) { + if (!request.containsHeader(PROXY_CONN_DIRECTIVE)) { + request.addHeader(PROXY_CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestDefaultHeaders.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestDefaultHeaders.java new file mode 100644 index 000000000..c4414f1f4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestDefaultHeaders.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; +import java.util.Collection; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.params.ClientPNames; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Request interceptor that adds default request headers. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@Immutable +public class RequestDefaultHeaders implements HttpRequestInterceptor { + + private final Collection defaultHeaders; + + /** + * @since 4.3 + */ + public RequestDefaultHeaders(final Collection defaultHeaders) { + super(); + this.defaultHeaders = defaultHeaders; + } + + public RequestDefaultHeaders() { + this(null); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase("CONNECT")) { + return; + } + + // Add default headers + @SuppressWarnings("unchecked") + Collection defHeaders = (Collection) + request.getParams().getParameter(ClientPNames.DEFAULT_HEADERS); + if (defHeaders == null) { + defHeaders = this.defaultHeaders; + } + + if (defHeaders != null) { + for (final Header defHeader : defHeaders) { + request.addHeader(defHeader); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestExpectContinue.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestExpectContinue.java new file mode 100644 index 000000000..237004f0f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestExpectContinue.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * RequestExpectContinue is responsible for enabling the 'expect-continue' + * handshake by adding Expect header. + *

    + * This interceptor takes into account {@link RequestConfig#isExpectContinueEnabled()} + * setting. + * + * @since 4.3 + */ +@Immutable +public class RequestExpectContinue implements HttpRequestInterceptor { + + public RequestExpectContinue() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + + if (!request.containsHeader(HTTP.EXPECT_DIRECTIVE)) { + if (request instanceof HttpEntityEnclosingRequest) { + final ProtocolVersion ver = request.getRequestLine().getProtocolVersion(); + final HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity(); + // Do not send the expect header if request body is known to be empty + if (entity != null + && entity.getContentLength() != 0 && !ver.lessEquals(HttpVersion.HTTP_1_0)) { + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final RequestConfig config = clientContext.getRequestConfig(); + if (config.isExpectContinueEnabled()) { + request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE); + } + } + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestProxyAuthentication.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestProxyAuthentication.java new file mode 100644 index 000000000..e67468ff8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestProxyAuthentication.java @@ -0,0 +1,92 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.conn.HttpRoutedConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.protocol.ExecutionContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Generates authentication header for the proxy host, if required, + * based on the actual state of the HTTP authentication context. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator}. + */ +@Deprecated +@Immutable +public class RequestProxyAuthentication extends RequestAuthenticationBase { + + public RequestProxyAuthentication() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + if (request.containsHeader(AUTH.PROXY_AUTH_RESP)) { + return; + } + + final HttpRoutedConnection conn = (HttpRoutedConnection) context.getAttribute( + ExecutionContext.HTTP_CONNECTION); + if (conn == null) { + this.log.debug("HTTP connection not set in the context"); + return; + } + final HttpRoute route = conn.getRoute(); + if (route.isTunnelled()) { + return; + } + + // Obtain authentication state + final AuthState authState = (AuthState) context.getAttribute( + ClientContext.PROXY_AUTH_STATE); + if (authState == null) { + this.log.debug("Proxy auth state not set in the context"); + return; + } + if (this.log.isDebugEnabled()) { + this.log.debug("Proxy auth state: " + authState.getState()); + } + process(authState, request, context); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestTargetAuthentication.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestTargetAuthentication.java new file mode 100644 index 000000000..dd5c52597 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/RequestTargetAuthentication.java @@ -0,0 +1,83 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Generates authentication header for the target host, if required, + * based on the actual state of the HTTP authentication context. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator}. + */ +@Deprecated +@Immutable +public class RequestTargetAuthentication extends RequestAuthenticationBase { + + public RequestTargetAuthentication() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase("CONNECT")) { + return; + } + + if (request.containsHeader(AUTH.WWW_AUTH_RESP)) { + return; + } + + // Obtain authentication state + final AuthState authState = (AuthState) context.getAttribute( + ClientContext.TARGET_AUTH_STATE); + if (authState == null) { + this.log.debug("Target auth state not set in the context"); + return; + } + if (this.log.isDebugEnabled()) { + this.log.debug("Target auth state: " + authState.getState()); + } + process(authState, request, context); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseAuthCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseAuthCache.java new file mode 100644 index 000000000..fc51012f6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseAuthCache.java @@ -0,0 +1,151 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.client.AuthCache; +import ch.boye.httpclientandroidlib.client.params.AuthPolicy; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.impl.client.BasicAuthCache; +import ch.boye.httpclientandroidlib.protocol.ExecutionContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Response interceptor that adds successfully completed {@link AuthScheme}s + * to the local {@link AuthCache} instance. Cached {@link AuthScheme}s can be + * re-used when executing requests against known hosts, thus avoiding + * additional authentication round-trips. + * + * @since 4.1 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.client.AuthenticationStrategy} + */ +@Immutable +@Deprecated +public class ResponseAuthCache implements HttpResponseInterceptor { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public ResponseAuthCache() { + super(); + } + + public void process(final HttpResponse response, final HttpContext context) + throws HttpException, IOException { + Args.notNull(response, "HTTP request"); + Args.notNull(context, "HTTP context"); + AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + + HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); + final AuthState targetState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); + if (target != null && targetState != null) { + if (this.log.isDebugEnabled()) { + this.log.debug("Target auth state: " + targetState.getState()); + } + if (isCachable(targetState)) { + final SchemeRegistry schemeRegistry = (SchemeRegistry) context.getAttribute( + ClientContext.SCHEME_REGISTRY); + if (target.getPort() < 0) { + final Scheme scheme = schemeRegistry.getScheme(target); + target = new HttpHost(target.getHostName(), + scheme.resolvePort(target.getPort()), target.getSchemeName()); + } + if (authCache == null) { + authCache = new BasicAuthCache(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + } + switch (targetState.getState()) { + case CHALLENGED: + cache(authCache, target, targetState.getAuthScheme()); + break; + case FAILURE: + uncache(authCache, target, targetState.getAuthScheme()); + } + } + } + + final HttpHost proxy = (HttpHost) context.getAttribute(ExecutionContext.HTTP_PROXY_HOST); + final AuthState proxyState = (AuthState) context.getAttribute(ClientContext.PROXY_AUTH_STATE); + if (proxy != null && proxyState != null) { + if (this.log.isDebugEnabled()) { + this.log.debug("Proxy auth state: " + proxyState.getState()); + } + if (isCachable(proxyState)) { + if (authCache == null) { + authCache = new BasicAuthCache(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + } + switch (proxyState.getState()) { + case CHALLENGED: + cache(authCache, proxy, proxyState.getAuthScheme()); + break; + case FAILURE: + uncache(authCache, proxy, proxyState.getAuthScheme()); + } + } + } + } + + private boolean isCachable(final AuthState authState) { + final AuthScheme authScheme = authState.getAuthScheme(); + if (authScheme == null || !authScheme.isComplete()) { + return false; + } + final String schemeName = authScheme.getSchemeName(); + return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) || + schemeName.equalsIgnoreCase(AuthPolicy.DIGEST); + } + + private void cache(final AuthCache authCache, final HttpHost host, final AuthScheme authScheme) { + if (this.log.isDebugEnabled()) { + this.log.debug("Caching '" + authScheme.getSchemeName() + + "' auth scheme for " + host); + } + authCache.put(host, authScheme); + } + + private void uncache(final AuthCache authCache, final HttpHost host, final AuthScheme authScheme) { + if (this.log.isDebugEnabled()) { + this.log.debug("Removing from cache '" + authScheme.getSchemeName() + + "' auth scheme for " + host); + } + authCache.remove(host); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseContentEncoding.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseContentEncoding.java new file mode 100644 index 000000000..df3ee4457 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseContentEncoding.java @@ -0,0 +1,110 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.entity.DeflateDecompressingEntity; +import ch.boye.httpclientandroidlib.client.entity.GzipDecompressingEntity; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link HttpResponseInterceptor} responsible for processing Content-Encoding + * responses. + *

    + * Instances of this class are stateless and immutable, therefore threadsafe. + * + * @since 4.1 + * + */ +@Immutable +public class ResponseContentEncoding implements HttpResponseInterceptor { + + public static final String UNCOMPRESSED = "http.client.response.uncompressed"; + + /** + * Handles the following {@code Content-Encoding}s by + * using the appropriate decompressor to wrap the response Entity: + *

      + *
    • gzip - see {@link GzipDecompressingEntity}
    • + *
    • deflate - see {@link DeflateDecompressingEntity}
    • + *
    • identity - no action needed
    • + *
    + * + * @param response the response which contains the entity + * @param context not currently used + * + * @throws HttpException if the {@code Content-Encoding} is none of the above + */ + public void process( + final HttpResponse response, + final HttpContext context) throws HttpException, IOException { + final HttpEntity entity = response.getEntity(); + + // entity can be null in case of 304 Not Modified, 204 No Content or similar + // check for zero length entity. + if (entity != null && entity.getContentLength() != 0) { + final Header ceheader = entity.getContentEncoding(); + if (ceheader != null) { + final HeaderElement[] codecs = ceheader.getElements(); + boolean uncompressed = false; + for (final HeaderElement codec : codecs) { + final String codecname = codec.getName().toLowerCase(Locale.ENGLISH); + if ("gzip".equals(codecname) || "x-gzip".equals(codecname)) { + response.setEntity(new GzipDecompressingEntity(response.getEntity())); + uncompressed = true; + break; + } else if ("deflate".equals(codecname)) { + response.setEntity(new DeflateDecompressingEntity(response.getEntity())); + uncompressed = true; + break; + } else if ("identity".equals(codecname)) { + + /* Don't need to transform the content - no-op */ + return; + } else { + throw new HttpException("Unsupported Content-Coding: " + codec.getName()); + } + } + if (uncompressed) { + response.removeHeaders("Content-Length"); + response.removeHeaders("Content-Encoding"); + response.removeHeaders("Content-MD5"); + } + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseProcessCookies.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseProcessCookies.java new file mode 100644 index 000000000..06827bfdc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/ResponseProcessCookies.java @@ -0,0 +1,156 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.protocol; + +import java.io.IOException; +import java.util.List; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SM; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Response interceptor that populates the current {@link CookieStore} with data + * contained in response cookies received in the given the HTTP response. + * + * @since 4.0 + */ +@Immutable +public class ResponseProcessCookies implements HttpResponseInterceptor { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public ResponseProcessCookies() { + super(); + } + + public void process(final HttpResponse response, final HttpContext context) + throws HttpException, IOException { + Args.notNull(response, "HTTP request"); + Args.notNull(context, "HTTP context"); + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + // Obtain actual CookieSpec instance + final CookieSpec cookieSpec = clientContext.getCookieSpec(); + if (cookieSpec == null) { + this.log.debug("Cookie spec not specified in HTTP context"); + return; + } + // Obtain cookie store + final CookieStore cookieStore = clientContext.getCookieStore(); + if (cookieStore == null) { + this.log.debug("Cookie store not specified in HTTP context"); + return; + } + // Obtain actual CookieOrigin instance + final CookieOrigin cookieOrigin = clientContext.getCookieOrigin(); + if (cookieOrigin == null) { + this.log.debug("Cookie origin not specified in HTTP context"); + return; + } + HeaderIterator it = response.headerIterator(SM.SET_COOKIE); + processCookies(it, cookieSpec, cookieOrigin, cookieStore); + + // see if the cookie spec supports cookie versioning. + if (cookieSpec.getVersion() > 0) { + // process set-cookie2 headers. + // Cookie2 will replace equivalent Cookie instances + it = response.headerIterator(SM.SET_COOKIE2); + processCookies(it, cookieSpec, cookieOrigin, cookieStore); + } + } + + private void processCookies( + final HeaderIterator iterator, + final CookieSpec cookieSpec, + final CookieOrigin cookieOrigin, + final CookieStore cookieStore) { + while (iterator.hasNext()) { + final Header header = iterator.nextHeader(); + try { + final List cookies = cookieSpec.parse(header, cookieOrigin); + for (final Cookie cookie : cookies) { + try { + cookieSpec.validate(cookie, cookieOrigin); + cookieStore.addCookie(cookie); + + if (this.log.isDebugEnabled()) { + this.log.debug("Cookie accepted [" + formatCooke(cookie) + "]"); + } + } catch (final MalformedCookieException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn("Cookie rejected [" + formatCooke(cookie) + "] " + + ex.getMessage()); + } + } + } + } catch (final MalformedCookieException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn("Invalid cookie header: \"" + + header + "\". " + ex.getMessage()); + } + } + } + } + + private static String formatCooke(final Cookie cookie) { + final StringBuilder buf = new StringBuilder(); + buf.append(cookie.getName()); + buf.append("=\""); + String v = cookie.getValue(); + if (v.length() > 100) { + v = v.substring(0, 100) + "..."; + } + buf.append(v); + buf.append("\""); + buf.append(", version:"); + buf.append(Integer.toString(cookie.getVersion())); + buf.append(", domain:"); + buf.append(cookie.getDomain()); + buf.append(", path:"); + buf.append(cookie.getPath()); + buf.append(", expiry:"); + buf.append(cookie.getExpiryDate()); + return buf.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/package-info.java new file mode 100644 index 000000000..a585e614d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/protocol/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client specific HTTP protocol handlers. + */ +package ch.boye.httpclientandroidlib.client.protocol; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/CloneUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/CloneUtils.java new file mode 100644 index 000000000..67f70ee07 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/CloneUtils.java @@ -0,0 +1,86 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * A collection of utilities to workaround limitations of Java clone framework. + * + * @since 4.0 + */ +@Immutable +public class CloneUtils { + + /** + * @since 4.3 + */ + public static T cloneObject(final T obj) throws CloneNotSupportedException { + if (obj == null) { + return null; + } + if (obj instanceof Cloneable) { + final Class clazz = obj.getClass (); + final Method m; + try { + m = clazz.getMethod("clone", (Class[]) null); + } catch (final NoSuchMethodException ex) { + throw new NoSuchMethodError(ex.getMessage()); + } + try { + @SuppressWarnings("unchecked") // OK because clone() preserves the class + final T result = (T) m.invoke(obj, (Object []) null); + return result; + } catch (final InvocationTargetException ex) { + final Throwable cause = ex.getCause(); + if (cause instanceof CloneNotSupportedException) { + throw ((CloneNotSupportedException) cause); + } else { + throw new Error("Unexpected exception", cause); + } + } catch (final IllegalAccessException ex) { + throw new IllegalAccessError(ex.getMessage()); + } + } else { + throw new CloneNotSupportedException(); + } + } + + public static Object clone(final Object obj) throws CloneNotSupportedException { + return cloneObject(obj); + } + + /** + * This class should not be instantiated. + */ + private CloneUtils() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/DateUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/DateUtils.java new file mode 100644 index 000000000..e337923d3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/DateUtils.java @@ -0,0 +1,250 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.utils; + +import java.lang.ref.SoftReference; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A utility class for parsing and formatting HTTP dates as used in cookies and + * other headers. This class handles dates as defined by RFC 2616 section + * 3.3.1 as well as some other common non-standard formats. + * + * @since 4.3 + */ +@Immutable +public final class DateUtils { + + /** + * Date format pattern used to parse HTTP date headers in RFC 1123 format. + */ + public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; + + /** + * Date format pattern used to parse HTTP date headers in RFC 1036 format. + */ + public static final String PATTERN_RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz"; + + /** + * Date format pattern used to parse HTTP date headers in ANSI C + * asctime() format. + */ + public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy"; + + private static final String[] DEFAULT_PATTERNS = new String[] { + PATTERN_RFC1123, + PATTERN_RFC1036, + PATTERN_ASCTIME + }; + + private static final Date DEFAULT_TWO_DIGIT_YEAR_START; + + public static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + + static { + final Calendar calendar = Calendar.getInstance(); + calendar.setTimeZone(GMT); + calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime(); + } + + /** + * Parses a date value. The formats used for parsing the date value are retrieved from + * the default http params. + * + * @param dateValue the date value to parse + * + * @return the parsed date or null if input could not be parsed + */ + public static Date parseDate(final String dateValue) { + return parseDate(dateValue, null, null); + } + + /** + * Parses the date value using the given date formats. + * + * @param dateValue the date value to parse + * @param dateFormats the date formats to use + * + * @return the parsed date or null if input could not be parsed + */ + public static Date parseDate(final String dateValue, final String[] dateFormats) { + return parseDate(dateValue, dateFormats, null); + } + + /** + * Parses the date value using the given date formats. + * + * @param dateValue the date value to parse + * @param dateFormats the date formats to use + * @param startDate During parsing, two digit years will be placed in the range + * startDate to startDate + 100 years. This value may + * be null. When null is given as a parameter, year + * 2000 will be used. + * + * @return the parsed date or null if input could not be parsed + */ + public static Date parseDate( + final String dateValue, + final String[] dateFormats, + final Date startDate) { + Args.notNull(dateValue, "Date value"); + final String[] localDateFormats = dateFormats != null ? dateFormats : DEFAULT_PATTERNS; + final Date localStartDate = startDate != null ? startDate : DEFAULT_TWO_DIGIT_YEAR_START; + String v = dateValue; + // trim single quotes around date if present + // see issue #5279 + if (v.length() > 1 && v.startsWith("'") && v.endsWith("'")) { + v = v.substring (1, v.length() - 1); + } + + for (final String dateFormat : localDateFormats) { + final SimpleDateFormat dateParser = DateFormatHolder.formatFor(dateFormat); + dateParser.set2DigitYearStart(localStartDate); + final ParsePosition pos = new ParsePosition(0); + final Date result = dateParser.parse(v, pos); + if (pos.getIndex() != 0) { + return result; + } + } + return null; + } + + /** + * Formats the given date according to the RFC 1123 pattern. + * + * @param date The date to format. + * @return An RFC 1123 formatted date string. + * + * @see #PATTERN_RFC1123 + */ + public static String formatDate(final Date date) { + return formatDate(date, PATTERN_RFC1123); + } + + /** + * Formats the given date according to the specified pattern. The pattern + * must conform to that used by the {@link SimpleDateFormat simple date + * format} class. + * + * @param date The date to format. + * @param pattern The pattern to use for formatting the date. + * @return A formatted date string. + * + * @throws IllegalArgumentException If the given date pattern is invalid. + * + * @see SimpleDateFormat + */ + public static String formatDate(final Date date, final String pattern) { + Args.notNull(date, "Date"); + Args.notNull(pattern, "Pattern"); + final SimpleDateFormat formatter = DateFormatHolder.formatFor(pattern); + return formatter.format(date); + } + + /** + * Clears thread-local variable containing {@link java.text.DateFormat} cache. + * + * @since 4.3 + */ + public static void clearThreadLocal() { + DateFormatHolder.clearThreadLocal(); + } + + /** This class should not be instantiated. */ + private DateUtils() { + } + + /** + * A factory for {@link SimpleDateFormat}s. The instances are stored in a + * threadlocal way because SimpleDateFormat is not threadsafe as noted in + * {@link SimpleDateFormat its javadoc}. + * + */ + final static class DateFormatHolder { + + private static final ThreadLocal>> + THREADLOCAL_FORMATS = new ThreadLocal>>() { + + @Override + protected SoftReference> initialValue() { + return new SoftReference>( + new HashMap()); + } + + }; + + /** + * creates a {@link SimpleDateFormat} for the requested format string. + * + * @param pattern + * a non-null format String according to + * {@link SimpleDateFormat}. The format is not checked against + * null since all paths go through + * {@link DateUtils}. + * @return the requested format. This simple dateformat should not be used + * to {@link SimpleDateFormat#applyPattern(String) apply} to a + * different pattern. + */ + public static SimpleDateFormat formatFor(final String pattern) { + final SoftReference> ref = THREADLOCAL_FORMATS.get(); + Map formats = ref.get(); + if (formats == null) { + formats = new HashMap(); + THREADLOCAL_FORMATS.set( + new SoftReference>(formats)); + } + + SimpleDateFormat format = formats.get(pattern); + if (format == null) { + format = new SimpleDateFormat(pattern, Locale.US); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + formats.put(pattern, format); + } + + return format; + } + + public static void clearThreadLocal() { + THREADLOCAL_FORMATS.remove(); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/HttpClientUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/HttpClientUtils.java new file mode 100644 index 000000000..3c7769c2a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/HttpClientUtils.java @@ -0,0 +1,149 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +import java.io.Closeable; +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * Convenience methods for closing response and client objects. + * + * @since 4.2 + */ +public class HttpClientUtils { + + private HttpClientUtils() { + } + + /** + * Unconditionally close a response. + *

    + * Example Code: + * + *

    +     * HttpResponse httpResponse = null;
    +     * try {
    +     *     httpResponse = httpClient.execute(httpGet);
    +     * } catch (Exception e) {
    +     *     // error handling
    +     * } finally {
    +     *     HttpClientUtils.closeQuietly(httpResponse);
    +     * }
    +     * 
    + * + * @param response + * the HttpResponse to release resources, may be null or already + * closed. + * + * @since 4.2 + */ + public static void closeQuietly(final HttpResponse response) { + if (response != null) { + final HttpEntity entity = response.getEntity(); + if (entity != null) { + try { + EntityUtils.consume(entity); + } catch (final IOException ex) { + } + } + } + } + + /** + * Unconditionally close a response. + *

    + * Example Code: + * + *

    +     * HttpResponse httpResponse = null;
    +     * try {
    +     *     httpResponse = httpClient.execute(httpGet);
    +     * } catch (Exception e) {
    +     *     // error handling
    +     * } finally {
    +     *     HttpClientUtils.closeQuietly(httpResponse);
    +     * }
    +     * 
    + * + * @param response + * the HttpResponse to release resources, may be null or already + * closed. + * + * @since 4.3 + */ + public static void closeQuietly(final CloseableHttpResponse response) { + if (response != null) { + try { + try { + EntityUtils.consume(response.getEntity()); + } finally { + response.close(); + } + } catch (final IOException ignore) { + } + } + } + + /** + * Unconditionally close a httpClient. Shuts down the underlying connection + * manager and releases the resources. + *

    + * Example Code: + * + *

    +     * HttpClient httpClient = HttpClients.createDefault();
    +     * try {
    +     *   httpClient.execute(request);
    +     * } catch (Exception e) {
    +     *   // error handling
    +     * } finally {
    +     *   HttpClientUtils.closeQuietly(httpClient);
    +     * }
    +     * 
    + * + * @param httpClient + * the HttpClient to close, may be null or already closed. + * @since 4.2 + */ + public static void closeQuietly(final HttpClient httpClient) { + if (httpClient != null) { + if (httpClient instanceof Closeable) { + try { + ((Closeable) httpClient).close(); + } catch (final IOException ignore) { + } + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Idn.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Idn.java new file mode 100644 index 000000000..a2b5bd036 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Idn.java @@ -0,0 +1,42 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +/** + * Abstraction of international domain name (IDN) conversion. + * + * @since 4.0 + */ +public interface Idn { + /** + * Converts a name from its punycode representation to Unicode. + * The name may be a single hostname or a dot-separated qualified domain name. + * @param punycode the Punycode representation + * @return the Unicode domain name + */ + String toUnicode(String punycode); +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/JdkIdn.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/JdkIdn.java new file mode 100644 index 000000000..53d46fe23 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/JdkIdn.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Uses the java.net.IDN class through reflection. + * + * @since 4.0 + */ +@Immutable +public class JdkIdn implements Idn { + private final Method toUnicode; + + /** + * + * @throws ClassNotFoundException if java.net.IDN is not available + */ + public JdkIdn() throws ClassNotFoundException { + final Class clazz = Class.forName("java.net.IDN"); + try { + toUnicode = clazz.getMethod("toUnicode", String.class); + } catch (final SecurityException e) { + // doesn't happen + throw new IllegalStateException(e.getMessage(), e); + } catch (final NoSuchMethodException e) { + // doesn't happen + throw new IllegalStateException(e.getMessage(), e); + } + } + + public String toUnicode(final String punycode) { + try { + return (String) toUnicode.invoke(null, punycode); + } catch (final IllegalAccessException e) { + throw new IllegalStateException(e.getMessage(), e); + } catch (final InvocationTargetException e) { + final Throwable t = e.getCause(); + throw new RuntimeException(t.getMessage(), t); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Punycode.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Punycode.java new file mode 100644 index 000000000..fa4872c41 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Punycode.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Facade that provides conversion between Unicode and Punycode domain names. + * It will use an appropriate implementation. + * + * @since 4.0 + */ +@Immutable +public class Punycode { + private static final Idn impl; + static { + Idn _impl; + try { + _impl = new JdkIdn(); + } catch (final Exception e) { + _impl = new Rfc3492Idn(); + } + impl = _impl; + } + + public static String toUnicode(final String punycode) { + return impl.toUnicode(punycode); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Rfc3492Idn.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Rfc3492Idn.java new file mode 100644 index 000000000..ccf980050 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/Rfc3492Idn.java @@ -0,0 +1,141 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +import java.util.StringTokenizer; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Implementation from pseudo code in RFC 3492. + * + * @since 4.0 + */ +@Immutable +public class Rfc3492Idn implements Idn { + private static final int base = 36; + private static final int tmin = 1; + private static final int tmax = 26; + private static final int skew = 38; + private static final int damp = 700; + private static final int initial_bias = 72; + private static final int initial_n = 128; + private static final char delimiter = '-'; + private static final String ACE_PREFIX = "xn--"; + + private int adapt(final int delta, final int numpoints, final boolean firsttime) { + int d = delta; + if (firsttime) { + d = d / damp; + } else { + d = d / 2; + } + d = d + (d / numpoints); + int k = 0; + while (d > ((base - tmin) * tmax) / 2) { + d = d / (base - tmin); + k = k + base; + } + return k + (((base - tmin + 1) * d) / (d + skew)); + } + + private int digit(final char c) { + if ((c >= 'A') && (c <= 'Z')) { + return (c - 'A'); + } + if ((c >= 'a') && (c <= 'z')) { + return (c - 'a'); + } + if ((c >= '0') && (c <= '9')) { + return (c - '0') + 26; + } + throw new IllegalArgumentException("illegal digit: "+ c); + } + + public String toUnicode(final String punycode) { + final StringBuilder unicode = new StringBuilder(punycode.length()); + final StringTokenizer tok = new StringTokenizer(punycode, "."); + while (tok.hasMoreTokens()) { + String t = tok.nextToken(); + if (unicode.length() > 0) { + unicode.append('.'); + } + if (t.startsWith(ACE_PREFIX)) { + t = decode(t.substring(4)); + } + unicode.append(t); + } + return unicode.toString(); + } + + protected String decode(final String s) { + String input = s; + int n = initial_n; + int i = 0; + int bias = initial_bias; + final StringBuilder output = new StringBuilder(input.length()); + final int lastdelim = input.lastIndexOf(delimiter); + if (lastdelim != -1) { + output.append(input.subSequence(0, lastdelim)); + input = input.substring(lastdelim + 1); + } + + while (input.length() > 0) { + final int oldi = i; + int w = 1; + for (int k = base;; k += base) { + if (input.length() == 0) { + break; + } + final char c = input.charAt(0); + input = input.substring(1); + final int digit = digit(c); + i = i + digit * w; // FIXME fail on overflow + final int t; + if (k <= bias + tmin) { + t = tmin; + } else if (k >= bias + tmax) { + t = tmax; + } else { + t = k - bias; + } + if (digit < t) { + break; + } + w = w * (base - t); // FIXME fail on overflow + } + bias = adapt(i - oldi, output.length() + 1, (oldi == 0)); + n = n + i / (output.length() + 1); // FIXME fail on overflow + i = i % (output.length() + 1); + // {if n is a basic code point then fail} + output.insert(i, (char) n); + i++; + } + return output.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIBuilder.java new file mode 100644 index 000000000..e41958f7f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIBuilder.java @@ -0,0 +1,490 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.util.InetAddressUtils; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; + +/** + * Builder for {@link URI} instances. + * + * @since 4.2 + */ +@NotThreadSafe +public class URIBuilder { + + private String scheme; + private String encodedSchemeSpecificPart; + private String encodedAuthority; + private String userInfo; + private String encodedUserInfo; + private String host; + private int port; + private String path; + private String encodedPath; + private String encodedQuery; + private List queryParams; + private String query; + private String fragment; + private String encodedFragment; + + /** + * Constructs an empty instance. + */ + public URIBuilder() { + super(); + this.port = -1; + } + + /** + * Construct an instance from the string which must be a valid URI. + * + * @param string a valid URI in string form + * @throws URISyntaxException if the input is not a valid URI + */ + public URIBuilder(final String string) throws URISyntaxException { + super(); + digestURI(new URI(string)); + } + + /** + * Construct an instance from the provided URI. + * @param uri + */ + public URIBuilder(final URI uri) { + super(); + digestURI(uri); + } + + private List parseQuery(final String query, final Charset charset) { + if (query != null && query.length() > 0) { + return URLEncodedUtils.parse(query, charset); + } + return null; + } + + /** + * Builds a {@link URI} instance. + */ + public URI build() throws URISyntaxException { + return new URI(buildString()); + } + + private String buildString() { + final StringBuilder sb = new StringBuilder(); + if (this.scheme != null) { + sb.append(this.scheme).append(':'); + } + if (this.encodedSchemeSpecificPart != null) { + sb.append(this.encodedSchemeSpecificPart); + } else { + if (this.encodedAuthority != null) { + sb.append("//").append(this.encodedAuthority); + } else if (this.host != null) { + sb.append("//"); + if (this.encodedUserInfo != null) { + sb.append(this.encodedUserInfo).append("@"); + } else if (this.userInfo != null) { + sb.append(encodeUserInfo(this.userInfo)).append("@"); + } + if (InetAddressUtils.isIPv6Address(this.host)) { + sb.append("[").append(this.host).append("]"); + } else { + sb.append(this.host); + } + if (this.port >= 0) { + sb.append(":").append(this.port); + } + } + if (this.encodedPath != null) { + sb.append(normalizePath(this.encodedPath)); + } else if (this.path != null) { + sb.append(encodePath(normalizePath(this.path))); + } + if (this.encodedQuery != null) { + sb.append("?").append(this.encodedQuery); + } else if (this.queryParams != null) { + sb.append("?").append(encodeUrlForm(this.queryParams)); + } else if (this.query != null) { + sb.append("?").append(encodeUric(this.query)); + } + } + if (this.encodedFragment != null) { + sb.append("#").append(this.encodedFragment); + } else if (this.fragment != null) { + sb.append("#").append(encodeUric(this.fragment)); + } + return sb.toString(); + } + + private void digestURI(final URI uri) { + this.scheme = uri.getScheme(); + this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart(); + this.encodedAuthority = uri.getRawAuthority(); + this.host = uri.getHost(); + this.port = uri.getPort(); + this.encodedUserInfo = uri.getRawUserInfo(); + this.userInfo = uri.getUserInfo(); + this.encodedPath = uri.getRawPath(); + this.path = uri.getPath(); + this.encodedQuery = uri.getRawQuery(); + this.queryParams = parseQuery(uri.getRawQuery(), Consts.UTF_8); + this.encodedFragment = uri.getRawFragment(); + this.fragment = uri.getFragment(); + } + + private String encodeUserInfo(final String userInfo) { + return URLEncodedUtils.encUserInfo(userInfo, Consts.UTF_8); + } + + private String encodePath(final String path) { + return URLEncodedUtils.encPath(path, Consts.UTF_8); + } + + private String encodeUrlForm(final List params) { + return URLEncodedUtils.format(params, Consts.UTF_8); + } + + private String encodeUric(final String fragment) { + return URLEncodedUtils.encUric(fragment, Consts.UTF_8); + } + + /** + * Sets URI scheme. + */ + public URIBuilder setScheme(final String scheme) { + this.scheme = scheme; + return this; + } + + /** + * Sets URI user info. The value is expected to be unescaped and may contain non ASCII + * characters. + */ + public URIBuilder setUserInfo(final String userInfo) { + this.userInfo = userInfo; + this.encodedSchemeSpecificPart = null; + this.encodedAuthority = null; + this.encodedUserInfo = null; + return this; + } + + /** + * Sets URI user info as a combination of username and password. These values are expected to + * be unescaped and may contain non ASCII characters. + */ + public URIBuilder setUserInfo(final String username, final String password) { + return setUserInfo(username + ':' + password); + } + + /** + * Sets URI host. + */ + public URIBuilder setHost(final String host) { + this.host = host; + this.encodedSchemeSpecificPart = null; + this.encodedAuthority = null; + return this; + } + + /** + * Sets URI port. + */ + public URIBuilder setPort(final int port) { + this.port = port < 0 ? -1 : port; + this.encodedSchemeSpecificPart = null; + this.encodedAuthority = null; + return this; + } + + /** + * Sets URI path. The value is expected to be unescaped and may contain non ASCII characters. + */ + public URIBuilder setPath(final String path) { + this.path = path; + this.encodedSchemeSpecificPart = null; + this.encodedPath = null; + return this; + } + + /** + * Removes URI query. + */ + public URIBuilder removeQuery() { + this.queryParams = null; + this.query = null; + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + return this; + } + + /** + * Sets URI query. + *

    + * The value is expected to be encoded form data. + * + * @deprecated (4.3) use {@link #setParameters(List)} or {@link #setParameters(NameValuePair...)} + * + * @see URLEncodedUtils#parse + */ + @Deprecated + public URIBuilder setQuery(final String query) { + this.queryParams = parseQuery(query, Consts.UTF_8); + this.query = null; + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + return this; + } + + /** + * Sets URI query parameters. The parameter name / values are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + * + * @since 4.3 + */ + public URIBuilder setParameters(final List nvps) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } else { + this.queryParams.clear(); + } + this.queryParams.addAll(nvps); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Adds URI query parameters. The parameter name / values are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + * + * @since 4.3 + */ + public URIBuilder addParameters(final List nvps) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } + this.queryParams.addAll(nvps); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Sets URI query parameters. The parameter name / values are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + * + * @since 4.3 + */ + public URIBuilder setParameters(final NameValuePair... nvps) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } else { + this.queryParams.clear(); + } + for (final NameValuePair nvp: nvps) { + this.queryParams.add(nvp); + } + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Adds parameter to URI query. The parameter name and value are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + */ + public URIBuilder addParameter(final String param, final String value) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } + this.queryParams.add(new BasicNameValuePair(param, value)); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Sets parameter of URI query overriding existing value if set. The parameter name and value + * are expected to be unescaped and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + */ + public URIBuilder setParameter(final String param, final String value) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } + if (!this.queryParams.isEmpty()) { + for (final Iterator it = this.queryParams.iterator(); it.hasNext(); ) { + final NameValuePair nvp = it.next(); + if (nvp.getName().equals(param)) { + it.remove(); + } + } + } + this.queryParams.add(new BasicNameValuePair(param, value)); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Clears URI query parameters. + * + * @since 4.3 + */ + public URIBuilder clearParameters() { + this.queryParams = null; + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + return this; + } + + /** + * Sets custom URI query. The value is expected to be unescaped and may contain non ASCII + * characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove query parameters if present. + * + * @since 4.3 + */ + public URIBuilder setCustomQuery(final String query) { + this.query = query; + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.queryParams = null; + return this; + } + + /** + * Sets URI fragment. The value is expected to be unescaped and may contain non ASCII + * characters. + */ + public URIBuilder setFragment(final String fragment) { + this.fragment = fragment; + this.encodedFragment = null; + return this; + } + + /** + * @since 4.3 + */ + public boolean isAbsolute() { + return this.scheme != null; + } + + /** + * @since 4.3 + */ + public boolean isOpaque() { + return this.path == null; + } + + public String getScheme() { + return this.scheme; + } + + public String getUserInfo() { + return this.userInfo; + } + + public String getHost() { + return this.host; + } + + public int getPort() { + return this.port; + } + + public String getPath() { + return this.path; + } + + public List getQueryParams() { + if (this.queryParams != null) { + return new ArrayList(this.queryParams); + } else { + return new ArrayList(); + } + } + + public String getFragment() { + return this.fragment; + } + + @Override + public String toString() { + return buildString(); + } + + private static String normalizePath(final String path) { + String s = path; + if (s == null) { + return null; + } + int n = 0; + for (; n < s.length(); n++) { + if (s.charAt(n) != '/') { + break; + } + } + if (n > 1) { + s = s.substring(n - 1); + } + return s; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIUtils.java new file mode 100644 index 000000000..73619c90a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URIUtils.java @@ -0,0 +1,428 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.client.utils; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Locale; +import java.util.Stack; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.TextUtils; + +/** + * A collection of utilities for {@link URI URIs}, to workaround + * bugs within the class or for ease-of-use features. + * + * @since 4.0 + */ +@Immutable +public class URIUtils { + + /** + * Constructs a {@link URI} using all the parameters. This should be + * used instead of + * {@link URI#URI(String, String, String, int, String, String, String)} + * or any of the other URI multi-argument URI constructors. + * + * @param scheme + * Scheme name + * @param host + * Host name + * @param port + * Port number + * @param path + * Path + * @param query + * Query + * @param fragment + * Fragment + * + * @throws URISyntaxException + * If both a scheme and a path are given but the path is + * relative, if the URI string constructed from the given + * components violates RFC 2396, or if the authority + * component of the string is present but cannot be parsed + * as a server-based authority + * + * @deprecated (4.2) use {@link URIBuilder}. + */ + @Deprecated + public static URI createURI( + final String scheme, + final String host, + final int port, + final String path, + final String query, + final String fragment) throws URISyntaxException { + final StringBuilder buffer = new StringBuilder(); + if (host != null) { + if (scheme != null) { + buffer.append(scheme); + buffer.append("://"); + } + buffer.append(host); + if (port > 0) { + buffer.append(':'); + buffer.append(port); + } + } + if (path == null || !path.startsWith("/")) { + buffer.append('/'); + } + if (path != null) { + buffer.append(path); + } + if (query != null) { + buffer.append('?'); + buffer.append(query); + } + if (fragment != null) { + buffer.append('#'); + buffer.append(fragment); + } + return new URI(buffer.toString()); + } + + /** + * A convenience method for creating a new {@link URI} whose scheme, host + * and port are taken from the target host, but whose path, query and + * fragment are taken from the existing URI. The fragment is only used if + * dropFragment is false. The path is set to "/" if not explicitly specified. + * + * @param uri + * Contains the path, query and fragment to use. + * @param target + * Contains the scheme, host and port to use. + * @param dropFragment + * True if the fragment should not be copied. + * + * @throws URISyntaxException + * If the resulting URI is invalid. + */ + public static URI rewriteURI( + final URI uri, + final HttpHost target, + final boolean dropFragment) throws URISyntaxException { + Args.notNull(uri, "URI"); + if (uri.isOpaque()) { + return uri; + } + final URIBuilder uribuilder = new URIBuilder(uri); + if (target != null) { + uribuilder.setScheme(target.getSchemeName()); + uribuilder.setHost(target.getHostName()); + uribuilder.setPort(target.getPort()); + } else { + uribuilder.setScheme(null); + uribuilder.setHost(null); + uribuilder.setPort(-1); + } + if (dropFragment) { + uribuilder.setFragment(null); + } + if (TextUtils.isEmpty(uribuilder.getPath())) { + uribuilder.setPath("/"); + } + return uribuilder.build(); + } + + /** + * A convenience method for + * {@link URIUtils#rewriteURI(URI, HttpHost, boolean)} that always keeps the + * fragment. + */ + public static URI rewriteURI( + final URI uri, + final HttpHost target) throws URISyntaxException { + return rewriteURI(uri, target, false); + } + + /** + * A convenience method that creates a new {@link URI} whose scheme, host, port, path, + * query are taken from the existing URI, dropping any fragment or user-information. + * The path is set to "/" if not explicitly specified. The existing URI is returned + * unmodified if it has no fragment or user-information and has a path. + * + * @param uri + * original URI. + * @throws URISyntaxException + * If the resulting URI is invalid. + */ + public static URI rewriteURI(final URI uri) throws URISyntaxException { + Args.notNull(uri, "URI"); + if (uri.isOpaque()) { + return uri; + } + final URIBuilder uribuilder = new URIBuilder(uri); + if (uribuilder.getUserInfo() != null) { + uribuilder.setUserInfo(null); + } + if (TextUtils.isEmpty(uribuilder.getPath())) { + uribuilder.setPath("/"); + } + if (uribuilder.getHost() != null) { + uribuilder.setHost(uribuilder.getHost().toLowerCase(Locale.ENGLISH)); + } + uribuilder.setFragment(null); + return uribuilder.build(); + } + + /** + * Resolves a URI reference against a base URI. Work-around for bug in + * java.net.URI () + * + * @param baseURI the base URI + * @param reference the URI reference + * @return the resulting URI + */ + public static URI resolve(final URI baseURI, final String reference) { + return URIUtils.resolve(baseURI, URI.create(reference)); + } + + /** + * Resolves a URI reference against a base URI. Work-around for bugs in + * java.net.URI (e.g. ) + * + * @param baseURI the base URI + * @param reference the URI reference + * @return the resulting URI + */ + public static URI resolve(final URI baseURI, final URI reference){ + Args.notNull(baseURI, "Base URI"); + Args.notNull(reference, "Reference URI"); + URI ref = reference; + final String s = ref.toString(); + if (s.startsWith("?")) { + return resolveReferenceStartingWithQueryString(baseURI, ref); + } + final boolean emptyReference = s.length() == 0; + if (emptyReference) { + ref = URI.create("#"); + } + URI resolved = baseURI.resolve(ref); + if (emptyReference) { + final String resolvedString = resolved.toString(); + resolved = URI.create(resolvedString.substring(0, + resolvedString.indexOf('#'))); + } + return normalizeSyntax(resolved); + } + + /** + * Resolves a reference starting with a query string. + * + * @param baseURI the base URI + * @param reference the URI reference starting with a query string + * @return the resulting URI + */ + private static URI resolveReferenceStartingWithQueryString( + final URI baseURI, final URI reference) { + String baseUri = baseURI.toString(); + baseUri = baseUri.indexOf('?') > -1 ? + baseUri.substring(0, baseUri.indexOf('?')) : baseUri; + return URI.create(baseUri + reference.toString()); + } + + /** + * Removes dot segments according to RFC 3986, section 5.2.4 and + * Syntax-Based Normalization according to RFC 3986, section 6.2.2. + * + * @param uri the original URI + * @return the URI without dot segments + */ + private static URI normalizeSyntax(final URI uri) { + if (uri.isOpaque() || uri.getAuthority() == null) { + // opaque and file: URIs + return uri; + } + Args.check(uri.isAbsolute(), "Base URI must be absolute"); + final String path = uri.getPath() == null ? "" : uri.getPath(); + final String[] inputSegments = path.split("/"); + final Stack outputSegments = new Stack(); + for (final String inputSegment : inputSegments) { + if ((inputSegment.length() == 0) + || (".".equals(inputSegment))) { + // Do nothing + } else if ("..".equals(inputSegment)) { + if (!outputSegments.isEmpty()) { + outputSegments.pop(); + } + } else { + outputSegments.push(inputSegment); + } + } + final StringBuilder outputBuffer = new StringBuilder(); + for (final String outputSegment : outputSegments) { + outputBuffer.append('/').append(outputSegment); + } + if (path.lastIndexOf('/') == path.length() - 1) { + // path.endsWith("/") || path.equals("") + outputBuffer.append('/'); + } + try { + final String scheme = uri.getScheme().toLowerCase(Locale.ENGLISH); + final String auth = uri.getAuthority().toLowerCase(Locale.ENGLISH); + final URI ref = new URI(scheme, auth, outputBuffer.toString(), + null, null); + if (uri.getQuery() == null && uri.getFragment() == null) { + return ref; + } + final StringBuilder normalized = new StringBuilder( + ref.toASCIIString()); + if (uri.getQuery() != null) { + // query string passed through unchanged + normalized.append('?').append(uri.getRawQuery()); + } + if (uri.getFragment() != null) { + // fragment passed through unchanged + normalized.append('#').append(uri.getRawFragment()); + } + return URI.create(normalized.toString()); + } catch (final URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Extracts target host from the given {@link URI}. + * + * @param uri + * @return the target host if the URI is absolute or null if the URI is + * relative or does not contain a valid host name. + * + * @since 4.1 + */ + public static HttpHost extractHost(final URI uri) { + if (uri == null) { + return null; + } + HttpHost target = null; + if (uri.isAbsolute()) { + int port = uri.getPort(); // may be overridden later + String host = uri.getHost(); + if (host == null) { // normal parse failed; let's do it ourselves + // authority does not seem to care about the valid character-set for host names + host = uri.getAuthority(); + if (host != null) { + // Strip off any leading user credentials + final int at = host.indexOf('@'); + if (at >= 0) { + if (host.length() > at+1 ) { + host = host.substring(at+1); + } else { + host = null; // @ on its own + } + } + // Extract the port suffix, if present + if (host != null) { + final int colon = host.indexOf(':'); + if (colon >= 0) { + final int pos = colon + 1; + int len = 0; + for (int i = pos; i < host.length(); i++) { + if (Character.isDigit(host.charAt(i))) { + len++; + } else { + break; + } + } + if (len > 0) { + try { + port = Integer.parseInt(host.substring(pos, pos + len)); + } catch (final NumberFormatException ex) { + } + } + host = host.substring(0, colon); + } + } + } + } + final String scheme = uri.getScheme(); + if (!TextUtils.isBlank(host)) { + target = new HttpHost(host, port, scheme); + } + } + return target; + } + + /** + * Derives the interpreted (absolute) URI that was used to generate the last + * request. This is done by extracting the request-uri and target origin for + * the last request and scanning all the redirect locations for the last + * fragment identifier, then combining the result into a {@link URI}. + * + * @param originalURI + * original request before any redirects + * @param target + * if the last URI is relative, it is resolved against this target, + * or null if not available. + * @param redirects + * collection of redirect locations since the original request + * or null if not available. + * @return interpreted (absolute) URI + */ + public static URI resolve( + final URI originalURI, + final HttpHost target, + final List redirects) throws URISyntaxException { + Args.notNull(originalURI, "Request URI"); + final URIBuilder uribuilder; + if (redirects == null || redirects.isEmpty()) { + uribuilder = new URIBuilder(originalURI); + } else { + uribuilder = new URIBuilder(redirects.get(redirects.size() - 1)); + String frag = uribuilder.getFragment(); + // read interpreted fragment identifier from redirect locations + for (int i = redirects.size() - 1; frag == null && i >= 0; i--) { + frag = redirects.get(i).getFragment(); + } + uribuilder.setFragment(frag); + } + // read interpreted fragment identifier from original request + if (uribuilder.getFragment() == null) { + uribuilder.setFragment(originalURI.getFragment()); + } + // last target origin + if (target != null && !uribuilder.isAbsolute()) { + uribuilder.setScheme(target.getSchemeName()); + uribuilder.setHost(target.getHostName()); + uribuilder.setPort(target.getPort()); + } + return uribuilder.build(); + } + + /** + * This class should not be instantiated. + */ + private URIUtils() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URLEncodedUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URLEncodedUtils.java new file mode 100644 index 000000000..97465401f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/URLEncodedUtils.java @@ -0,0 +1,628 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.client.utils; + +import java.io.IOException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.List; +import java.util.Scanner; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueParser; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * A collection of utilities for encoding URLs. + * + * @since 4.0 + */ +@Immutable +public class URLEncodedUtils { + + /** + * The default HTML form content type. + */ + public static final String CONTENT_TYPE = "application/x-www-form-urlencoded"; + + private static final char QP_SEP_A = '&'; + private static final char QP_SEP_S = ';'; + private static final String NAME_VALUE_SEPARATOR = "="; + + /** + * Returns a list of {@link NameValuePair NameValuePairs} as built from the URI's query portion. For example, a URI + * of http://example.org/path/to/file?a=1&b=2&c=3 would return a list of three NameValuePairs, one for a=1, one for + * b=2, and one for c=3. By convention, {@code '&'} and {@code ';'} are accepted as parameter separators. + *

    + * This is typically useful while parsing an HTTP PUT. + * + * This API is currently only used for testing. + * + * @param uri + * URI to parse + * @param charset + * Charset name to use while parsing the query + * @return a list of {@link NameValuePair} as built from the URI's query portion. + */ + public static List parse(final URI uri, final String charset) { + final String query = uri.getRawQuery(); + if (query != null && query.length() > 0) { + final List result = new ArrayList(); + final Scanner scanner = new Scanner(query); + parse(result, scanner, QP_SEP_PATTERN, charset); + return result; + } + return Collections.emptyList(); + } + + /** + * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an {@link HttpEntity}. The encoding is + * taken from the entity's Content-Encoding header. + *

    + * This is typically used while parsing an HTTP POST. + * + * @param entity + * The entity to parse + * @return a list of {@link NameValuePair} as built from the URI's query portion. + * @throws IOException + * If there was an exception getting the entity's data. + */ + public static List parse( + final HttpEntity entity) throws IOException { + final ContentType contentType = ContentType.get(entity); + if (contentType != null && contentType.getMimeType().equalsIgnoreCase(CONTENT_TYPE)) { + final String content = EntityUtils.toString(entity, Consts.ASCII); + if (content != null && content.length() > 0) { + Charset charset = contentType.getCharset(); + if (charset == null) { + charset = HTTP.DEF_CONTENT_CHARSET; + } + return parse(content, charset, QP_SEPS); + } + } + return Collections.emptyList(); + } + + /** + * Returns true if the entity's Content-Type header is + * application/x-www-form-urlencoded. + */ + public static boolean isEncoded(final HttpEntity entity) { + final Header h = entity.getContentType(); + if (h != null) { + final HeaderElement[] elems = h.getElements(); + if (elems.length > 0) { + final String contentType = elems[0].getName(); + return contentType.equalsIgnoreCase(CONTENT_TYPE); + } + } + return false; + } + + /** + * Adds all parameters within the Scanner to the list of parameters, as encoded by + * encoding. For example, a scanner containing the string a=1&b=2&c=3 would add the + * {@link NameValuePair NameValuePairs} a=1, b=2, and c=3 to the list of parameters. By convention, {@code '&'} and + * {@code ';'} are accepted as parameter separators. + * + * @param parameters + * List to add parameters to. + * @param scanner + * Input that contains the parameters to parse. + * @param charset + * Encoding to use when decoding the parameters. + */ + public static void parse( + final List parameters, + final Scanner scanner, + final String charset) { + parse(parameters, scanner, QP_SEP_PATTERN, charset); + } + + /** + * Adds all parameters within the Scanner to the list of + * parameters, as encoded by encoding. For + * example, a scanner containing the string a=1&b=2&c=3 would + * add the {@link NameValuePair NameValuePairs} a=1, b=2, and c=3 to the + * list of parameters. + * + * @param parameters + * List to add parameters to. + * @param scanner + * Input that contains the parameters to parse. + * @param parameterSepartorPattern + * The Pattern string for parameter separators, by convention {@code "[&;]"} + * @param charset + * Encoding to use when decoding the parameters. + */ + public static void parse( + final List parameters, + final Scanner scanner, + final String parameterSepartorPattern, + final String charset) { + scanner.useDelimiter(parameterSepartorPattern); + while (scanner.hasNext()) { + String name = null; + String value = null; + final String token = scanner.next(); + final int i = token.indexOf(NAME_VALUE_SEPARATOR); + if (i != -1) { + name = decodeFormFields(token.substring(0, i).trim(), charset); + value = decodeFormFields(token.substring(i + 1).trim(), charset); + } else { + name = decodeFormFields(token.trim(), charset); + } + parameters.add(new BasicNameValuePair(name, value)); + } + } + + /** + * Query parameter separators. + */ + private static final char[] QP_SEPS = new char[] { QP_SEP_A, QP_SEP_S }; + + /** + * Query parameter separator pattern. + */ + private static final String QP_SEP_PATTERN = "[" + new String(QP_SEPS) + "]"; + + /** + * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using the given character + * encoding. By convention, {@code '&'} and {@code ';'} are accepted as parameter separators. + * + * @param s + * text to parse. + * @param charset + * Encoding to use when decoding the parameters. + * @return a list of {@link NameValuePair} as built from the URI's query portion. + * + * @since 4.2 + */ + public static List parse(final String s, final Charset charset) { + return parse(s, charset, QP_SEPS); + } + + /** + * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using the given character + * encoding. + * + * @param s + * text to parse. + * @param charset + * Encoding to use when decoding the parameters. + * @param parameterSeparator + * The characters used to separate parameters, by convention, {@code '&'} and {@code ';'}. + * @return a list of {@link NameValuePair} as built from the URI's query portion. + * + * @since 4.3 + */ + public static List parse(final String s, final Charset charset, final char... parameterSeparator) { + if (s == null) { + return Collections.emptyList(); + } + final BasicHeaderValueParser parser = BasicHeaderValueParser.INSTANCE; + final CharArrayBuffer buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + final ParserCursor cursor = new ParserCursor(0, buffer.length()); + final List list = new ArrayList(); + while (!cursor.atEnd()) { + final NameValuePair nvp = parser.parseNameValuePair(buffer, cursor, parameterSeparator); + if (nvp.getName().length() > 0) { + list.add(new BasicNameValuePair( + decodeFormFields(nvp.getName(), charset), + decodeFormFields(nvp.getValue(), charset))); + } + } + return list; + } + + /** + * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} + * list of parameters in an HTTP PUT or HTTP POST. + * + * @param parameters The parameters to include. + * @param charset The encoding to use. + * @return An {@code application/x-www-form-urlencoded} string + */ + public static String format( + final List parameters, + final String charset) { + return format(parameters, QP_SEP_A, charset); + } + + /** + * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} + * list of parameters in an HTTP PUT or HTTP POST. + * + * @param parameters The parameters to include. + * @param parameterSeparator The parameter separator, by convention, {@code '&'} or {@code ';'}. + * @param charset The encoding to use. + * @return An {@code application/x-www-form-urlencoded} string + * + * @since 4.3 + */ + public static String format( + final List parameters, + final char parameterSeparator, + final String charset) { + final StringBuilder result = new StringBuilder(); + for (final NameValuePair parameter : parameters) { + final String encodedName = encodeFormFields(parameter.getName(), charset); + final String encodedValue = encodeFormFields(parameter.getValue(), charset); + if (result.length() > 0) { + result.append(parameterSeparator); + } + result.append(encodedName); + if (encodedValue != null) { + result.append(NAME_VALUE_SEPARATOR); + result.append(encodedValue); + } + } + return result.toString(); + } + + /** + * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} + * list of parameters in an HTTP PUT or HTTP POST. + * + * @param parameters The parameters to include. + * @param charset The encoding to use. + * @return An {@code application/x-www-form-urlencoded} string + * + * @since 4.2 + */ + public static String format( + final Iterable parameters, + final Charset charset) { + return format(parameters, QP_SEP_A, charset); + } + + /** + * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} + * list of parameters in an HTTP PUT or HTTP POST. + * + * @param parameters The parameters to include. + * @param parameterSeparator The parameter separator, by convention, {@code '&'} or {@code ';'}. + * @param charset The encoding to use. + * @return An {@code application/x-www-form-urlencoded} string + * + * @since 4.3 + */ + public static String format( + final Iterable parameters, + final char parameterSeparator, + final Charset charset) { + final StringBuilder result = new StringBuilder(); + for (final NameValuePair parameter : parameters) { + final String encodedName = encodeFormFields(parameter.getName(), charset); + final String encodedValue = encodeFormFields(parameter.getValue(), charset); + if (result.length() > 0) { + result.append(parameterSeparator); + } + result.append(encodedName); + if (encodedValue != null) { + result.append(NAME_VALUE_SEPARATOR); + result.append(encodedValue); + } + } + return result.toString(); + } + + /** + * Unreserved characters, i.e. alphanumeric, plus: {@code _ - ! . ~ ' ( ) *} + *

    + * This list is the same as the {@code unreserved} list in + * RFC 2396 + */ + private static final BitSet UNRESERVED = new BitSet(256); + /** + * Punctuation characters: , ; : $ & + = + *

    + * These are the additional characters allowed by userinfo. + */ + private static final BitSet PUNCT = new BitSet(256); + /** Characters which are safe to use in userinfo, + * i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation */ + private static final BitSet USERINFO = new BitSet(256); + /** Characters which are safe to use in a path, + * i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation plus / @ */ + private static final BitSet PATHSAFE = new BitSet(256); + /** Characters which are safe to use in a query or a fragment, + * i.e. {@link #RESERVED} plus {@link #UNRESERVED} */ + private static final BitSet URIC = new BitSet(256); + + /** + * Reserved characters, i.e. {@code ;/?:@&=+$,[]} + *

    + * This list is the same as the {@code reserved} list in + * RFC 2396 + * as augmented by + * RFC 2732 + */ + private static final BitSet RESERVED = new BitSet(256); + + + /** + * Safe characters for x-www-form-urlencoded data, as per java.net.URLEncoder and browser behaviour, + * i.e. alphanumeric plus {@code "-", "_", ".", "*"} + */ + private static final BitSet URLENCODER = new BitSet(256); + + static { + // unreserved chars + // alpha characters + for (int i = 'a'; i <= 'z'; i++) { + UNRESERVED.set(i); + } + for (int i = 'A'; i <= 'Z'; i++) { + UNRESERVED.set(i); + } + // numeric characters + for (int i = '0'; i <= '9'; i++) { + UNRESERVED.set(i); + } + UNRESERVED.set('_'); // these are the charactes of the "mark" list + UNRESERVED.set('-'); + UNRESERVED.set('.'); + UNRESERVED.set('*'); + URLENCODER.or(UNRESERVED); // skip remaining unreserved characters + UNRESERVED.set('!'); + UNRESERVED.set('~'); + UNRESERVED.set('\''); + UNRESERVED.set('('); + UNRESERVED.set(')'); + // punct chars + PUNCT.set(','); + PUNCT.set(';'); + PUNCT.set(':'); + PUNCT.set('$'); + PUNCT.set('&'); + PUNCT.set('+'); + PUNCT.set('='); + // Safe for userinfo + USERINFO.or(UNRESERVED); + USERINFO.or(PUNCT); + + // URL path safe + PATHSAFE.or(UNRESERVED); + PATHSAFE.set('/'); // segment separator + PATHSAFE.set(';'); // param separator + PATHSAFE.set(':'); // rest as per list in 2396, i.e. : @ & = + $ , + PATHSAFE.set('@'); + PATHSAFE.set('&'); + PATHSAFE.set('='); + PATHSAFE.set('+'); + PATHSAFE.set('$'); + PATHSAFE.set(','); + + RESERVED.set(';'); + RESERVED.set('/'); + RESERVED.set('?'); + RESERVED.set(':'); + RESERVED.set('@'); + RESERVED.set('&'); + RESERVED.set('='); + RESERVED.set('+'); + RESERVED.set('$'); + RESERVED.set(','); + RESERVED.set('['); // added by RFC 2732 + RESERVED.set(']'); // added by RFC 2732 + + URIC.or(RESERVED); + URIC.or(UNRESERVED); + } + + private static final int RADIX = 16; + + private static String urlEncode( + final String content, + final Charset charset, + final BitSet safechars, + final boolean blankAsPlus) { + if (content == null) { + return null; + } + final StringBuilder buf = new StringBuilder(); + final ByteBuffer bb = charset.encode(content); + while (bb.hasRemaining()) { + final int b = bb.get() & 0xff; + if (safechars.get(b)) { + buf.append((char) b); + } else if (blankAsPlus && b == ' ') { + buf.append('+'); + } else { + buf.append("%"); + final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX)); + final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX)); + buf.append(hex1); + buf.append(hex2); + } + } + return buf.toString(); + } + + /** + * Decode/unescape a portion of a URL, to use with the query part ensure {@code plusAsBlank} is true. + * + * @param content the portion to decode + * @param charset the charset to use + * @param plusAsBlank if {@code true}, then convert '+' to space (e.g. for www-url-form-encoded content), otherwise leave as is. + * @return encoded string + */ + private static String urlDecode( + final String content, + final Charset charset, + final boolean plusAsBlank) { + if (content == null) { + return null; + } + final ByteBuffer bb = ByteBuffer.allocate(content.length()); + final CharBuffer cb = CharBuffer.wrap(content); + while (cb.hasRemaining()) { + final char c = cb.get(); + if (c == '%' && cb.remaining() >= 2) { + final char uc = cb.get(); + final char lc = cb.get(); + final int u = Character.digit(uc, 16); + final int l = Character.digit(lc, 16); + if (u != -1 && l != -1) { + bb.put((byte) ((u << 4) + l)); + } else { + bb.put((byte) '%'); + bb.put((byte) uc); + bb.put((byte) lc); + } + } else if (plusAsBlank && c == '+') { + bb.put((byte) ' '); + } else { + bb.put((byte) c); + } + } + bb.flip(); + return charset.decode(bb).toString(); + } + + /** + * Decode/unescape www-url-form-encoded content. + * + * @param content the content to decode, will decode '+' as space + * @param charset the charset to use + * @return encoded string + */ + private static String decodeFormFields (final String content, final String charset) { + if (content == null) { + return null; + } + return urlDecode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8, true); + } + + /** + * Decode/unescape www-url-form-encoded content. + * + * @param content the content to decode, will decode '+' as space + * @param charset the charset to use + * @return encoded string + */ + private static String decodeFormFields (final String content, final Charset charset) { + if (content == null) { + return null; + } + return urlDecode(content, charset != null ? charset : Consts.UTF_8, true); + } + + /** + * Encode/escape www-url-form-encoded content. + *

    + * Uses the {@link #URLENCODER} set of characters, rather than + * the {@link #UNRSERVED} set; this is for compatibilty with previous + * releases, URLEncoder.encode() and most browsers. + * + * @param content the content to encode, will convert space to '+' + * @param charset the charset to use + * @return encoded string + */ + private static String encodeFormFields(final String content, final String charset) { + if (content == null) { + return null; + } + return urlEncode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8, URLENCODER, true); + } + + /** + * Encode/escape www-url-form-encoded content. + *

    + * Uses the {@link #URLENCODER} set of characters, rather than + * the {@link #UNRSERVED} set; this is for compatibilty with previous + * releases, URLEncoder.encode() and most browsers. + * + * @param content the content to encode, will convert space to '+' + * @param charset the charset to use + * @return encoded string + */ + private static String encodeFormFields (final String content, final Charset charset) { + if (content == null) { + return null; + } + return urlEncode(content, charset != null ? charset : Consts.UTF_8, URLENCODER, true); + } + + /** + * Encode a String using the {@link #USERINFO} set of characters. + *

    + * Used by URIBuilder to encode the userinfo segment. + * + * @param content the string to encode, does not convert space to '+' + * @param charset the charset to use + * @return the encoded string + */ + static String encUserInfo(final String content, final Charset charset) { + return urlEncode(content, charset, USERINFO, false); + } + + /** + * Encode a String using the {@link #URIC} set of characters. + *

    + * Used by URIBuilder to encode the query and fragment segments. + * + * @param content the string to encode, does not convert space to '+' + * @param charset the charset to use + * @return the encoded string + */ + static String encUric(final String content, final Charset charset) { + return urlEncode(content, charset, URIC, false); + } + + /** + * Encode a String using the {@link #PATHSAFE} set of characters. + *

    + * Used by URIBuilder to encode path segments. + * + * @param content the string to encode, does not convert space to '+' + * @param charset the charset to use + * @return the encoded string + */ + static String encPath(final String content, final Charset charset) { + return urlEncode(content, charset, PATHSAFE, false); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/package-info.java new file mode 100644 index 000000000..7f5671654 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/client/utils/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client utility classes. + */ +package ch.boye.httpclientandroidlib.client.utils; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/BasicFuture.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/BasicFuture.java new file mode 100644 index 000000000..a2215ceca --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/BasicFuture.java @@ -0,0 +1,154 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.concurrent; + +import ch.boye.httpclientandroidlib.util.Args; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Basic implementation of the {@link Future} interface. BasicFuture + * can be put into a completed state by invoking any of the following methods: + * {@link #cancel()}, {@link #failed(Exception)}, or {@link #completed(Object)}. + * + * @param the future result type of an asynchronous operation. + * @since 4.2 + */ +public class BasicFuture implements Future, Cancellable { + + private final FutureCallback callback; + + private volatile boolean completed; + private volatile boolean cancelled; + private volatile T result; + private volatile Exception ex; + + public BasicFuture(final FutureCallback callback) { + super(); + this.callback = callback; + } + + public boolean isCancelled() { + return this.cancelled; + } + + public boolean isDone() { + return this.completed; + } + + private T getResult() throws ExecutionException { + if (this.ex != null) { + throw new ExecutionException(this.ex); + } + return this.result; + } + + public synchronized T get() throws InterruptedException, ExecutionException { + while (!this.completed) { + wait(); + } + return getResult(); + } + + public synchronized T get(final long timeout, final TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + Args.notNull(unit, "Time unit"); + final long msecs = unit.toMillis(timeout); + final long startTime = (msecs <= 0) ? 0 : System.currentTimeMillis(); + long waitTime = msecs; + if (this.completed) { + return getResult(); + } else if (waitTime <= 0) { + throw new TimeoutException(); + } else { + for (;;) { + wait(waitTime); + if (this.completed) { + return getResult(); + } else { + waitTime = msecs - (System.currentTimeMillis() - startTime); + if (waitTime <= 0) { + throw new TimeoutException(); + } + } + } + } + } + + public boolean completed(final T result) { + synchronized(this) { + if (this.completed) { + return false; + } + this.completed = true; + this.result = result; + notifyAll(); + } + if (this.callback != null) { + this.callback.completed(result); + } + return true; + } + + public boolean failed(final Exception exception) { + synchronized(this) { + if (this.completed) { + return false; + } + this.completed = true; + this.ex = exception; + notifyAll(); + } + if (this.callback != null) { + this.callback.failed(exception); + } + return true; + } + + public boolean cancel(final boolean mayInterruptIfRunning) { + synchronized(this) { + if (this.completed) { + return false; + } + this.completed = true; + this.cancelled = true; + notifyAll(); + } + if (this.callback != null) { + this.callback.cancelled(); + } + return true; + } + + public boolean cancel() { + return cancel(true); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/Cancellable.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/Cancellable.java new file mode 100644 index 000000000..c51daa27f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/Cancellable.java @@ -0,0 +1,39 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.concurrent; + +/** + * A Cancellable represents a process or an operation that can be + * canceled. + * + * @since 4.2 + */ +public interface Cancellable { + + boolean cancel(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/FutureCallback.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/FutureCallback.java new file mode 100644 index 000000000..91ed93940 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/FutureCallback.java @@ -0,0 +1,44 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.concurrent; + +/** + * A callback interface that gets invoked upon completion of + * a {@link java.util.concurrent.Future}. + * + * @param the future result type returned by this callback. + * @since 4.2 + */ +public interface FutureCallback { + + void completed(T result); + + void failed(Exception ex); + + void cancelled(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/package-info.java new file mode 100644 index 000000000..38d2699ed --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/concurrent/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Core concurrency APIs. + */ +package ch.boye.httpclientandroidlib.concurrent; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/ConnectionConfig.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/ConnectionConfig.java new file mode 100644 index 000000000..50fb21460 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/ConnectionConfig.java @@ -0,0 +1,192 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.config; + +import java.nio.charset.Charset; +import java.nio.charset.CodingErrorAction; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * HTTP connection configuration. + * + * @since 4.3 + */ +@Immutable +public class ConnectionConfig implements Cloneable { + + public static final ConnectionConfig DEFAULT = new Builder().build(); + + private final int bufferSize; + private final int fragmentSizeHint; + private final Charset charset; + private final CodingErrorAction malformedInputAction; + private final CodingErrorAction unmappableInputAction; + private final MessageConstraints messageConstraints; + + ConnectionConfig( + final int bufferSize, + final int fragmentSizeHint, + final Charset charset, + final CodingErrorAction malformedInputAction, + final CodingErrorAction unmappableInputAction, + final MessageConstraints messageConstraints) { + super(); + this.bufferSize = bufferSize; + this.fragmentSizeHint = fragmentSizeHint; + this.charset = charset; + this.malformedInputAction = malformedInputAction; + this.unmappableInputAction = unmappableInputAction; + this.messageConstraints = messageConstraints; + } + + public int getBufferSize() { + return bufferSize; + } + + public int getFragmentSizeHint() { + return fragmentSizeHint; + } + + public Charset getCharset() { + return charset; + } + + public CodingErrorAction getMalformedInputAction() { + return malformedInputAction; + } + + public CodingErrorAction getUnmappableInputAction() { + return unmappableInputAction; + } + + public MessageConstraints getMessageConstraints() { + return messageConstraints; + } + + @Override + protected ConnectionConfig clone() throws CloneNotSupportedException { + return (ConnectionConfig) super.clone(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("[bufferSize=").append(this.bufferSize) + .append(", fragmentSizeHint=").append(this.fragmentSizeHint) + .append(", charset=").append(this.charset) + .append(", malformedInputAction=").append(this.malformedInputAction) + .append(", unmappableInputAction=").append(this.unmappableInputAction) + .append(", messageConstraints=").append(this.messageConstraints) + .append("]"); + return builder.toString(); + } + + public static ConnectionConfig.Builder custom() { + return new Builder(); + } + + public static ConnectionConfig.Builder copy(final ConnectionConfig config) { + Args.notNull(config, "Connection config"); + return new Builder() + .setCharset(config.getCharset()) + .setMalformedInputAction(config.getMalformedInputAction()) + .setUnmappableInputAction(config.getUnmappableInputAction()) + .setMessageConstraints(config.getMessageConstraints()); + } + + public static class Builder { + + private int bufferSize; + private int fragmentSizeHint; + private Charset charset; + private CodingErrorAction malformedInputAction; + private CodingErrorAction unmappableInputAction; + private MessageConstraints messageConstraints; + + Builder() { + this.fragmentSizeHint = -1; + } + + public Builder setBufferSize(final int bufferSize) { + this.bufferSize = bufferSize; + return this; + } + + public Builder setFragmentSizeHint(final int fragmentSizeHint) { + this.fragmentSizeHint = fragmentSizeHint; + return this; + } + + public Builder setCharset(final Charset charset) { + this.charset = charset; + return this; + } + + public Builder setMalformedInputAction(final CodingErrorAction malformedInputAction) { + this.malformedInputAction = malformedInputAction; + if (malformedInputAction != null && this.charset == null) { + this.charset = Consts.ASCII; + } + return this; + } + + public Builder setUnmappableInputAction(final CodingErrorAction unmappableInputAction) { + this.unmappableInputAction = unmappableInputAction; + if (unmappableInputAction != null && this.charset == null) { + this.charset = Consts.ASCII; + } + return this; + } + + public Builder setMessageConstraints(final MessageConstraints messageConstraints) { + this.messageConstraints = messageConstraints; + return this; + } + + public ConnectionConfig build() { + Charset cs = charset; + if (cs == null && (malformedInputAction != null || unmappableInputAction != null)) { + cs = Consts.ASCII; + } + final int bufSize = this.bufferSize > 0 ? this.bufferSize : 8 * 1024; + final int fragmentHintSize = this.fragmentSizeHint >= 0 ? this.fragmentSizeHint : bufSize; + return new ConnectionConfig( + bufSize, + fragmentHintSize, + cs, + malformedInputAction, + unmappableInputAction, + messageConstraints); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Lookup.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Lookup.java new file mode 100644 index 000000000..343dda2d3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Lookup.java @@ -0,0 +1,40 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.config; + + +/** + * Generic lookup by low-case string ID. + * + * @since 4.3 + */ +public interface Lookup { + + I lookup(String name); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/MessageConstraints.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/MessageConstraints.java new file mode 100644 index 000000000..5ac8c065a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/MessageConstraints.java @@ -0,0 +1,113 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.config; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * HTTP Message constraints: line length and header count. + * + * @since 4.3 + */ +public class MessageConstraints implements Cloneable { + + public static final MessageConstraints DEFAULT = new Builder().build(); + + private final int maxLineLength; + private final int maxHeaderCount; + + MessageConstraints(final int maxLineLength, final int maxHeaderCount) { + super(); + this.maxLineLength = maxLineLength; + this.maxHeaderCount = maxHeaderCount; + } + + public int getMaxLineLength() { + return maxLineLength; + } + + public int getMaxHeaderCount() { + return maxHeaderCount; + } + + @Override + protected MessageConstraints clone() throws CloneNotSupportedException { + return (MessageConstraints) super.clone(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("[maxLineLength=").append(maxLineLength) + .append(", maxHeaderCount=").append(maxHeaderCount) + .append("]"); + return builder.toString(); + } + + public static MessageConstraints lineLen(final int max) { + return new MessageConstraints(Args.notNegative(max, "Max line length"), -1); + } + + public static MessageConstraints.Builder custom() { + return new Builder(); + } + + public static MessageConstraints.Builder copy(final MessageConstraints config) { + Args.notNull(config, "Message constraints"); + return new Builder() + .setMaxHeaderCount(config.getMaxHeaderCount()) + .setMaxLineLength(config.getMaxLineLength()); + } + + public static class Builder { + + private int maxLineLength; + private int maxHeaderCount; + + Builder() { + this.maxLineLength = -1; + this.maxHeaderCount = -1; + } + + public Builder setMaxLineLength(final int maxLineLength) { + this.maxLineLength = maxLineLength; + return this; + } + + public Builder setMaxHeaderCount(final int maxHeaderCount) { + this.maxHeaderCount = maxHeaderCount; + return this; + } + + public MessageConstraints build() { + return new MessageConstraints(maxLineLength, maxHeaderCount); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Registry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Registry.java new file mode 100644 index 000000000..71b718bee --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/Registry.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.config; + +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +/** + * Generic registry of items keyed by low-case string ID. + * + * @since 4.3 + */ +@ThreadSafe +public final class Registry implements Lookup { + + private final Map map; + + Registry(final Map map) { + super(); + this.map = new ConcurrentHashMap(map); + } + + public I lookup(final String key) { + if (key == null) { + return null; + } + return map.get(key.toLowerCase(Locale.US)); + } + + @Override + public String toString() { + return map.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/RegistryBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/RegistryBuilder.java new file mode 100644 index 000000000..56dbd7976 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/RegistryBuilder.java @@ -0,0 +1,72 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.config; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Builder for {@link Registry} instances. + * + * @since 4.3 + */ +@NotThreadSafe +public final class RegistryBuilder { + + private final Map items; + + public static RegistryBuilder create() { + return new RegistryBuilder(); + } + + RegistryBuilder() { + super(); + this.items = new HashMap(); + } + + public RegistryBuilder register(final String id, final I item) { + Args.notEmpty(id, "ID"); + Args.notNull(item, "Item"); + items.put(id.toLowerCase(Locale.US), item); + return this; + } + + public Registry build() { + return new Registry(items); + } + + @Override + public String toString() { + return items.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/SocketConfig.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/SocketConfig.java new file mode 100644 index 000000000..515dac1b1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/SocketConfig.java @@ -0,0 +1,197 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.config; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Socket configuration. + * + * @since 4.3 + */ +@Immutable +public class SocketConfig implements Cloneable { + + public static final SocketConfig DEFAULT = new Builder().build(); + + private final int soTimeout; + private final boolean soReuseAddress; + private final int soLinger; + private final boolean soKeepAlive; + private final boolean tcpNoDelay; + + SocketConfig( + final int soTimeout, + final boolean soReuseAddress, + final int soLinger, + final boolean soKeepAlive, + final boolean tcpNoDelay) { + super(); + this.soTimeout = soTimeout; + this.soReuseAddress = soReuseAddress; + this.soLinger = soLinger; + this.soKeepAlive = soKeepAlive; + this.tcpNoDelay = tcpNoDelay; + } + + /** + * Determines the default socket timeout value for non-blocking I/O operations. + *

    + * Default: 0 (no timeout) + * + * @see java.net.SocketOptions#SO_TIMEOUT + */ + public int getSoTimeout() { + return soTimeout; + } + + /** + * Determines the default value of the {@link java.net.SocketOptions#SO_REUSEADDR} parameter + * for newly created sockets. + *

    + * Default: false + * + * @see java.net.SocketOptions#SO_REUSEADDR + */ + public boolean isSoReuseAddress() { + return soReuseAddress; + } + + /** + * Determines the default value of the {@link java.net.SocketOptions#SO_LINGER} parameter + * for newly created sockets. + *

    + * Default: -1 + * + * @see java.net.SocketOptions#SO_LINGER + */ + public int getSoLinger() { + return soLinger; + } + + /** + * Determines the default value of the {@link java.net.SocketOptions#SO_KEEPALIVE} parameter + * for newly created sockets. + *

    + * Default: -1 + * + * @see java.net.SocketOptions#SO_KEEPALIVE + */ + public boolean isSoKeepAlive() { + return this.soKeepAlive; + } + + /** + * Determines the default value of the {@link java.net.SocketOptions#TCP_NODELAY} parameter + * for newly created sockets. + *

    + * Default: false + * + * @see java.net.SocketOptions#TCP_NODELAY + */ + public boolean isTcpNoDelay() { + return tcpNoDelay; + } + + @Override + protected SocketConfig clone() throws CloneNotSupportedException { + return (SocketConfig) super.clone(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("[soTimeout=").append(this.soTimeout) + .append(", soReuseAddress=").append(this.soReuseAddress) + .append(", soLinger=").append(this.soLinger) + .append(", soKeepAlive=").append(this.soKeepAlive) + .append(", tcpNoDelay=").append(this.tcpNoDelay) + .append("]"); + return builder.toString(); + } + + public static SocketConfig.Builder custom() { + return new Builder(); + } + + public static SocketConfig.Builder copy(final SocketConfig config) { + Args.notNull(config, "Socket config"); + return new Builder() + .setSoTimeout(config.getSoTimeout()) + .setSoReuseAddress(config.isSoReuseAddress()) + .setSoLinger(config.getSoLinger()) + .setSoKeepAlive(config.isSoKeepAlive()) + .setTcpNoDelay(config.isTcpNoDelay()); + } + + public static class Builder { + + private int soTimeout; + private boolean soReuseAddress; + private int soLinger; + private boolean soKeepAlive; + private boolean tcpNoDelay; + + Builder() { + this.soLinger = -1; + this.tcpNoDelay = true; + } + + public Builder setSoTimeout(final int soTimeout) { + this.soTimeout = soTimeout; + return this; + } + + public Builder setSoReuseAddress(final boolean soReuseAddress) { + this.soReuseAddress = soReuseAddress; + return this; + } + + public Builder setSoLinger(final int soLinger) { + this.soLinger = soLinger; + return this; + } + + public Builder setSoKeepAlive(final boolean soKeepAlive) { + this.soKeepAlive = soKeepAlive; + return this; + } + + public Builder setTcpNoDelay(final boolean tcpNoDelay) { + this.tcpNoDelay = tcpNoDelay; + return this; + } + + public SocketConfig build() { + return new SocketConfig(soTimeout, soReuseAddress, soLinger, soKeepAlive, tcpNoDelay); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/package-info.java new file mode 100644 index 000000000..9521d6828 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/config/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Core configuration APIs. + */ +package ch.boye.httpclientandroidlib.config; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicEofSensorWatcher.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicEofSensorWatcher.java new file mode 100644 index 000000000..0273224ad --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicEofSensorWatcher.java @@ -0,0 +1,105 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of {@link EofSensorWatcher}. The underlying connection + * is released on close or EOF. + * + * @since 4.0 + * + * @deprecated (4.3) do not use. + */ +@Deprecated +@NotThreadSafe +public class BasicEofSensorWatcher implements EofSensorWatcher { + + /** The connection to auto-release. */ + protected final ManagedClientConnection managedConn; + + /** Whether to keep the connection alive. */ + protected final boolean attemptReuse; + + /** + * Creates a new watcher for auto-releasing a connection. + * + * @param conn the connection to auto-release + * @param reuse whether the connection should be re-used + */ + public BasicEofSensorWatcher(final ManagedClientConnection conn, + final boolean reuse) { + Args.notNull(conn, "Connection"); + managedConn = conn; + attemptReuse = reuse; + } + + public boolean eofDetected(final InputStream wrapped) + throws IOException { + + try { + if (attemptReuse) { + // there may be some cleanup required, such as + // reading trailers after the response body: + wrapped.close(); + managedConn.markReusable(); + } + } finally { + managedConn.releaseConnection(); + } + return false; + } + + public boolean streamClosed(final InputStream wrapped) + throws IOException { + + try { + if (attemptReuse) { + // this assumes that closing the stream will + // consume the remainder of the response body: + wrapped.close(); + managedConn.markReusable(); + } + } finally { + managedConn.releaseConnection(); + } + return false; + } + + public boolean streamAbort(final InputStream wrapped) + throws IOException { + + managedConn.abortConnection(); + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicManagedEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicManagedEntity.java new file mode 100644 index 000000000..59556b5e9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/BasicManagedEntity.java @@ -0,0 +1,208 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * An entity that releases a {@link ManagedClientConnection connection}. + * A {@link ManagedClientConnection} will + * typically not return a managed entity, but you can replace + * the unmanaged entity in the response with a managed one. + * + * @since 4.0 + * + * @deprecated (4.3) do not use. + */ +@Deprecated +@NotThreadSafe +public class BasicManagedEntity extends HttpEntityWrapper + implements ConnectionReleaseTrigger, EofSensorWatcher { + + /** The connection to release. */ + protected ManagedClientConnection managedConn; + + /** Whether to keep the connection alive. */ + protected final boolean attemptReuse; + + /** + * Creates a new managed entity that can release a connection. + * + * @param entity the entity of which to wrap the content. + * Note that the argument entity can no longer be used + * afterwards, since the content will be taken by this + * managed entity. + * @param conn the connection to release + * @param reuse whether the connection should be re-used + */ + public BasicManagedEntity(final HttpEntity entity, + final ManagedClientConnection conn, + final boolean reuse) { + super(entity); + Args.notNull(conn, "Connection"); + this.managedConn = conn; + this.attemptReuse = reuse; + } + + @Override + public boolean isRepeatable() { + return false; + } + + @Override + public InputStream getContent() throws IOException { + return new EofSensorInputStream(wrappedEntity.getContent(), this); + } + + private void ensureConsumed() throws IOException { + if (managedConn == null) { + return; + } + + try { + if (attemptReuse) { + // this will not trigger a callback from EofSensorInputStream + EntityUtils.consume(wrappedEntity); + managedConn.markReusable(); + } else { + managedConn.unmarkReusable(); + } + } finally { + releaseManagedConnection(); + } + } + + /** + * @deprecated (4.1) Use {@link EntityUtils#consume(HttpEntity)} + */ + @Deprecated + @Override + public void consumeContent() throws IOException { + ensureConsumed(); + } + + @Override + public void writeTo(final OutputStream outstream) throws IOException { + super.writeTo(outstream); + ensureConsumed(); + } + + public void releaseConnection() throws IOException { + ensureConsumed(); + } + + public void abortConnection() throws IOException { + + if (managedConn != null) { + try { + managedConn.abortConnection(); + } finally { + managedConn = null; + } + } + } + + public boolean eofDetected(final InputStream wrapped) throws IOException { + try { + if (managedConn != null) { + if (attemptReuse) { + // there may be some cleanup required, such as + // reading trailers after the response body: + wrapped.close(); + managedConn.markReusable(); + } else { + managedConn.unmarkReusable(); + } + } + } finally { + releaseManagedConnection(); + } + return false; + } + + public boolean streamClosed(final InputStream wrapped) throws IOException { + try { + if (managedConn != null) { + if (attemptReuse) { + final boolean valid = managedConn.isOpen(); + // this assumes that closing the stream will + // consume the remainder of the response body: + try { + wrapped.close(); + managedConn.markReusable(); + } catch (final SocketException ex) { + if (valid) { + throw ex; + } + } + } else { + managedConn.unmarkReusable(); + } + } + } finally { + releaseManagedConnection(); + } + return false; + } + + public boolean streamAbort(final InputStream wrapped) throws IOException { + if (managedConn != null) { + managedConn.abortConnection(); + } + return false; + } + + /** + * Releases the connection gracefully. + * The connection attribute will be nullified. + * Subsequent invocations are no-ops. + * + * @throws IOException in case of an IO problem. + * The connection attribute will be nullified anyway. + */ + protected void releaseManagedConnection() + throws IOException { + + if (managedConn != null) { + try { + managedConn.releaseConnection(); + } finally { + managedConn = null; + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManager.java new file mode 100644 index 000000000..dcdbb4ce5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManager.java @@ -0,0 +1,117 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; + +/** + * Management interface for {@link ManagedClientConnection client connections}. + * The purpose of an HTTP connection manager is to serve as a factory for new + * HTTP connections, manage persistent connections and synchronize access to + * persistent connections making sure that only one thread of execution can + * have access to a connection at a time. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. + */ +@Deprecated +public interface ClientConnectionManager { + + /** + * Obtains the scheme registry used by this manager. + * + * @return the scheme registry, never null + */ + SchemeRegistry getSchemeRegistry(); + + /** + * Returns a new {@link ClientConnectionRequest}, from which a + * {@link ManagedClientConnection} can be obtained or the request can be + * aborted. + */ + ClientConnectionRequest requestConnection(HttpRoute route, Object state); + + /** + * Releases a connection for use by others. + * You may optionally specify how long the connection is valid + * to be reused. Values <= 0 are considered to be valid forever. + * If the connection is not marked as reusable, the connection will + * not be reused regardless of the valid duration. + * + * If the connection has been released before, + * the call will be ignored. + * + * @param conn the connection to release + * @param validDuration the duration of time this connection is valid for reuse + * @param timeUnit the unit of time validDuration is measured in + * + * @see #closeExpiredConnections() + */ + void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit); + + /** + * Closes idle connections in the pool. + * Open connections in the pool that have not been used for the + * timespan given by the argument will be closed. + * Currently allocated connections are not subject to this method. + * Times will be checked with milliseconds precision + * + * All expired connections will also be closed. + * + * @param idletime the idle time of connections to be closed + * @param tunit the unit for the idletime + * + * @see #closeExpiredConnections() + */ + void closeIdleConnections(long idletime, TimeUnit tunit); + + /** + * Closes all expired connections in the pool. + * Open connections in the pool that have not been used for + * the timespan defined when the connection was released will be closed. + * Currently allocated connections are not subject to this method. + * Times will be checked with milliseconds precision. + */ + void closeExpiredConnections(); + + /** + * Shuts down this connection manager and releases allocated resources. + * This includes closing all connections, whether they are currently + * used or not. + */ + void shutdown(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManagerFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManagerFactory.java new file mode 100644 index 000000000..895690e95 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionManagerFactory.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * A factory for creating new {@link ClientConnectionManager} instances. + * + * @since 4.0 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. + */ +@Deprecated +public interface ClientConnectionManagerFactory { + + ClientConnectionManager newInstance( + HttpParams params, + SchemeRegistry schemeRegistry); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionOperator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionOperator.java new file mode 100644 index 000000000..333975b2c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionOperator.java @@ -0,0 +1,107 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * ClientConnectionOperator represents a strategy for creating + * {@link OperatedClientConnection} instances and updating the underlying + * {@link java.net.Socket} of those objects. Implementations will most + * likely make use of {@link ch.boye.httpclientandroidlib.conn.scheme.SchemeSocketFactory}s + * to create {@link java.net.Socket} instances. + *

    + * The methods in this interface allow the creation of plain and layered + * sockets. Creating a tunnelled connection through a proxy, however, + * is not within the scope of the operator. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. + */ +@Deprecated +public interface ClientConnectionOperator { + + /** + * Creates a new connection that can be operated. + * + * @return a new, unopened connection for use with this operator + */ + OperatedClientConnection createConnection(); + + /** + * Opens a connection to the given target host. + * + * @param conn the connection to open + * @param target the target host to connect to + * @param local the local address to route from, or + * null for the default + * @param context the context for the connection + * @param params the parameters for the connection + * + * @throws IOException in case of a problem + */ + void openConnection(OperatedClientConnection conn, + HttpHost target, + InetAddress local, + HttpContext context, + HttpParams params) + throws IOException; + + /** + * Updates a connection with a layered secure connection. + * The typical use of this method is to update a tunnelled plain + * connection (HTTP) to a secure TLS/SSL connection (HTTPS). + * + * @param conn the open connection to update + * @param target the target host for the updated connection. + * The connection must already be open or tunnelled + * to the host and port, but the scheme of the target + * will be used to create a layered connection. + * @param context the context for the connection + * @param params the parameters for the updated connection + * + * @throws IOException in case of a problem + */ + void updateSecureConnection(OperatedClientConnection conn, + HttpHost target, + HttpContext context, + HttpParams params) + throws IOException; + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionRequest.java new file mode 100644 index 000000000..656284081 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ClientConnectionRequest.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.util.concurrent.TimeUnit; + +/** + * Encapsulates a request for a {@link ManagedClientConnection}. + * + * @since 4.0 + * + * @deprecated (4.3) replaced by {@link ConnectionRequest}. + */ +@Deprecated +public interface ClientConnectionRequest { + + /** + * Obtains a connection within a given time. + * This method will block until a connection becomes available, + * the timeout expires, or the connection manager is + * {@link ClientConnectionManager#shutdown() shut down}. + * Timeouts are handled with millisecond precision. + * + * If {@link #abortRequest()} is called while this is blocking or + * before this began, an {@link InterruptedException} will + * be thrown. + * + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the timeout, + * may be null only if there is no timeout + * + * @return a connection that can be used to communicate + * along the given route + * + * @throws ConnectionPoolTimeoutException + * in case of a timeout + * @throws InterruptedException + * if the calling thread is interrupted while waiting + */ + ManagedClientConnection getConnection(long timeout, TimeUnit tunit) + throws InterruptedException, ConnectionPoolTimeoutException; + + /** + * Aborts the call to {@link #getConnection(long, TimeUnit)}, + * causing it to throw an {@link InterruptedException}. + */ + void abortRequest(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectTimeoutException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectTimeoutException.java new file mode 100644 index 000000000..f98bd1941 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectTimeoutException.java @@ -0,0 +1,94 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.util.Arrays; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * A timeout while connecting to an HTTP server or waiting for an + * available connection from an HttpConnectionManager. + * + * + * @since 4.0 + */ +@Immutable +public class ConnectTimeoutException extends InterruptedIOException { + + private static final long serialVersionUID = -4816682903149535989L; + + private final HttpHost host; + + /** + * Creates a ConnectTimeoutException with a null detail message. + */ + public ConnectTimeoutException() { + super(); + this.host = null; + } + + /** + * Creates a ConnectTimeoutException with the specified detail message. + */ + public ConnectTimeoutException(final String message) { + super(message); + this.host = null; + } + + /** + * Creates a ConnectTimeoutException based on original {@link IOException}. + * + * @since 4.3 + */ + public ConnectTimeoutException( + final IOException cause, + final HttpHost host, + final InetAddress... remoteAddresses) { + super("Connect to " + + (host != null ? host.toHostString() : "remote host") + + (remoteAddresses != null && remoteAddresses.length > 0 ? + " " + Arrays.asList(remoteAddresses) : "") + + ((cause != null && cause.getMessage() != null) ? + " failed: " + cause.getMessage() : " timed out")); + this.host = host; + initCause(cause); + } + + /** + * @since 4.3 + */ + public HttpHost getHost() { + return host; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionKeepAliveStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionKeepAliveStrategy.java new file mode 100644 index 000000000..3f06e4873 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionKeepAliveStrategy.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Interface for deciding how long a connection can remain + * idle before being reused. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + */ +public interface ConnectionKeepAliveStrategy { + + /** + * Returns the duration of time which this connection can be safely kept + * idle. If the connection is left idle for longer than this period of time, + * it MUST not reused. A value of 0 or less may be returned to indicate that + * there is no suitable suggestion. + * + * When coupled with a {@link ch.boye.httpclientandroidlib.ConnectionReuseStrategy}, if + * {@link ch.boye.httpclientandroidlib.ConnectionReuseStrategy#keepAlive( + * HttpResponse, HttpContext)} returns true, this allows you to control + * how long the reuse will last. If keepAlive returns false, this should + * have no meaningful impact + * + * @param response + * The last response received over the connection. + * @param context + * the context in which the connection is being used. + * + * @return the duration in ms for which it is safe to keep the connection + * idle, or <=0 if no suggested duration. + */ + long getKeepAliveDuration(HttpResponse response, HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionPoolTimeoutException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionPoolTimeoutException.java new file mode 100644 index 000000000..d4066b2a3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionPoolTimeoutException.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * A timeout while waiting for an available connection + * from a connection manager. + * + * + * @since 4.0 + */ +@Immutable +public class ConnectionPoolTimeoutException extends ConnectTimeoutException { + + private static final long serialVersionUID = -7898874842020245128L; + + /** + * Creates a ConnectTimeoutException with a null detail message. + */ + public ConnectionPoolTimeoutException() { + super(); + } + + /** + * Creates a ConnectTimeoutException with the specified detail message. + * + * @param message The exception detail message + */ + public ConnectionPoolTimeoutException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionReleaseTrigger.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionReleaseTrigger.java new file mode 100644 index 000000000..97269268b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionReleaseTrigger.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; + +/** + * Interface for releasing a connection. This can be implemented by various + * "trigger" objects which are associated with a connection, for example + * a {@link EofSensorInputStream stream} or an {@link BasicManagedEntity entity} + * or the {@link ManagedClientConnection connection} itself. + *

    + * The methods in this interface can safely be called multiple times. + * The first invocation releases the connection, subsequent calls + * are ignored. + * + * @since 4.0 + */ +public interface ConnectionReleaseTrigger { + + /** + * Releases the connection with the option of keep-alive. This is a + * "graceful" release and may cause IO operations for consuming the + * remainder of a response entity. Use + * {@link #abortConnection abortConnection} for a hard release. The + * connection may be reused as specified by the duration. + * + * @throws IOException + * in case of an IO problem. The connection will be released + * anyway. + */ + void releaseConnection() + throws IOException; + + /** + * Releases the connection without the option of keep-alive. + * This is a "hard" release that implies a shutdown of the connection. + * Use {@link #releaseConnection()} for a graceful release. + * + * @throws IOException in case of an IO problem. + * The connection will be released anyway. + */ + void abortConnection() + throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionRequest.java new file mode 100644 index 000000000..838c35567 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ConnectionRequest.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.concurrent.Cancellable; + +/** + * Represents a request for a {@link HttpClientConnection} whose life cycle + * is managed by a connection manager. + * + * @since 4.3 + */ +public interface ConnectionRequest extends Cancellable { + + /** + * Obtains a connection within a given time. + * This method will block until a connection becomes available, + * the timeout expires, or the connection manager is shut down. + * Timeouts are handled with millisecond precision. + * + * If {@link #cancel()} is called while this is blocking or + * before this began, an {@link InterruptedException} will + * be thrown. + * + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the timeout, + * may be null only if there is no timeout + * + * @return a connection that can be used to communicate + * along the given route + * + * @throws ConnectionPoolTimeoutException + * in case of a timeout + * @throws InterruptedException + * if the calling thread is interrupted while waiting + */ + HttpClientConnection get(long timeout, TimeUnit tunit) + throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/DnsResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/DnsResolver.java new file mode 100644 index 000000000..78ce8709b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/DnsResolver.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Users may implement this interface to override the normal DNS lookup offered + * by the OS. + * + * @since 4.2 + */ +public interface DnsResolver { + + /** + * Returns the IP address for the specified host name, or null if the given + * host is not recognized or the associated IP address cannot be used to + * build an InetAddress instance. + * + * @see InetAddress + * + * @param host + * The host name to be resolved by this resolver. + * @return The IP address associated to the given host name, or null if the + * host name is not known by the implementation class. + */ + InetAddress[] resolve(String host) throws UnknownHostException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorInputStream.java new file mode 100644 index 000000000..7db23eed4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorInputStream.java @@ -0,0 +1,289 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A stream wrapper that triggers actions on {@link #close close()} and EOF. + * Primarily used to auto-release an underlying managed connection when the response + * body is consumed or no longer needed. + * + * @see EofSensorWatcher + * + * @since 4.0 + */ +// don't use FilterInputStream as the base class, we'd have to +// override markSupported(), mark(), and reset() to disable them +@NotThreadSafe +public class EofSensorInputStream extends InputStream implements ConnectionReleaseTrigger { + + /** + * The wrapped input stream, while accessible. + * The value changes to null when the wrapped stream + * becomes inaccessible. + */ + protected InputStream wrappedStream; + + /** + * Indicates whether this stream itself is closed. + * If it isn't, but {@link #wrappedStream wrappedStream} + * is null, we're running in EOF mode. + * All read operations will indicate EOF without accessing + * the underlying stream. After closing this stream, read + * operations will trigger an {@link IOException IOException}. + * + * @see #isReadAllowed isReadAllowed + */ + private boolean selfClosed; + + /** The watcher to be notified, if any. */ + private final EofSensorWatcher eofWatcher; + + /** + * Creates a new EOF sensor. + * If no watcher is passed, the underlying stream will simply be + * closed when EOF is detected or {@link #close close} is called. + * Otherwise, the watcher decides whether the underlying stream + * should be closed before detaching from it. + * + * @param in the wrapped stream + * @param watcher the watcher for events, or null for + * auto-close behavior without notification + */ + public EofSensorInputStream(final InputStream in, + final EofSensorWatcher watcher) { + Args.notNull(in, "Wrapped stream"); + wrappedStream = in; + selfClosed = false; + eofWatcher = watcher; + } + + boolean isSelfClosed() { + return selfClosed; + } + + InputStream getWrappedStream() { + return wrappedStream; + } + + /** + * Checks whether the underlying stream can be read from. + * + * @return true if the underlying stream is accessible, + * false if this stream is in EOF mode and + * detached from the underlying stream + * + * @throws IOException if this stream is already closed + */ + protected boolean isReadAllowed() throws IOException { + if (selfClosed) { + throw new IOException("Attempted read on closed stream."); + } + return (wrappedStream != null); + } + + @Override + public int read() throws IOException { + int l = -1; + + if (isReadAllowed()) { + try { + l = wrappedStream.read(); + checkEOF(l); + } catch (final IOException ex) { + checkAbort(); + throw ex; + } + } + + return l; + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + int l = -1; + + if (isReadAllowed()) { + try { + l = wrappedStream.read(b, off, len); + checkEOF(l); + } catch (final IOException ex) { + checkAbort(); + throw ex; + } + } + + return l; + } + + @Override + public int read(final byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int available() throws IOException { + int a = 0; // not -1 + + if (isReadAllowed()) { + try { + a = wrappedStream.available(); + // no checkEOF() here, available() can't trigger EOF + } catch (final IOException ex) { + checkAbort(); + throw ex; + } + } + + return a; + } + + @Override + public void close() throws IOException { + // tolerate multiple calls to close() + selfClosed = true; + checkClose(); + } + + /** + * Detects EOF and notifies the watcher. + * This method should only be called while the underlying stream is + * still accessible. Use {@link #isReadAllowed isReadAllowed} to + * check that condition. + *
    + * If EOF is detected, the watcher will be notified and this stream + * is detached from the underlying stream. This prevents multiple + * notifications from this stream. + * + * @param eof the result of the calling read operation. + * A negative value indicates that EOF is reached. + * + * @throws IOException + * in case of an IO problem on closing the underlying stream + */ + protected void checkEOF(final int eof) throws IOException { + + if ((wrappedStream != null) && (eof < 0)) { + try { + boolean scws = true; // should close wrapped stream? + if (eofWatcher != null) { + scws = eofWatcher.eofDetected(wrappedStream); + } + if (scws) { + wrappedStream.close(); + } + } finally { + wrappedStream = null; + } + } + } + + /** + * Detects stream close and notifies the watcher. + * There's not much to detect since this is called by {@link #close close}. + * The watcher will only be notified if this stream is closed + * for the first time and before EOF has been detected. + * This stream will be detached from the underlying stream to prevent + * multiple notifications to the watcher. + * + * @throws IOException + * in case of an IO problem on closing the underlying stream + */ + protected void checkClose() throws IOException { + + if (wrappedStream != null) { + try { + boolean scws = true; // should close wrapped stream? + if (eofWatcher != null) { + scws = eofWatcher.streamClosed(wrappedStream); + } + if (scws) { + wrappedStream.close(); + } + } finally { + wrappedStream = null; + } + } + } + + /** + * Detects stream abort and notifies the watcher. + * There's not much to detect since this is called by + * {@link #abortConnection abortConnection}. + * The watcher will only be notified if this stream is aborted + * for the first time and before EOF has been detected or the + * stream has been {@link #close closed} gracefully. + * This stream will be detached from the underlying stream to prevent + * multiple notifications to the watcher. + * + * @throws IOException + * in case of an IO problem on closing the underlying stream + */ + protected void checkAbort() throws IOException { + + if (wrappedStream != null) { + try { + boolean scws = true; // should close wrapped stream? + if (eofWatcher != null) { + scws = eofWatcher.streamAbort(wrappedStream); + } + if (scws) { + wrappedStream.close(); + } + } finally { + wrappedStream = null; + } + } + } + + /** + * Same as {@link #close close()}. + */ + public void releaseConnection() throws IOException { + close(); + } + + /** + * Aborts this stream. + * This is a special version of {@link #close close()} which prevents + * re-use of the underlying connection, if any. Calling this method + * indicates that there should be no attempt to read until the end of + * the stream. + */ + public void abortConnection() throws IOException { + // tolerate multiple calls + selfClosed = true; + checkAbort(); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorWatcher.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorWatcher.java new file mode 100644 index 000000000..c57772fb8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/EofSensorWatcher.java @@ -0,0 +1,95 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A watcher for {@link EofSensorInputStream}. Each stream will notify its + * watcher at most once. + * + * @since 4.0 + */ +public interface EofSensorWatcher { + + /** + * Indicates that EOF is detected. + * + * @param wrapped the underlying stream which has reached EOF + * + * @return true if wrapped should be closed, + * false if it should be left alone + * + * @throws IOException + * in case of an IO problem, for example if the watcher itself + * closes the underlying stream. The caller will leave the + * wrapped stream alone, as if false was returned. + */ + boolean eofDetected(InputStream wrapped) + throws IOException; + + /** + * Indicates that the {@link EofSensorInputStream stream} is closed. + * This method will be called only if EOF was not detected + * before closing. Otherwise, {@link #eofDetected eofDetected} is called. + * + * @param wrapped the underlying stream which has not reached EOF + * + * @return true if wrapped should be closed, + * false if it should be left alone + * + * @throws IOException + * in case of an IO problem, for example if the watcher itself + * closes the underlying stream. The caller will leave the + * wrapped stream alone, as if false was returned. + */ + boolean streamClosed(InputStream wrapped) + throws IOException; + + /** + * Indicates that the {@link EofSensorInputStream stream} is aborted. + * This method will be called only if EOF was not detected + * before aborting. Otherwise, {@link #eofDetected eofDetected} is called. + *

    + * This method will also be invoked when an input operation causes an + * IOException to be thrown to make sure the input stream gets shut down. + * + * @param wrapped the underlying stream which has not reached EOF + * + * @return true if wrapped should be closed, + * false if it should be left alone + * + * @throws IOException + * in case of an IO problem, for example if the watcher itself + * closes the underlying stream. The caller will leave the + * wrapped stream alone, as if false was returned. + */ + boolean streamAbort(InputStream wrapped) + throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpClientConnectionManager.java new file mode 100644 index 000000000..0381a803f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpClientConnectionManager.java @@ -0,0 +1,176 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Represents a manager of persistent client connections. + *

    + * The purpose of an HTTP connection manager is to serve as a factory for new + * HTTP connections, manage persistent connections and synchronize access to + * persistent connections making sure that only one thread of execution can + * have access to a connection at a time. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.3 + */ +public interface HttpClientConnectionManager { + + /** + * Returns a new {@link ConnectionRequest}, from which a + * {@link HttpClientConnection} can be obtained or the request can be + * aborted. + *

    + * Please note that newly allocated connections can be returned + * in the closed state. The consumer of that connection is responsible + * for fully establishing the route the to the connection target + * by calling {@link #connect(ch.boye.httpclientandroidlib.HttpClientConnection, + * ch.boye.httpclientandroidlib.conn.routing.HttpRoute, int, + * ch.boye.httpclientandroidlib.protocol.HttpContext) connect} in order to connect + * directly to the target or to the first proxy hop, optionally calling + * {@link #upgrade(ch.boye.httpclientandroidlib.HttpClientConnection, + * ch.boye.httpclientandroidlib.conn.routing.HttpRoute, + * ch.boye.httpclientandroidlib.protocol.HttpContext) upgrade} method to upgrade + * the connection after having executed CONNECT method to + * all intermediate proxy hops and and finally calling {@link #routeComplete( + * ch.boye.httpclientandroidlib.HttpClientConnection, + * ch.boye.httpclientandroidlib.conn.routing.HttpRoute, + * ch.boye.httpclientandroidlib.protocol.HttpContext) routeComplete} to mark the route + * as fully completed. + * + * @param route HTTP route of the requested connection. + * @param state expected state of the connection or null + * if the connection is not expected to carry any state. + */ + ConnectionRequest requestConnection(HttpRoute route, Object state); + + /** + * Releases the connection back to the manager making it potentially + * re-usable by other consumers. Optionally, the maximum period + * of how long the manager should keep the connection alive can be + * defined using validDuration and timeUnit + * parameters. + * + * @param conn the managed connection to release. + * @param validDuration the duration of time this connection is valid for reuse. + * @param timeUnit the time unit. + * + * @see #closeExpiredConnections() + */ + void releaseConnection( + HttpClientConnection conn, Object newState, long validDuration, TimeUnit timeUnit); + + /** + * Connects the underlying connection socket to the connection target in case + * of a direct route or to the first proxy hop in case of a route via a proxy + * (or multiple proxies). + * + * @param conn the managed connection. + * @param route the route of the connection. + * @param connectTimeout connect timeout in milliseconds. + * @param context the actual HTTP context. + * @throws IOException + */ + void connect( + HttpClientConnection conn, + HttpRoute route, + int connectTimeout, + HttpContext context) throws IOException; + + /** + * Upgrades the underlying connection socket to TLS/SSL (or another layering + * protocol) after having executed CONNECT method to all + * intermediate proxy hops + * + * @param conn the managed connection. + * @param route the route of the connection. + * @param context the actual HTTP context. + * @throws IOException + */ + void upgrade( + HttpClientConnection conn, + HttpRoute route, + HttpContext context) throws IOException; + + /** + * Marks the connection as fully established with all its intermediate + * hops completed. + * + * @param conn the managed connection. + * @param route the route of the connection. + * @param context the actual HTTP context. + * @throws IOException + */ + void routeComplete( + HttpClientConnection conn, + HttpRoute route, + HttpContext context) throws IOException; + + /** + * Closes idle connections in the pool. + *

    + * Open connections in the pool that have not been used for the + * timespan given by the argument will be closed. + * Currently allocated connections are not subject to this method. + * Times will be checked with milliseconds precision + * + * All expired connections will also be closed. + * + * @param idletime the idle time of connections to be closed + * @param tunit the unit for the idletime + * + * @see #closeExpiredConnections() + */ + void closeIdleConnections(long idletime, TimeUnit tunit); + + /** + * Closes all expired connections in the pool. + *

    + * Open connections in the pool that have not been used for + * the timespan defined when the connection was released will be closed. + * Currently allocated connections are not subject to this method. + * Times will be checked with milliseconds precision. + */ + void closeExpiredConnections(); + + /** + * Shuts down this connection manager and releases allocated resources. + * This includes closing all connections, whether they are currently + * used or not. + */ + void shutdown(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpConnectionFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpConnectionFactory.java new file mode 100644 index 000000000..be910921f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpConnectionFactory.java @@ -0,0 +1,41 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import ch.boye.httpclientandroidlib.HttpConnection; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; + +/** + * Generic {@link HttpConnection} factory. + * + * @since 4.3 + */ +public interface HttpConnectionFactory { + + C create(T route, ConnectionConfig config); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpHostConnectException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpHostConnectException.java new file mode 100644 index 000000000..9b1a1c57a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpHostConnectException.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.util.Arrays; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * A {@link ConnectException} that specifies the {@link HttpHost} that was + * being connected to. + * + * @since 4.0 + */ +@Immutable +public class HttpHostConnectException extends ConnectException { + + private static final long serialVersionUID = -3194482710275220224L; + + private final HttpHost host; + + /** + * @deprecated (4.3) use {@link #HttpHostConnectException(java.io.IOException, ch.boye.httpclientandroidlib.HttpHost, + * java.net.InetAddress...)} + */ + @Deprecated + public HttpHostConnectException(final HttpHost host, final ConnectException cause) { + this(cause, host, null); + } + + /** + * Creates a HttpHostConnectException based on original {@link java.io.IOException}. + * + * @since 4.3 + */ + public HttpHostConnectException( + final IOException cause, + final HttpHost host, + final InetAddress... remoteAddresses) { + super("Connect to " + + (host != null ? host.toHostString() : "remote host") + + (remoteAddresses != null && remoteAddresses .length > 0 ? + " " + Arrays.asList(remoteAddresses) : "") + + ((cause != null && cause.getMessage() != null) ? + " failed: " + cause.getMessage() : " refused")); + this.host = host; + initCause(cause); + } + + public HttpHost getHost() { + return this.host; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpInetSocketAddress.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpInetSocketAddress.java new file mode 100644 index 000000000..3bd878d21 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpInetSocketAddress.java @@ -0,0 +1,65 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Extended {@link InetSocketAddress} implementation that also provides access to the original + * {@link HttpHost} used to resolve the address. + * + * @since 4.2 no longer used. + * + * @deprecated (4.3) + */ +@Deprecated +public class HttpInetSocketAddress extends InetSocketAddress { + + private static final long serialVersionUID = -6650701828361907957L; + + private final HttpHost httphost; + + public HttpInetSocketAddress(final HttpHost httphost, final InetAddress addr, final int port) { + super(addr, port); + Args.notNull(httphost, "HTTP host"); + this.httphost = httphost; + } + + public HttpHost getHttpHost() { + return this.httphost; + } + + @Override + public String toString() { + return this.httphost.getHostName() + ":" + getPort(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpRoutedConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpRoutedConnection.java new file mode 100644 index 000000000..956e61208 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/HttpRoutedConnection.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import javax.net.ssl.SSLSession; + +import ch.boye.httpclientandroidlib.HttpInetConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; + +/** + * Interface to access routing information of a client side connection. + * + * @since 4.1 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. + */ +@Deprecated +public interface HttpRoutedConnection extends HttpInetConnection { + + /** + * Indicates whether this connection is secure. + * The return value is well-defined only while the connection is open. + * It may change even while the connection is open. + * + * @return true if this connection is secure, + * false otherwise + */ + boolean isSecure(); + + /** + * Obtains the current route of this connection. + * + * @return the route established so far, or + * null if not connected + */ + HttpRoute getRoute(); + + /** + * Obtains the SSL session of the underlying connection, if any. + * If this connection is open, and the underlying socket is an + * {@link javax.net.ssl.SSLSocket SSLSocket}, the SSL session of + * that socket is obtained. This is a potentially blocking operation. + *
    + * Note: Whether the underlying socket is an SSL socket + * can not necessarily be determined via {@link #isSecure}. + * Plain sockets may be considered secure, for example if they are + * connected to a known host in the same network segment. + * On the other hand, SSL sockets may be considered insecure, + * for example depending on the chosen cipher suite. + * + * @return the underlying SSL session if available, + * null otherwise + */ + SSLSession getSSLSession(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedClientConnection.java new file mode 100644 index 000000000..a54567b4f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedClientConnection.java @@ -0,0 +1,228 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLSession; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A client-side connection with advanced connection logic. + * Instances are typically obtained from a connection manager. + * + * @since 4.0 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. + */ +@Deprecated +public interface ManagedClientConnection extends + HttpClientConnection, HttpRoutedConnection, ManagedHttpClientConnection, ConnectionReleaseTrigger { + + /** + * Indicates whether this connection is secure. + * The return value is well-defined only while the connection is open. + * It may change even while the connection is open. + * + * @return true if this connection is secure, + * false otherwise + */ + boolean isSecure(); + + /** + * Obtains the current route of this connection. + * + * @return the route established so far, or + * null if not connected + */ + HttpRoute getRoute(); + + /** + * Obtains the SSL session of the underlying connection, if any. + * If this connection is open, and the underlying socket is an + * {@link javax.net.ssl.SSLSocket SSLSocket}, the SSL session of + * that socket is obtained. This is a potentially blocking operation. + *
    + * Note: Whether the underlying socket is an SSL socket + * can not necessarily be determined via {@link #isSecure}. + * Plain sockets may be considered secure, for example if they are + * connected to a known host in the same network segment. + * On the other hand, SSL sockets may be considered insecure, + * for example depending on the chosen cipher suite. + * + * @return the underlying SSL session if available, + * null otherwise + */ + SSLSession getSSLSession(); + + /** + * Opens this connection according to the given route. + * + * @param route the route along which to open. It will be opened to + * the first proxy if present, or directly to the target. + * @param context the context for opening this connection + * @param params the parameters for opening this connection + * + * @throws IOException in case of a problem + */ + void open(HttpRoute route, HttpContext context, HttpParams params) + throws IOException; + + /** + * Indicates that a tunnel to the target has been established. + * The route is the one previously passed to {@link #open open}. + * Subsequently, {@link #layerProtocol layerProtocol} can be called + * to layer the TLS/SSL protocol on top of the tunnelled connection. + *
    + * Note: In HttpClient 3, a call to the corresponding method + * would automatically trigger the layering of the TLS/SSL protocol. + * This is not the case anymore, you can establish a tunnel without + * layering a new protocol over the connection. + * + * @param secure true if the tunnel should be considered + * secure, false otherwise + * @param params the parameters for tunnelling this connection + * + * @throws IOException in case of a problem + */ + void tunnelTarget(boolean secure, HttpParams params) + throws IOException; + + /** + * Indicates that a tunnel to an intermediate proxy has been established. + * This is used exclusively for so-called proxy chains, where + * a request has to pass through multiple proxies before reaching the + * target. In that case, all proxies but the last need to be tunnelled + * when establishing the connection. Tunnelling of the last proxy to the + * target is optional and would be indicated via {@link #tunnelTarget}. + * + * @param next the proxy to which the tunnel was established. + * This is not the proxy through which + * the tunnel was established, but the new end point + * of the tunnel. The tunnel does not yet + * reach to the target, use {@link #tunnelTarget} + * to indicate an end-to-end tunnel. + * @param secure true if the connection should be + * considered secure, false otherwise + * @param params the parameters for tunnelling this connection + * + * @throws IOException in case of a problem + */ + void tunnelProxy(HttpHost next, boolean secure, HttpParams params) + throws IOException; + + /** + * Layers a new protocol on top of a {@link #tunnelTarget tunnelled} + * connection. This is typically used to create a TLS/SSL connection + * through a proxy. + * The route is the one previously passed to {@link #open open}. + * It is not guaranteed that the layered connection is + * {@link #isSecure secure}. + * + * @param context the context for layering on top of this connection + * @param params the parameters for layering on top of this connection + * + * @throws IOException in case of a problem + */ + void layerProtocol(HttpContext context, HttpParams params) + throws IOException; + + /** + * Marks this connection as being in a reusable communication state. + * The checkpoints for reuseable communication states (in the absence + * of pipelining) are before sending a request and after receiving + * the response in its entirety. + * The connection will automatically clear the checkpoint when + * used for communication. A call to this method indicates that + * the next checkpoint has been reached. + *
    + * A reusable communication state is necessary but not sufficient + * for the connection to be reused. + * A {@link #getRoute route} mismatch, the connection being closed, + * or other circumstances might prevent reuse. + */ + void markReusable(); + + /** + * Marks this connection as not being in a reusable state. + * This can be used immediately before releasing this connection + * to prevent its reuse. Reasons for preventing reuse include + * error conditions and the evaluation of a + * {@link ch.boye.httpclientandroidlib.ConnectionReuseStrategy reuse strategy}. + *
    + * Note: + * It is not necessary to call here before writing to + * or reading from this connection. Communication attempts will + * automatically unmark the state as non-reusable. It can then + * be switched back using {@link #markReusable markReusable}. + */ + void unmarkReusable(); + + /** + * Indicates whether this connection is in a reusable communication state. + * See {@link #markReusable markReusable} and + * {@link #unmarkReusable unmarkReusable} for details. + * + * @return true if this connection is marked as being in + * a reusable communication state, + * false otherwise + */ + boolean isMarkedReusable(); + + /** + * Assigns a state object to this connection. Connection managers may make + * use of the connection state when allocating persistent connections. + * + * @param state The state object + */ + void setState(Object state); + + /** + * Returns the state object associated with this connection. + * + * @return The state object + */ + Object getState(); + + /** + * Sets the duration that this connection can remain idle before it is + * reused. The connection should not be used again if this time elapses. The + * idle duration must be reset after each request sent over this connection. + * The elapsed time starts counting when the connection is released, which + * is typically after the headers (and any response body, if present) is + * fully consumed. + */ + void setIdleDuration(long duration, TimeUnit unit); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedHttpClientConnection.java new file mode 100644 index 000000000..4195d59b7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ManagedHttpClientConnection.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.net.Socket; + +import javax.net.ssl.SSLSession; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpInetConnection; + +/** + * Represents a managed connection whose state and life cycle is managed by + * a connection manager. This interface extends {@link HttpClientConnection} + * with methods to bind the connection to an arbitrary socket and + * to obtain SSL session details. + * + * @since 4.3 + */ +public interface ManagedHttpClientConnection extends HttpClientConnection, HttpInetConnection { + + /** + * Returns connection ID which is expected to be unique + * for the life span of the connection manager. + */ + String getId(); + + /** + * Binds this connection to the given socket. The connection + * is considered open if it is bound and the underlying socket + * is connection to a remote host. + * + * @param socket the socket to bind the connection to. + * @throws IOException + */ + void bind(Socket socket) throws IOException; + + /** + * Returns the underlying socket. + */ + Socket getSocket(); + + /** + * Obtains the SSL session of the underlying connection, if any. + * If this connection is open, and the underlying socket is an + * {@link javax.net.ssl.SSLSocket SSLSocket}, the SSL session of + * that socket is obtained. This is a potentially blocking operation. + * + * @return the underlying SSL session if available, + * null otherwise + */ + SSLSession getSSLSession(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/MultihomePlainSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/MultihomePlainSocketFactory.java new file mode 100644 index 000000000..c4c34f769 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/MultihomePlainSocketFactory.java @@ -0,0 +1,173 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.scheme.SocketFactory; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Socket factory that implements a simple multi-home fail-over on connect failure, + * provided the same hostname resolves to multiple {@link InetAddress}es. Please note + * the {@link #connectSocket(Socket, String, int, InetAddress, int, HttpParams)} + * method cannot be reliably interrupted by closing the socket returned by the + * {@link #createSocket()} method. + * + * @since 4.0 + * + * @deprecated (4.1) Do not use. For multihome support socket factories must implement + * {@link ch.boye.httpclientandroidlib.conn.scheme.SchemeSocketFactory} interface. + */ +@Deprecated +@Immutable +public final class MultihomePlainSocketFactory implements SocketFactory { + + /** + * The factory singleton. + */ + private static final + MultihomePlainSocketFactory DEFAULT_FACTORY = new MultihomePlainSocketFactory(); + + /** + * Gets the singleton instance of this class. + * @return the one and only plain socket factory + */ + public static MultihomePlainSocketFactory getSocketFactory() { + return DEFAULT_FACTORY; + } + + /** + * Restricted default constructor. + */ + private MultihomePlainSocketFactory() { + super(); + } + + + // non-javadoc, see interface ch.boye.httpclientandroidlib.conn.SocketFactory + public Socket createSocket() { + return new Socket(); + } + + /** + * Attempts to connects the socket to any of the {@link InetAddress}es the + * given host name resolves to. If connection to all addresses fail, the + * last I/O exception is propagated to the caller. + * + * @param socket socket to connect to any of the given addresses + * @param host Host name to connect to + * @param port the port to connect to + * @param localAddress local address + * @param localPort local port + * @param params HTTP parameters + * + * @throws IOException if an error occurs during the connection + * @throws SocketTimeoutException if timeout expires before connecting + */ + public Socket connectSocket(final Socket socket, final String host, final int port, + final InetAddress localAddress, final int localPort, + final HttpParams params) + throws IOException { + Args.notNull(host, "Target host"); + Args.notNull(params, "HTTP parameters"); + + Socket sock = socket; + if (sock == null) { + sock = createSocket(); + } + + if ((localAddress != null) || (localPort > 0)) { + final InetSocketAddress isa = new InetSocketAddress(localAddress, + localPort > 0 ? localPort : 0); + sock.bind(isa); + } + + final int timeout = HttpConnectionParams.getConnectionTimeout(params); + + final InetAddress[] inetadrs = InetAddress.getAllByName(host); + final List addresses = new ArrayList(inetadrs.length); + addresses.addAll(Arrays.asList(inetadrs)); + Collections.shuffle(addresses); + + IOException lastEx = null; + for (final InetAddress remoteAddress: addresses) { + try { + sock.connect(new InetSocketAddress(remoteAddress, port), timeout); + break; + } catch (final SocketTimeoutException ex) { + throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out"); + } catch (final IOException ex) { + // create new socket + sock = new Socket(); + // keep the last exception and retry + lastEx = ex; + } + } + if (lastEx != null) { + throw lastEx; + } + return sock; + } // connectSocket + + + /** + * Checks whether a socket connection is secure. + * This factory creates plain socket connections + * which are not considered secure. + * + * @param sock the connected socket + * + * @return false + * + * @throws IllegalArgumentException if the argument is invalid + */ + public final boolean isSecure(final Socket sock) + throws IllegalArgumentException { + + Args.notNull(sock, "Socket"); + // This check is performed last since it calls a method implemented + // by the argument object. getClass() is final in java.lang.Object. + Asserts.check(!sock.isClosed(), "Socket is closed"); + return false; + + } // isSecure + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/OperatedClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/OperatedClientConnection.java new file mode 100644 index 000000000..3f8df0591 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/OperatedClientConnection.java @@ -0,0 +1,155 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpInetConnection; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * A client-side connection that relies on outside logic to connect sockets to the + * appropriate hosts. It can be operated directly by an application, or through an + * {@link ClientConnectionOperator operator}. + * + * @since 4.0 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. + */ +@Deprecated +public interface OperatedClientConnection extends HttpClientConnection, HttpInetConnection { + + /** + * Obtains the target host for this connection. + * If the connection is to a proxy but not tunnelled, this is + * the proxy. If the connection is tunnelled through a proxy, + * this is the target of the tunnel. + *
    + * The return value is well-defined only while the connection is open. + * It may change even while the connection is open, + * because of an {@link #update update}. + * + * @return the host to which this connection is opened + */ + HttpHost getTargetHost(); + + /** + * Indicates whether this connection is secure. + * The return value is well-defined only while the connection is open. + * It may change even while the connection is open, + * because of an {@link #update update}. + * + * @return true if this connection is secure, + * false otherwise + */ + boolean isSecure(); + + /** + * Obtains the socket for this connection. + * The return value is well-defined only while the connection is open. + * It may change even while the connection is open, + * because of an {@link #update update}. + * + * @return the socket for communicating with the + * {@link #getTargetHost target host} + */ + Socket getSocket(); + + /** + * Signals that this connection is in the process of being open. + *

    + * By calling this method, the connection can be re-initialized + * with a new Socket instance before {@link #openCompleted} is called. + * This enabled the connection to close that socket if + * {@link ch.boye.httpclientandroidlib.HttpConnection#shutdown shutdown} + * is called before it is fully open. Closing an unconnected socket + * will interrupt a thread that is blocked on the connect. + * Otherwise, that thread will either time out on the connect, + * or it returns successfully and then opens this connection + * which was just shut down. + *

    + * This method can be called multiple times if the connection + * is layered over another protocol. Note: This method + * will not close the previously used socket. It is + * the caller's responsibility to close that socket if it is + * no longer required. + *

    + * The caller must invoke {@link #openCompleted} in order to complete + * the process. + * + * @param sock the unconnected socket which is about to + * be connected. + * @param target the target host of this connection + */ + void opening(Socket sock, HttpHost target) + throws IOException; + + /** + * Signals that the connection has been successfully open. + * An attempt to call this method on an open connection will cause + * an exception. + * + * @param secure true if this connection is secure, for + * example if an SSLSocket is used, or + * false if it is not secure + * @param params parameters for this connection. The parameters will + * be used when creating dependent objects, for example + * to determine buffer sizes. + */ + void openCompleted(boolean secure, HttpParams params) + throws IOException; + + /** + * Updates this connection. + * A connection can be updated only while it is open. + * Updates are used for example when a tunnel has been established, + * or when a TLS/SSL connection has been layered on top of a plain + * socket connection. + *
    + * Note: Updating the connection will not close the + * previously used socket. It is the caller's responsibility to close + * that socket if it is no longer required. + * + * @param sock the new socket for communicating with the target host, + * or null to continue using the old socket. + * If null is passed, helper objects that + * depend on the socket should be re-used. In that case, + * some changes in the parameters will not take effect. + * @param target the new target host of this connection + * @param secure true if this connection is now secure, + * false if it is not secure + * @param params new parameters for this connection + */ + void update(Socket sock, HttpHost target, + boolean secure, HttpParams params) + throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/SchemePortResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/SchemePortResolver.java new file mode 100644 index 000000000..10df60f04 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/SchemePortResolver.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn; + +import ch.boye.httpclientandroidlib.HttpHost; + +/** + * Strategy for default port resolution for protocol schemes. + * + * @since 4.3 + */ +public interface SchemePortResolver { + + /** + * Returns the actual port for the host based on the protocol scheme. + */ + int resolve(HttpHost host) throws UnsupportedSchemeException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/UnsupportedSchemeException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/UnsupportedSchemeException.java new file mode 100644 index 000000000..c8034191e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/UnsupportedSchemeException.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals failure to establish connection using an unknown protocol scheme. + * + * @since 4.3 + */ +@Immutable +public class UnsupportedSchemeException extends IOException { + + private static final long serialVersionUID = 3597127619218687636L; + + /** + * Creates a UnsupportedSchemeException with the specified detail message. + */ + public UnsupportedSchemeException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/package-info.java new file mode 100644 index 000000000..6d83f5a8b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client connection management APIs. + */ +package ch.boye.httpclientandroidlib.conn; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionPNames.java new file mode 100644 index 000000000..59246459f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionPNames.java @@ -0,0 +1,64 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.params; + +/** + * Parameter names for HTTP client connections. + * + * @since 4.0 + * + * @deprecated (4.1) use custom {@link + * ch.boye.httpclientandroidlib.impl.conn.DefaultHttpResponseParser} implementation. + */ +@Deprecated +public interface ConnConnectionPNames { + + /** + * Defines the maximum number of ignorable lines before we expect + * a HTTP response's status line. + *

    + * With HTTP/1.1 persistent connections, the problem arises that + * broken scripts could return a wrong Content-Length + * (there are more bytes sent than specified). + * Unfortunately, in some cases, this cannot be detected after the + * bad response, but only before the next one. + * So HttpClient must be able to skip those surplus lines this way. + *

    + *

    + * This parameter expects a value of type {@link Integer}. + * 0 disallows all garbage/empty lines before the status line. + * Use {@link java.lang.Integer#MAX_VALUE} for unlimited number. + *

    + * + * @deprecated (4.1) Use custom {@link + * ch.boye.httpclientandroidlib.impl.conn.DefaultHttpResponseParser} implementation + */ + @Deprecated + public static final String MAX_STATUS_LINE_GARBAGE = "http.connection.max-status-line-garbage"; + + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionParamBean.java new file mode 100644 index 000000000..8c8b1b2dc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnConnectionParamBean.java @@ -0,0 +1,59 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.params; + +import ch.boye.httpclientandroidlib.params.HttpAbstractParamBean; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate HTTP client connection parameters + * using Java Beans conventions. + * + * @since 4.0 + * + * @deprecated (4.1) use custom {@link + * ch.boye.httpclientandroidlib.impl.conn.DefaultHttpResponseParser} implementation. + */ +@Deprecated +public class ConnConnectionParamBean extends HttpAbstractParamBean { + + public ConnConnectionParamBean (final HttpParams params) { + super(params); + } + + /** + * @deprecated (4.2) Use custom {@link + * ch.boye.httpclientandroidlib.impl.conn.DefaultHttpResponseParser} implementation + */ + @Deprecated + public void setMaxStatusLineGarbage (final int maxStatusLineGarbage) { + params.setIntParameter(ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, maxStatusLineGarbage); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerPNames.java new file mode 100644 index 000000000..16a79ca18 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerPNames.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.params; + +/** + * Parameter names for connection managers in HttpConn. + * + * @since 4.0 + * + * @deprecated (4.1) use configuration methods of the specific connection manager implementation. +*/ +@Deprecated +public interface ConnManagerPNames { + + /** + * Defines the timeout in milliseconds used when retrieving an instance of + * {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection} from the + * {@link ch.boye.httpclientandroidlib.conn.ClientConnectionManager}. + *

    + * This parameter expects a value of type {@link Long}. + */ + public static final String TIMEOUT = "http.conn-manager.timeout"; + + /** + * Defines the maximum number of connections per route. + * This limit is interpreted by client connection managers + * and applies to individual manager instances. + *

    + * This parameter expects a value of type {@link ConnPerRoute}. + *

    + */ + public static final String MAX_CONNECTIONS_PER_ROUTE = "http.conn-manager.max-per-route"; + + /** + * Defines the maximum number of connections in total. + * This limit is interpreted by client connection managers + * and applies to individual manager instances. + *

    + * This parameter expects a value of type {@link Integer}. + */ + public static final String MAX_TOTAL_CONNECTIONS = "http.conn-manager.max-total"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParamBean.java new file mode 100644 index 000000000..735a25360 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParamBean.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.params; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.HttpAbstractParamBean; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate connection manager parameters + * using Java Beans conventions. + * + * @since 4.0 + * + * @deprecated (4.1) use configuration methods of the specific connection manager implementation. + */ +@NotThreadSafe +@Deprecated +public class ConnManagerParamBean extends HttpAbstractParamBean { + + public ConnManagerParamBean (final HttpParams params) { + super(params); + } + + public void setTimeout (final long timeout) { + params.setLongParameter(ConnManagerPNames.TIMEOUT, timeout); + } + + public void setMaxTotalConnections (final int maxConnections) { + params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, maxConnections); + } + + public void setConnectionsPerRoute(final ConnPerRouteBean connPerRoute) { + params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, connPerRoute); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParams.java new file mode 100644 index 000000000..e6521134a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnManagerParams.java @@ -0,0 +1,147 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.params; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * An adaptor for manipulating HTTP connection management + * parameters in {@link HttpParams}. + * + * @since 4.0 + * + * @see ConnManagerPNames + * + * @deprecated (4.1) use configuration methods of the specific connection manager implementation. + */ +@Deprecated +@Immutable +public final class ConnManagerParams implements ConnManagerPNames { + + /** The default maximum number of connections allowed overall */ + public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20; + + /** + * Returns the timeout in milliseconds used when retrieving a + * {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection} from the + * {@link ch.boye.httpclientandroidlib.conn.ClientConnectionManager}. + * + * @return timeout in milliseconds. + * + * @deprecated (4.1) use {@link + * ch.boye.httpclientandroidlib.params.HttpConnectionParams#getConnectionTimeout(HttpParams)} + */ + @Deprecated + public static long getTimeout(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getLongParameter(TIMEOUT, 0); + } + + /** + * Sets the timeout in milliseconds used when retrieving a + * {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection} from the + * {@link ch.boye.httpclientandroidlib.conn.ClientConnectionManager}. + * + * @param timeout the timeout in milliseconds + * + * @deprecated (4.1) use {@link + * ch.boye.httpclientandroidlib.params.HttpConnectionParams#setConnectionTimeout(HttpParams, int)} + */ + @Deprecated + public static void setTimeout(final HttpParams params, final long timeout) { + Args.notNull(params, "HTTP parameters"); + params.setLongParameter(TIMEOUT, timeout); + } + + /** The default maximum number of connections allowed per host */ + private static final ConnPerRoute DEFAULT_CONN_PER_ROUTE = new ConnPerRoute() { + + public int getMaxForRoute(final HttpRoute route) { + return ConnPerRouteBean.DEFAULT_MAX_CONNECTIONS_PER_ROUTE; + } + + }; + + /** + * Sets lookup interface for maximum number of connections allowed per route. + * + * @param params HTTP parameters + * @param connPerRoute lookup interface for maximum number of connections allowed + * per route + */ + public static void setMaxConnectionsPerRoute(final HttpParams params, + final ConnPerRoute connPerRoute) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(MAX_CONNECTIONS_PER_ROUTE, connPerRoute); + } + + /** + * Returns lookup interface for maximum number of connections allowed per route. + * + * @param params HTTP parameters + * + * @return lookup interface for maximum number of connections allowed per route. + */ + public static ConnPerRoute getMaxConnectionsPerRoute(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + ConnPerRoute connPerRoute = (ConnPerRoute) params.getParameter(MAX_CONNECTIONS_PER_ROUTE); + if (connPerRoute == null) { + connPerRoute = DEFAULT_CONN_PER_ROUTE; + } + return connPerRoute; + } + + /** + * Sets the maximum number of connections allowed. + * + * @param params HTTP parameters + * @param maxTotalConnections The maximum number of connections allowed. + */ + public static void setMaxTotalConnections( + final HttpParams params, + final int maxTotalConnections) { + Args.notNull(params, "HTTP parameters"); + params.setIntParameter(MAX_TOTAL_CONNECTIONS, maxTotalConnections); + } + + /** + * Gets the maximum number of connections allowed. + * + * @param params HTTP parameters + * + * @return The maximum number of connections allowed. + */ + public static int getMaxTotalConnections( + final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getIntParameter(MAX_TOTAL_CONNECTIONS, DEFAULT_MAX_TOTAL_CONNECTIONS); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRoute.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRoute.java new file mode 100644 index 000000000..ebaad93cb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRoute.java @@ -0,0 +1,46 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.params; + +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; + +/** + * This interface is intended for looking up maximum number of connections + * allowed for a given route. This class can be used by pooling + * {@link ch.boye.httpclientandroidlib.conn.ClientConnectionManager connection managers} for + * a fine-grained control of connections on a per route basis. + * + * @since 4.0 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +public interface ConnPerRoute { + + int getMaxForRoute(HttpRoute route); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRouteBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRouteBean.java new file mode 100644 index 000000000..6cc6da8ab --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnPerRouteBean.java @@ -0,0 +1,112 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.params; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * This class maintains a map of HTTP routes to maximum number of connections allowed + * for those routes. This class can be used by pooling + * {@link ch.boye.httpclientandroidlib.conn.ClientConnectionManager connection managers} for + * a fine-grained control of connections on a per route basis. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.ConnPoolControl} + */ +@Deprecated +@ThreadSafe +public final class ConnPerRouteBean implements ConnPerRoute { + + /** The default maximum number of connections allowed per host */ + public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 2; // Per RFC 2616 sec 8.1.4 + + private final ConcurrentHashMap maxPerHostMap; + + private volatile int defaultMax; + + public ConnPerRouteBean(final int defaultMax) { + super(); + this.maxPerHostMap = new ConcurrentHashMap(); + setDefaultMaxPerRoute(defaultMax); + } + + public ConnPerRouteBean() { + this(DEFAULT_MAX_CONNECTIONS_PER_ROUTE); + } + + public int getDefaultMax() { + return this.defaultMax; + } + + /** + * @since 4.1 + */ + public int getDefaultMaxPerRoute() { + return this.defaultMax; + } + + public void setDefaultMaxPerRoute(final int max) { + Args.positive(max, "Defautl max per route"); + this.defaultMax = max; + } + + public void setMaxForRoute(final HttpRoute route, final int max) { + Args.notNull(route, "HTTP route"); + Args.positive(max, "Max per route"); + this.maxPerHostMap.put(route, Integer.valueOf(max)); + } + + public int getMaxForRoute(final HttpRoute route) { + Args.notNull(route, "HTTP route"); + final Integer max = this.maxPerHostMap.get(route); + if (max != null) { + return max.intValue(); + } else { + return this.defaultMax; + } + } + + public void setMaxForRoutes(final Map map) { + if (map == null) { + return; + } + this.maxPerHostMap.clear(); + this.maxPerHostMap.putAll(map); + } + + @Override + public String toString() { + return this.maxPerHostMap.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRoutePNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRoutePNames.java new file mode 100644 index 000000000..083216b14 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRoutePNames.java @@ -0,0 +1,79 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.params; + +/** + * Parameter names for connection routing. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}. + */ +@Deprecated +public interface ConnRoutePNames { + + /** + * Parameter for the default proxy. + * The default value will be used by some + * {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner HttpRoutePlanner} + * implementations, in particular the default implementation. + *

    + * This parameter expects a value of type {@link ch.boye.httpclientandroidlib.HttpHost}. + *

    + */ + public static final String DEFAULT_PROXY = "http.route.default-proxy"; + + /** + * Parameter for the local address. + * On machines with multiple network interfaces, this parameter + * can be used to select the network interface from which the + * connection originates. + * It will be interpreted by the standard + * {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner HttpRoutePlanner} + * implementations, in particular the default implementation. + *

    + * This parameter expects a value of type {@link java.net.InetAddress}. + *

    + */ + public static final String LOCAL_ADDRESS = "http.route.local-address"; + + /** + * Parameter for an forced route. + * The forced route will be interpreted by the standard + * {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner HttpRoutePlanner} + * implementations. + * Instead of computing a route, the given forced route will be + * returned, even if it points to the wrong target host. + *

    + * This parameter expects a value of type + * {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoute HttpRoute}. + *

    + */ + public static final String FORCED_ROUTE = "http.route.forced-route"; + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParamBean.java new file mode 100644 index 000000000..d3018ad0b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParamBean.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.params; + +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpAbstractParamBean; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate connection routing parameters + * using Java Beans conventions. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}. + */ +@Deprecated +@NotThreadSafe +public class ConnRouteParamBean extends HttpAbstractParamBean { + + public ConnRouteParamBean (final HttpParams params) { + super(params); + } + + /** @see ConnRoutePNames#DEFAULT_PROXY */ + public void setDefaultProxy (final HttpHost defaultProxy) { + params.setParameter(ConnRoutePNames.DEFAULT_PROXY, defaultProxy); + } + + /** @see ConnRoutePNames#LOCAL_ADDRESS */ + public void setLocalAddress (final InetAddress address) { + params.setParameter(ConnRoutePNames.LOCAL_ADDRESS, address); + } + + /** @see ConnRoutePNames#FORCED_ROUTE */ + public void setForcedRoute (final HttpRoute route) { + params.setParameter(ConnRoutePNames.FORCED_ROUTE, route); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParams.java new file mode 100644 index 000000000..241953dbd --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/ConnRouteParams.java @@ -0,0 +1,178 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.params; + +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * An adaptor for manipulating HTTP routing parameters + * in {@link HttpParams}. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.config.RequestConfig}. + */ +@Deprecated +@Immutable +public class ConnRouteParams implements ConnRoutePNames { + + /** + * A special value indicating "no host". + * This relies on a nonsense scheme name to avoid conflicts + * with actual hosts. Note that this is a valid host. + */ + public static final HttpHost NO_HOST = + new HttpHost("127.0.0.255", 0, "no-host"); // Immutable + + /** + * A special value indicating "no route". + * This is a route with {@link #NO_HOST} as the target. + */ + public static final HttpRoute NO_ROUTE = new HttpRoute(NO_HOST); // Immutable + + /** Disabled default constructor. */ + private ConnRouteParams() { + // no body + } + + /** + * Obtains the {@link ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY} + * parameter value. + * {@link #NO_HOST} will be mapped to null, + * to allow unsetting in a hierarchy. + * + * @param params the parameters in which to look up + * + * @return the default proxy set in the argument parameters, or + * null if not set + */ + public static HttpHost getDefaultProxy(final HttpParams params) { + Args.notNull(params, "Parameters"); + HttpHost proxy = (HttpHost) + params.getParameter(DEFAULT_PROXY); + if ((proxy != null) && NO_HOST.equals(proxy)) { + // value is explicitly unset + proxy = null; + } + return proxy; + } + + /** + * Sets the {@link ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY} + * parameter value. + * + * @param params the parameters in which to set the value + * @param proxy the value to set, may be null. + * Note that {@link #NO_HOST} will be mapped to + * null by {@link #getDefaultProxy}, + * to allow for explicit unsetting in hierarchies. + */ + public static void setDefaultProxy(final HttpParams params, + final HttpHost proxy) { + Args.notNull(params, "Parameters"); + params.setParameter(DEFAULT_PROXY, proxy); + } + + /** + * Obtains the {@link ConnRoutePNames#FORCED_ROUTE FORCED_ROUTE} + * parameter value. + * {@link #NO_ROUTE} will be mapped to null, + * to allow unsetting in a hierarchy. + * + * @param params the parameters in which to look up + * + * @return the forced route set in the argument parameters, or + * null if not set + */ + public static HttpRoute getForcedRoute(final HttpParams params) { + Args.notNull(params, "Parameters"); + HttpRoute route = (HttpRoute) + params.getParameter(FORCED_ROUTE); + if ((route != null) && NO_ROUTE.equals(route)) { + // value is explicitly unset + route = null; + } + return route; + } + + /** + * Sets the {@link ConnRoutePNames#FORCED_ROUTE FORCED_ROUTE} + * parameter value. + * + * @param params the parameters in which to set the value + * @param route the value to set, may be null. + * Note that {@link #NO_ROUTE} will be mapped to + * null by {@link #getForcedRoute}, + * to allow for explicit unsetting in hierarchies. + */ + public static void setForcedRoute(final HttpParams params, + final HttpRoute route) { + Args.notNull(params, "Parameters"); + params.setParameter(FORCED_ROUTE, route); + } + + /** + * Obtains the {@link ConnRoutePNames#LOCAL_ADDRESS LOCAL_ADDRESS} + * parameter value. + * There is no special value that would automatically be mapped to + * null. You can use the wildcard address (0.0.0.0 for IPv4, + * :: for IPv6) to override a specific local address in a hierarchy. + * + * @param params the parameters in which to look up + * + * @return the local address set in the argument parameters, or + * null if not set + */ + public static InetAddress getLocalAddress(final HttpParams params) { + Args.notNull(params, "Parameters"); + final InetAddress local = (InetAddress) + params.getParameter(LOCAL_ADDRESS); + // no explicit unsetting + return local; + } + + /** + * Sets the {@link ConnRoutePNames#LOCAL_ADDRESS LOCAL_ADDRESS} + * parameter value. + * + * @param params the parameters in which to set the value + * @param local the value to set, may be null + */ + public static void setLocalAddress(final HttpParams params, + final InetAddress local) { + Args.notNull(params, "Parameters"); + params.setParameter(LOCAL_ADDRESS, local); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/package-info.java new file mode 100644 index 000000000..32b359e3e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/params/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Deprecated. + * @deprecated (4.3). + */ +package ch.boye.httpclientandroidlib.conn.params; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/BasicRouteDirector.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/BasicRouteDirector.java new file mode 100644 index 000000000..9c653eb3c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/BasicRouteDirector.java @@ -0,0 +1,181 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.routing; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic {@link HttpRouteDirector} implementation. + * + * @since 4.0 + */ +@Immutable +public class BasicRouteDirector implements HttpRouteDirector { + + /** + * Provides the next step. + * + * @param plan the planned route + * @param fact the currently established route, or + * null if nothing is established + * + * @return one of the constants defined in this class, indicating + * either the next step to perform, or success, or failure. + * 0 is for success, a negative value for failure. + */ + public int nextStep(final RouteInfo plan, final RouteInfo fact) { + Args.notNull(plan, "Planned route"); + + int step = UNREACHABLE; + + if ((fact == null) || (fact.getHopCount() < 1)) { + step = firstStep(plan); + } else if (plan.getHopCount() > 1) { + step = proxiedStep(plan, fact); + } else { + step = directStep(plan, fact); + } + + return step; + + } // nextStep + + + /** + * Determines the first step to establish a route. + * + * @param plan the planned route + * + * @return the first step + */ + protected int firstStep(final RouteInfo plan) { + + return (plan.getHopCount() > 1) ? + CONNECT_PROXY : CONNECT_TARGET; + } + + + /** + * Determines the next step to establish a direct connection. + * + * @param plan the planned route + * @param fact the currently established route + * + * @return one of the constants defined in this class, indicating + * either the next step to perform, or success, or failure + */ + protected int directStep(final RouteInfo plan, final RouteInfo fact) { + + if (fact.getHopCount() > 1) { + return UNREACHABLE; + } + if (!plan.getTargetHost().equals(fact.getTargetHost())) + { + return UNREACHABLE; + // If the security is too low, we could now suggest to layer + // a secure protocol on the direct connection. Layering on direct + // connections has not been supported in HttpClient 3.x, we don't + // consider it here until there is a real-life use case for it. + } + + // Should we tolerate if security is better than planned? + // (plan.isSecure() && !fact.isSecure()) + if (plan.isSecure() != fact.isSecure()) { + return UNREACHABLE; + } + + // Local address has to match only if the plan specifies one. + if ((plan.getLocalAddress() != null) && + !plan.getLocalAddress().equals(fact.getLocalAddress()) + ) { + return UNREACHABLE; + } + + return COMPLETE; + } + + + /** + * Determines the next step to establish a connection via proxy. + * + * @param plan the planned route + * @param fact the currently established route + * + * @return one of the constants defined in this class, indicating + * either the next step to perform, or success, or failure + */ + protected int proxiedStep(final RouteInfo plan, final RouteInfo fact) { + + if (fact.getHopCount() <= 1) { + return UNREACHABLE; + } + if (!plan.getTargetHost().equals(fact.getTargetHost())) { + return UNREACHABLE; + } + final int phc = plan.getHopCount(); + final int fhc = fact.getHopCount(); + if (phc < fhc) { + return UNREACHABLE; + } + + for (int i=0; i fhc) + { + return TUNNEL_PROXY; // need to extend the proxy chain + } + + // proxy chain and target are the same, check tunnelling and layering + if ((fact.isTunnelled() && !plan.isTunnelled()) || + (fact.isLayered() && !plan.isLayered())) { + return UNREACHABLE; + } + + if (plan.isTunnelled() && !fact.isTunnelled()) { + return TUNNEL_TARGET; + } + if (plan.isLayered() && !fact.isLayered()) { + return LAYER_PROTOCOL; + } + + // tunnel and layering are the same, remains to check the security + // Should we tolerate if security is better than planned? + // (plan.isSecure() && !fact.isSecure()) + if (plan.isSecure() != fact.isSecure()) { + return UNREACHABLE; + } + + return COMPLETE; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoute.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoute.java new file mode 100644 index 000000000..7bf02d14c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoute.java @@ -0,0 +1,328 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.routing; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * The route for a request. + * + * @since 4.0 + */ +@Immutable +public final class HttpRoute implements RouteInfo, Cloneable { + + /** The target host to connect to. */ + private final HttpHost targetHost; + + /** + * The local address to connect from. + * null indicates that the default should be used. + */ + private final InetAddress localAddress; + + /** The proxy servers, if any. Never null. */ + private final List proxyChain; + + /** Whether the the route is tunnelled through the proxy. */ + private final TunnelType tunnelled; + + /** Whether the route is layered. */ + private final LayerType layered; + + /** Whether the route is (supposed to be) secure. */ + private final boolean secure; + + private HttpRoute(final HttpHost target, final InetAddress local, final List proxies, + final boolean secure, final TunnelType tunnelled, final LayerType layered) { + Args.notNull(target, "Target host"); + this.targetHost = target; + this.localAddress = local; + if (proxies != null && !proxies.isEmpty()) { + this.proxyChain = new ArrayList(proxies); + } else { + this.proxyChain = null; + } + if (tunnelled == TunnelType.TUNNELLED) { + Args.check(this.proxyChain != null, "Proxy required if tunnelled"); + } + this.secure = secure; + this.tunnelled = tunnelled != null ? tunnelled : TunnelType.PLAIN; + this.layered = layered != null ? layered : LayerType.PLAIN; + } + + /** + * Creates a new route with all attributes specified explicitly. + * + * @param target the host to which to route + * @param local the local address to route from, or + * null for the default + * @param proxies the proxy chain to use, or + * null for a direct route + * @param secure true if the route is (to be) secure, + * false otherwise + * @param tunnelled the tunnel type of this route + * @param layered the layering type of this route + */ + public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost[] proxies, + final boolean secure, final TunnelType tunnelled, final LayerType layered) { + this(target, local, proxies != null ? Arrays.asList(proxies) : null, + secure, tunnelled, layered); + } + + /** + * Creates a new route with at most one proxy. + * + * @param target the host to which to route + * @param local the local address to route from, or + * null for the default + * @param proxy the proxy to use, or + * null for a direct route + * @param secure true if the route is (to be) secure, + * false otherwise + * @param tunnelled true if the route is (to be) tunnelled + * via the proxy, + * false otherwise + * @param layered true if the route includes a + * layered protocol, + * false otherwise + */ + public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost proxy, + final boolean secure, final TunnelType tunnelled, final LayerType layered) { + this(target, local, proxy != null ? Collections.singletonList(proxy) : null, + secure, tunnelled, layered); + } + + /** + * Creates a new direct route. + * That is a route without a proxy. + * + * @param target the host to which to route + * @param local the local address to route from, or + * null for the default + * @param secure true if the route is (to be) secure, + * false otherwise + */ + public HttpRoute(final HttpHost target, final InetAddress local, final boolean secure) { + this(target, local, Collections.emptyList(), secure, + TunnelType.PLAIN, LayerType.PLAIN); + } + + /** + * Creates a new direct insecure route. + * + * @param target the host to which to route + */ + public HttpRoute(final HttpHost target) { + this(target, null, Collections.emptyList(), false, + TunnelType.PLAIN, LayerType.PLAIN); + } + + /** + * Creates a new route through a proxy. + * When using this constructor, the proxy MUST be given. + * For convenience, it is assumed that a secure connection will be + * layered over a tunnel through the proxy. + * + * @param target the host to which to route + * @param local the local address to route from, or + * null for the default + * @param proxy the proxy to use + * @param secure true if the route is (to be) secure, + * false otherwise + */ + public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost proxy, + final boolean secure) { + this(target, local, Collections.singletonList(Args.notNull(proxy, "Proxy host")), secure, + secure ? TunnelType.TUNNELLED : TunnelType.PLAIN, + secure ? LayerType.LAYERED : LayerType.PLAIN); + } + + /** + * Creates a new plain route through a proxy. + * + * @param target the host to which to route + * @param proxy the proxy to use + * + * @since 4.3 + */ + public HttpRoute(final HttpHost target, final HttpHost proxy) { + this(target, null, proxy, false); + } + + public final HttpHost getTargetHost() { + return this.targetHost; + } + + public final InetAddress getLocalAddress() { + return this.localAddress; + } + + public final InetSocketAddress getLocalSocketAddress() { + return this.localAddress != null ? new InetSocketAddress(this.localAddress, 0) : null; + } + + public final int getHopCount() { + return proxyChain != null ? proxyChain.size() + 1 : 1; + } + + public final HttpHost getHopTarget(final int hop) { + Args.notNegative(hop, "Hop index"); + final int hopcount = getHopCount(); + Args.check(hop < hopcount, "Hop index exceeds tracked route length"); + if (hop < hopcount - 1) { + return this.proxyChain.get(hop); + } else { + return this.targetHost; + } + } + + public final HttpHost getProxyHost() { + return proxyChain != null && !this.proxyChain.isEmpty() ? this.proxyChain.get(0) : null; + } + + public final TunnelType getTunnelType() { + return this.tunnelled; + } + + public final boolean isTunnelled() { + return (this.tunnelled == TunnelType.TUNNELLED); + } + + public final LayerType getLayerType() { + return this.layered; + } + + public final boolean isLayered() { + return (this.layered == LayerType.LAYERED); + } + + public final boolean isSecure() { + return this.secure; + } + + /** + * Compares this route to another. + * + * @param obj the object to compare with + * + * @return true if the argument is the same route, + * false + */ + @Override + public final boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof HttpRoute) { + final HttpRoute that = (HttpRoute) obj; + return + // Do the cheapest tests first + (this.secure == that.secure) && + (this.tunnelled == that.tunnelled) && + (this.layered == that.layered) && + LangUtils.equals(this.targetHost, that.targetHost) && + LangUtils.equals(this.localAddress, that.localAddress) && + LangUtils.equals(this.proxyChain, that.proxyChain); + } else { + return false; + } + } + + + /** + * Generates a hash code for this route. + * + * @return the hash code + */ + @Override + public final int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.targetHost); + hash = LangUtils.hashCode(hash, this.localAddress); + if (this.proxyChain != null) { + for (final HttpHost element : this.proxyChain) { + hash = LangUtils.hashCode(hash, element); + } + } + hash = LangUtils.hashCode(hash, this.secure); + hash = LangUtils.hashCode(hash, this.tunnelled); + hash = LangUtils.hashCode(hash, this.layered); + return hash; + } + + /** + * Obtains a description of this route. + * + * @return a human-readable representation of this route + */ + @Override + public final String toString() { + final StringBuilder cab = new StringBuilder(50 + getHopCount()*30); + if (this.localAddress != null) { + cab.append(this.localAddress); + cab.append("->"); + } + cab.append('{'); + if (this.tunnelled == TunnelType.TUNNELLED) { + cab.append('t'); + } + if (this.layered == LayerType.LAYERED) { + cab.append('l'); + } + if (this.secure) { + cab.append('s'); + } + cab.append("}->"); + if (this.proxyChain != null) { + for (final HttpHost aProxyChain : this.proxyChain) { + cab.append(aProxyChain); + cab.append("->"); + } + } + cab.append(this.targetHost); + return cab.toString(); + } + + // default implementation of clone() is sufficient + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRouteDirector.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRouteDirector.java new file mode 100644 index 000000000..f8b774603 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRouteDirector.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.routing; + +/** + * Provides directions on establishing a route. + * Implementations of this interface compare a planned route with + * a tracked route and indicate the next step required. + * + * @since 4.0 + */ +public interface HttpRouteDirector { + + /** Indicates that the route can not be established at all. */ + public final static int UNREACHABLE = -1; + + /** Indicates that the route is complete. */ + public final static int COMPLETE = 0; + + /** Step: open connection to target. */ + public final static int CONNECT_TARGET = 1; + + /** Step: open connection to proxy. */ + public final static int CONNECT_PROXY = 2; + + /** Step: tunnel through proxy to target. */ + public final static int TUNNEL_TARGET = 3; + + /** Step: tunnel through proxy to other proxy. */ + public final static int TUNNEL_PROXY = 4; + + /** Step: layer protocol (over tunnel). */ + public final static int LAYER_PROTOCOL = 5; + + + /** + * Provides the next step. + * + * @param plan the planned route + * @param fact the currently established route, or + * null if nothing is established + * + * @return one of the constants defined in this interface, indicating + * either the next step to perform, or success, or failure. + * 0 is for success, a negative value for failure. + */ + public int nextStep(RouteInfo plan, RouteInfo fact); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoutePlanner.java new file mode 100644 index 000000000..9e8ba19d3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/HttpRoutePlanner.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.routing; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Encapsulates logic to compute a {@link HttpRoute} to a target host. + * Implementations may for example be based on parameters, or on the + * standard Java system properties. + *

    + * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.0 + */ +public interface HttpRoutePlanner { + + /** + * Determines the route for a request. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param context the context to use for the subsequent execution. + * Implementations may accept null. + * + * @return the route that the request should take + * + * @throws HttpException in case of a problem + */ + public HttpRoute determineRoute(HttpHost target, + HttpRequest request, + HttpContext context) throws HttpException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteInfo.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteInfo.java new file mode 100644 index 000000000..0784b7894 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteInfo.java @@ -0,0 +1,161 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.routing; + +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpHost; + +/** + * Read-only interface for route information. + * + * @since 4.0 + */ +public interface RouteInfo { + + /** + * The tunnelling type of a route. + * Plain routes are established by connecting to the target or + * the first proxy. + * Tunnelled routes are established by connecting to the first proxy + * and tunnelling through all proxies to the target. + * Routes without a proxy cannot be tunnelled. + */ + public enum TunnelType { PLAIN, TUNNELLED } + + /** + * The layering type of a route. + * Plain routes are established by connecting or tunnelling. + * Layered routes are established by layering a protocol such as TLS/SSL + * over an existing connection. + * Protocols can only be layered over a tunnel to the target, or + * or over a direct connection without proxies. + *
    + * Layering a protocol + * over a direct connection makes little sense, since the connection + * could be established with the new protocol in the first place. + * But we don't want to exclude that use case. + */ + public enum LayerType { PLAIN, LAYERED } + + /** + * Obtains the target host. + * + * @return the target host + */ + HttpHost getTargetHost(); + + /** + * Obtains the local address to connect from. + * + * @return the local address, + * or null + */ + InetAddress getLocalAddress(); + + /** + * Obtains the number of hops in this route. + * A direct route has one hop. A route through a proxy has two hops. + * A route through a chain of n proxies has n+1 hops. + * + * @return the number of hops in this route + */ + int getHopCount(); + + /** + * Obtains the target of a hop in this route. + * The target of the last hop is the {@link #getTargetHost target host}, + * the target of previous hops is the respective proxy in the chain. + * For a route through exactly one proxy, target of hop 0 is the proxy + * and target of hop 1 is the target host. + * + * @param hop index of the hop for which to get the target, + * 0 for first + * + * @return the target of the given hop + * + * @throws IllegalArgumentException + * if the argument is negative or not less than + * {@link #getHopCount getHopCount()} + */ + HttpHost getHopTarget(int hop); + + /** + * Obtains the first proxy host. + * + * @return the first proxy in the proxy chain, or + * null if this route is direct + */ + HttpHost getProxyHost(); + + /** + * Obtains the tunnel type of this route. + * If there is a proxy chain, only end-to-end tunnels are considered. + * + * @return the tunnelling type + */ + TunnelType getTunnelType(); + + /** + * Checks whether this route is tunnelled through a proxy. + * If there is a proxy chain, only end-to-end tunnels are considered. + * + * @return true if tunnelled end-to-end through at least + * one proxy, + * false otherwise + */ + boolean isTunnelled(); + + /** + * Obtains the layering type of this route. + * In the presence of proxies, only layering over an end-to-end tunnel + * is considered. + * + * @return the layering type + */ + LayerType getLayerType(); + + /** + * Checks whether this route includes a layered protocol. + * In the presence of proxies, only layering over an end-to-end tunnel + * is considered. + * + * @return true if layered, + * false otherwise + */ + boolean isLayered(); + + /** + * Checks whether this route is secure. + * + * @return true if secure, + * false otherwise + */ + boolean isSecure(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteTracker.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteTracker.java new file mode 100644 index 000000000..9a900319c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteTracker.java @@ -0,0 +1,366 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.routing; + +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Helps tracking the steps in establishing a route. + * + * @since 4.0 + */ +@NotThreadSafe +public final class RouteTracker implements RouteInfo, Cloneable { + + /** The target host to connect to. */ + private final HttpHost targetHost; + + /** + * The local address to connect from. + * null indicates that the default should be used. + */ + private final InetAddress localAddress; + + // the attributes above are fixed at construction time + // now follow attributes that indicate the established route + + /** Whether the first hop of the route is established. */ + private boolean connected; + + /** The proxy chain, if any. */ + private HttpHost[] proxyChain; + + /** Whether the the route is tunnelled end-to-end through proxies. */ + private TunnelType tunnelled; + + /** Whether the route is layered over a tunnel. */ + private LayerType layered; + + /** Whether the route is secure. */ + private boolean secure; + + /** + * Creates a new route tracker. + * The target and origin need to be specified at creation time. + * + * @param target the host to which to route + * @param local the local address to route from, or + * null for the default + */ + public RouteTracker(final HttpHost target, final InetAddress local) { + Args.notNull(target, "Target host"); + this.targetHost = target; + this.localAddress = local; + this.tunnelled = TunnelType.PLAIN; + this.layered = LayerType.PLAIN; + } + + /** + * @since 4.2 + */ + public void reset() { + this.connected = false; + this.proxyChain = null; + this.tunnelled = TunnelType.PLAIN; + this.layered = LayerType.PLAIN; + this.secure = false; + } + + /** + * Creates a new tracker for the given route. + * Only target and origin are taken from the route, + * everything else remains to be tracked. + * + * @param route the route to track + */ + public RouteTracker(final HttpRoute route) { + this(route.getTargetHost(), route.getLocalAddress()); + } + + /** + * Tracks connecting to the target. + * + * @param secure true if the route is secure, + * false otherwise + */ + public final void connectTarget(final boolean secure) { + Asserts.check(!this.connected, "Already connected"); + this.connected = true; + this.secure = secure; + } + + /** + * Tracks connecting to the first proxy. + * + * @param proxy the proxy connected to + * @param secure true if the route is secure, + * false otherwise + */ + public final void connectProxy(final HttpHost proxy, final boolean secure) { + Args.notNull(proxy, "Proxy host"); + Asserts.check(!this.connected, "Already connected"); + this.connected = true; + this.proxyChain = new HttpHost[]{ proxy }; + this.secure = secure; + } + + /** + * Tracks tunnelling to the target. + * + * @param secure true if the route is secure, + * false otherwise + */ + public final void tunnelTarget(final boolean secure) { + Asserts.check(this.connected, "No tunnel unless connected"); + Asserts.notNull(this.proxyChain, "No tunnel without proxy"); + this.tunnelled = TunnelType.TUNNELLED; + this.secure = secure; + } + + /** + * Tracks tunnelling to a proxy in a proxy chain. + * This will extend the tracked proxy chain, but it does not mark + * the route as tunnelled. Only end-to-end tunnels are considered there. + * + * @param proxy the proxy tunnelled to + * @param secure true if the route is secure, + * false otherwise + */ + public final void tunnelProxy(final HttpHost proxy, final boolean secure) { + Args.notNull(proxy, "Proxy host"); + Asserts.check(this.connected, "No tunnel unless connected"); + Asserts.notNull(this.proxyChain, "No tunnel without proxy"); + // prepare an extended proxy chain + final HttpHost[] proxies = new HttpHost[this.proxyChain.length+1]; + System.arraycopy(this.proxyChain, 0, + proxies, 0, this.proxyChain.length); + proxies[proxies.length-1] = proxy; + + this.proxyChain = proxies; + this.secure = secure; + } + + /** + * Tracks layering a protocol. + * + * @param secure true if the route is secure, + * false otherwise + */ + public final void layerProtocol(final boolean secure) { + // it is possible to layer a protocol over a direct connection, + // although this case is probably not considered elsewhere + Asserts.check(this.connected, "No layered protocol unless connected"); + this.layered = LayerType.LAYERED; + this.secure = secure; + } + + public final HttpHost getTargetHost() { + return this.targetHost; + } + + public final InetAddress getLocalAddress() { + return this.localAddress; + } + + public final int getHopCount() { + int hops = 0; + if (this.connected) { + if (proxyChain == null) { + hops = 1; + } else { + hops = proxyChain.length + 1; + } + } + return hops; + } + + public final HttpHost getHopTarget(final int hop) { + Args.notNegative(hop, "Hop index"); + final int hopcount = getHopCount(); + Args.check(hop < hopcount, "Hop index exceeds tracked route length"); + HttpHost result = null; + if (hop < hopcount-1) { + result = this.proxyChain[hop]; + } else { + result = this.targetHost; + } + + return result; + } + + public final HttpHost getProxyHost() { + return (this.proxyChain == null) ? null : this.proxyChain[0]; + } + + public final boolean isConnected() { + return this.connected; + } + + public final TunnelType getTunnelType() { + return this.tunnelled; + } + + public final boolean isTunnelled() { + return (this.tunnelled == TunnelType.TUNNELLED); + } + + public final LayerType getLayerType() { + return this.layered; + } + + public final boolean isLayered() { + return (this.layered == LayerType.LAYERED); + } + + public final boolean isSecure() { + return this.secure; + } + + /** + * Obtains the tracked route. + * If a route has been tracked, it is {@link #isConnected connected}. + * If not connected, nothing has been tracked so far. + * + * @return the tracked route, or + * null if nothing has been tracked so far + */ + public final HttpRoute toRoute() { + return !this.connected ? + null : new HttpRoute(this.targetHost, this.localAddress, + this.proxyChain, this.secure, + this.tunnelled, this.layered); + } + + /** + * Compares this tracked route to another. + * + * @param o the object to compare with + * + * @return true if the argument is the same tracked route, + * false + */ + @Override + public final boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof RouteTracker)) { + return false; + } + + final RouteTracker that = (RouteTracker) o; + return + // Do the cheapest checks first + (this.connected == that.connected) && + (this.secure == that.secure) && + (this.tunnelled == that.tunnelled) && + (this.layered == that.layered) && + LangUtils.equals(this.targetHost, that.targetHost) && + LangUtils.equals(this.localAddress, that.localAddress) && + LangUtils.equals(this.proxyChain, that.proxyChain); + } + + /** + * Generates a hash code for this tracked route. + * Route trackers are modifiable and should therefore not be used + * as lookup keys. Use {@link #toRoute toRoute} to obtain an + * unmodifiable representation of the tracked route. + * + * @return the hash code + */ + @Override + public final int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.targetHost); + hash = LangUtils.hashCode(hash, this.localAddress); + if (this.proxyChain != null) { + for (final HttpHost element : this.proxyChain) { + hash = LangUtils.hashCode(hash, element); + } + } + hash = LangUtils.hashCode(hash, this.connected); + hash = LangUtils.hashCode(hash, this.secure); + hash = LangUtils.hashCode(hash, this.tunnelled); + hash = LangUtils.hashCode(hash, this.layered); + return hash; + } + + /** + * Obtains a description of the tracked route. + * + * @return a human-readable representation of the tracked route + */ + @Override + public final String toString() { + final StringBuilder cab = new StringBuilder(50 + getHopCount()*30); + + cab.append("RouteTracker["); + if (this.localAddress != null) { + cab.append(this.localAddress); + cab.append("->"); + } + cab.append('{'); + if (this.connected) { + cab.append('c'); + } + if (this.tunnelled == TunnelType.TUNNELLED) { + cab.append('t'); + } + if (this.layered == LayerType.LAYERED) { + cab.append('l'); + } + if (this.secure) { + cab.append('s'); + } + cab.append("}->"); + if (this.proxyChain != null) { + for (final HttpHost element : this.proxyChain) { + cab.append(element); + cab.append("->"); + } + } + cab.append(this.targetHost); + cab.append(']'); + + return cab.toString(); + } + + + // default implementation of clone() is sufficient + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/package-info.java new file mode 100644 index 000000000..ddd6b8ae8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client connection routing APIs. + */ +package ch.boye.httpclientandroidlib.conn.routing; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/HostNameResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/HostNameResolver.java new file mode 100644 index 000000000..8dcca6902 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/HostNameResolver.java @@ -0,0 +1,52 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.InetAddress; + +/** + * Hostname to IP address resolver. + * + * @since 4.0 + * + * @deprecated (4.1) Do not use + */ +@Deprecated +public interface HostNameResolver { + + /** + * Resolves given hostname to its IP address + * + * @param hostname the hostname. + * @return IP address. + * @throws IOException + */ + InetAddress resolve (String hostname) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactory.java new file mode 100644 index 000000000..3833b2360 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactory.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * Extended {@link SchemeSocketFactory} interface for layered sockets such as SSL/TLS. + * + * @since 4.1 + * + * @deprecated (4.2) use {@link SchemeLayeredSocketFactory} + */ +@Deprecated +public interface LayeredSchemeSocketFactory extends SchemeSocketFactory { + + /** + * Returns a socket connected to the given host that is layered over an + * existing socket. Used primarily for creating secure sockets through + * proxies. + * + * @param socket the existing socket + * @param target the name of the target host. + * @param port the port to connect to on the target host + * @param autoClose a flag for closing the underling socket when the created + * socket is closed + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be + * determined + */ + Socket createLayeredSocket( + Socket socket, + String target, + int port, + boolean autoClose + ) throws IOException, UnknownHostException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactory.java new file mode 100644 index 000000000..318e35f9c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactory.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * A {@link SocketFactory SocketFactory} for layered sockets (SSL/TLS). + * See there for things to consider when implementing a socket factory. + * + * @since 4.0 + * + * @deprecated (4.1) use {@link SchemeSocketFactory} + */ +@Deprecated +public interface LayeredSocketFactory extends SocketFactory { + + /** + * Returns a socket connected to the given host that is layered over an + * existing socket. Used primarily for creating secure sockets through + * proxies. + * + * @param socket the existing socket + * @param host the host name/IP + * @param port the port on the host + * @param autoClose a flag for closing the underling socket when the created + * socket is closed + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be + * determined + */ + Socket createSocket( + Socket socket, + String host, + int port, + boolean autoClose + ) throws IOException, UnknownHostException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactoryAdaptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactoryAdaptor.java new file mode 100644 index 000000000..11ebd10cb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/LayeredSocketFactoryAdaptor.java @@ -0,0 +1,53 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * @deprecated (4.1) do not use + */ +@Deprecated +class LayeredSocketFactoryAdaptor extends SocketFactoryAdaptor implements LayeredSocketFactory { + + private final LayeredSchemeSocketFactory factory; + + LayeredSocketFactoryAdaptor(final LayeredSchemeSocketFactory factory) { + super(factory); + this.factory = factory; + } + + public Socket createSocket( + final Socket socket, + final String host, final int port, final boolean autoClose) throws IOException, UnknownHostException { + return this.factory.createLayeredSocket(socket, host, port, autoClose); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/PlainSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/PlainSocketFactory.java new file mode 100644 index 000000000..efa8f281b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/PlainSocketFactory.java @@ -0,0 +1,160 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * The default class for creating plain (unencrypted) sockets. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory} + */ +@Immutable +@Deprecated +public class PlainSocketFactory implements SocketFactory, SchemeSocketFactory { + + private final HostNameResolver nameResolver; + + /** + * Gets the default factory. + * + * @return the default factory + */ + public static PlainSocketFactory getSocketFactory() { + return new PlainSocketFactory(); + } + + /** + * @deprecated (4.1) use {@link ch.boye.httpclientandroidlib.conn.DnsResolver} + */ + @Deprecated + public PlainSocketFactory(final HostNameResolver nameResolver) { + super(); + this.nameResolver = nameResolver; + } + + public PlainSocketFactory() { + super(); + this.nameResolver = null; + } + + /** + * @param params Optional parameters. Parameters passed to this method will have no effect. + * This method will create a unconnected instance of {@link Socket} class + * using default constructor. + * + * @since 4.1 + */ + public Socket createSocket(final HttpParams params) { + return new Socket(); + } + + public Socket createSocket() { + return new Socket(); + } + + /** + * @since 4.1 + */ + public Socket connectSocket( + final Socket socket, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpParams params) throws IOException, ConnectTimeoutException { + Args.notNull(remoteAddress, "Remote address"); + Args.notNull(params, "HTTP parameters"); + Socket sock = socket; + if (sock == null) { + sock = createSocket(); + } + if (localAddress != null) { + sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params)); + sock.bind(localAddress); + } + final int connTimeout = HttpConnectionParams.getConnectionTimeout(params); + final int soTimeout = HttpConnectionParams.getSoTimeout(params); + + try { + sock.setSoTimeout(soTimeout); + sock.connect(remoteAddress, connTimeout); + } catch (final SocketTimeoutException ex) { + throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out"); + } + return sock; + } + + /** + * Checks whether a socket connection is secure. + * This factory creates plain socket connections + * which are not considered secure. + * + * @param sock the connected socket + * + * @return false + */ + public final boolean isSecure(final Socket sock) { + return false; + } + + /** + * @deprecated (4.1) Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)} + */ + @Deprecated + public Socket connectSocket( + final Socket socket, + final String host, final int port, + final InetAddress localAddress, final int localPort, + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { + InetSocketAddress local = null; + if (localAddress != null || localPort > 0) { + local = new InetSocketAddress(localAddress, localPort > 0 ? localPort : 0); + } + final InetAddress remoteAddress; + if (this.nameResolver != null) { + remoteAddress = this.nameResolver.resolve(host); + } else { + remoteAddress = InetAddress.getByName(host); + } + final InetSocketAddress remote = new InetSocketAddress(remoteAddress, port); + return connectSocket(socket, remote, local, params); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/Scheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/Scheme.java new file mode 100644 index 000000000..93a44de35 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/Scheme.java @@ -0,0 +1,260 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Encapsulates specifics of a protocol scheme such as "http" or "https". Schemes are identified + * by lowercase names. Supported schemes are typically collected in a {@link SchemeRegistry + * SchemeRegistry}. + *

    + * For example, to configure support for "https://" URLs, you could write code like the following: + *

    + * Scheme https = new Scheme("https", 443, new MySecureSocketFactory());
    + * SchemeRegistry registry = new SchemeRegistry();
    + * registry.register(https);
    + * 
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.conn.SchemePortResolver} for default port + * resolution and {@link ch.boye.httpclientandroidlib.config.Registry} for socket factory lookups. + */ +@Immutable +@Deprecated +public final class Scheme { + + /** The name of this scheme, in lowercase. (e.g. http, https) */ + private final String name; + + /** The socket factory for this scheme */ + private final SchemeSocketFactory socketFactory; + + /** The default port for this scheme */ + private final int defaultPort; + + /** Indicates whether this scheme allows for layered connections */ + private final boolean layered; + + /** A string representation, for {@link #toString toString}. */ + private String stringRep; + /* + * This is used to cache the result of the toString() method + * Since the method always generates the same value, there's no + * need to synchronize, and it does not affect immutability. + */ + + /** + * Creates a new scheme. + * Whether the created scheme allows for layered connections + * depends on the class of factory. + * + * @param name the scheme name, for example "http". + * The name will be converted to lowercase. + * @param port the default port for this scheme + * @param factory the factory for creating sockets for communication + * with this scheme + * + * @since 4.1 + */ + public Scheme(final String name, final int port, final SchemeSocketFactory factory) { + Args.notNull(name, "Scheme name"); + Args.check(port > 0 && port <= 0xffff, "Port is invalid"); + Args.notNull(factory, "Socket factory"); + this.name = name.toLowerCase(Locale.ENGLISH); + this.defaultPort = port; + if (factory instanceof SchemeLayeredSocketFactory) { + this.layered = true; + this.socketFactory = factory; + } else if (factory instanceof LayeredSchemeSocketFactory) { + this.layered = true; + this.socketFactory = new SchemeLayeredSocketFactoryAdaptor2((LayeredSchemeSocketFactory) factory); + } else { + this.layered = false; + this.socketFactory = factory; + } + } + + /** + * Creates a new scheme. + * Whether the created scheme allows for layered connections + * depends on the class of factory. + * + * @param name the scheme name, for example "http". + * The name will be converted to lowercase. + * @param factory the factory for creating sockets for communication + * with this scheme + * @param port the default port for this scheme + * + * @deprecated (4.1) Use {@link #Scheme(String, int, SchemeSocketFactory)} + */ + @Deprecated + public Scheme(final String name, + final SocketFactory factory, + final int port) { + + Args.notNull(name, "Scheme name"); + Args.notNull(factory, "Socket factory"); + Args.check(port > 0 && port <= 0xffff, "Port is invalid"); + + this.name = name.toLowerCase(Locale.ENGLISH); + if (factory instanceof LayeredSocketFactory) { + this.socketFactory = new SchemeLayeredSocketFactoryAdaptor( + (LayeredSocketFactory) factory); + this.layered = true; + } else { + this.socketFactory = new SchemeSocketFactoryAdaptor(factory); + this.layered = false; + } + this.defaultPort = port; + } + + /** + * Obtains the default port. + * + * @return the default port for this scheme + */ + public final int getDefaultPort() { + return defaultPort; + } + + + /** + * Obtains the socket factory. + * If this scheme is {@link #isLayered layered}, the factory implements + * {@link LayeredSocketFactory LayeredSocketFactory}. + * + * @return the socket factory for this scheme + * + * @deprecated (4.1) Use {@link #getSchemeSocketFactory()} + */ + @Deprecated + public final SocketFactory getSocketFactory() { + if (this.socketFactory instanceof SchemeSocketFactoryAdaptor) { + return ((SchemeSocketFactoryAdaptor) this.socketFactory).getFactory(); + } else { + if (this.layered) { + return new LayeredSocketFactoryAdaptor( + (LayeredSchemeSocketFactory) this.socketFactory); + } else { + return new SocketFactoryAdaptor(this.socketFactory); + } + } + } + + /** + * Obtains the socket factory. + * If this scheme is {@link #isLayered layered}, the factory implements + * {@link LayeredSocketFactory LayeredSchemeSocketFactory}. + * + * @return the socket factory for this scheme + * + * @since 4.1 + */ + public final SchemeSocketFactory getSchemeSocketFactory() { + return this.socketFactory; + } + + /** + * Obtains the scheme name. + * + * @return the name of this scheme, in lowercase + */ + public final String getName() { + return name; + } + + /** + * Indicates whether this scheme allows for layered connections. + * + * @return true if layered connections are possible, + * false otherwise + */ + public final boolean isLayered() { + return layered; + } + + /** + * Resolves the correct port for this scheme. + * Returns the given port if it is valid, the default port otherwise. + * + * @param port the port to be resolved, + * a negative number to obtain the default port + * + * @return the given port or the defaultPort + */ + public final int resolvePort(final int port) { + return port <= 0 ? defaultPort : port; + } + + /** + * Return a string representation of this object. + * + * @return a human-readable string description of this scheme + */ + @Override + public final String toString() { + if (stringRep == null) { + final StringBuilder buffer = new StringBuilder(); + buffer.append(this.name); + buffer.append(':'); + buffer.append(Integer.toString(this.defaultPort)); + stringRep = buffer.toString(); + } + return stringRep; + } + + @Override + public final boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Scheme) { + final Scheme that = (Scheme) obj; + return this.name.equals(that.name) + && this.defaultPort == that.defaultPort + && this.layered == that.layered; + } else { + return false; + } + } + + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.defaultPort); + hash = LangUtils.hashCode(hash, this.name); + hash = LangUtils.hashCode(hash, this.layered); + return hash; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactory.java new file mode 100644 index 000000000..b20ada328 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactory.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * Extended {@link SchemeSocketFactory} interface for layered sockets such as SSL/TLS. + * + * @since 4.2 + * + * @deprecated (4.3) use {@link + * ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory} + */ +@Deprecated +public interface SchemeLayeredSocketFactory extends SchemeSocketFactory { + + /** + * Returns a socket connected to the given host that is layered over an + * existing socket. Used primarily for creating secure sockets through + * proxies. + * + * @param socket the existing socket + * @param target the name of the target host. + * @param port the port to connect to on the target host + * @param params HTTP parameters + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be + * determined + */ + Socket createLayeredSocket( + Socket socket, + String target, + int port, + HttpParams params) throws IOException, UnknownHostException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor.java new file mode 100644 index 000000000..098f55d9a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * @deprecated (4.2) do not use + */ +@Deprecated +class SchemeLayeredSocketFactoryAdaptor extends SchemeSocketFactoryAdaptor + implements SchemeLayeredSocketFactory { + + private final LayeredSocketFactory factory; + + SchemeLayeredSocketFactoryAdaptor(final LayeredSocketFactory factory) { + super(factory); + this.factory = factory; + } + + public Socket createLayeredSocket( + final Socket socket, + final String target, final int port, + final HttpParams params) throws IOException, UnknownHostException { + return this.factory.createSocket(socket, target, port, true); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor2.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor2.java new file mode 100644 index 000000000..97f0442d7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeLayeredSocketFactoryAdaptor2.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * @deprecated (4.2) do not use + */ +@Deprecated +class SchemeLayeredSocketFactoryAdaptor2 implements SchemeLayeredSocketFactory { + + private final LayeredSchemeSocketFactory factory; + + SchemeLayeredSocketFactoryAdaptor2(final LayeredSchemeSocketFactory factory) { + super(); + this.factory = factory; + } + + public Socket createSocket(final HttpParams params) throws IOException { + return this.factory.createSocket(params); + } + + public Socket connectSocket( + final Socket sock, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { + return this.factory.connectSocket(sock, remoteAddress, localAddress, params); + } + + public boolean isSecure(final Socket sock) throws IllegalArgumentException { + return this.factory.isSecure(sock); + } + + public Socket createLayeredSocket( + final Socket socket, + final String target, final int port, + final HttpParams params) throws IOException, UnknownHostException { + return this.factory.createLayeredSocket(socket, target, port, true); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeRegistry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeRegistry.java new file mode 100644 index 000000000..ef87c809d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeRegistry.java @@ -0,0 +1,168 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A set of supported protocol {@link Scheme}s. + * Schemes are identified by lowercase names. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.config.Registry} + */ +@ThreadSafe +@Deprecated +public final class SchemeRegistry { + + /** The available schemes in this registry. */ + private final ConcurrentHashMap registeredSchemes; + + /** + * Creates a new, empty scheme registry. + */ + public SchemeRegistry() { + super(); + registeredSchemes = new ConcurrentHashMap(); + } + + /** + * Obtains a scheme by name. + * + * @param name the name of the scheme to look up (in lowercase) + * + * @return the scheme, never null + * + * @throws IllegalStateException + * if the scheme with the given name is not registered + */ + public final Scheme getScheme(final String name) { + final Scheme found = get(name); + if (found == null) { + throw new IllegalStateException + ("Scheme '"+name+"' not registered."); + } + return found; + } + + /** + * Obtains the scheme for a host. + * Convenience method for getScheme(host.getSchemeName()) + * + * @param host the host for which to obtain the scheme + * + * @return the scheme for the given host, never null + * + * @throws IllegalStateException + * if a scheme with the respective name is not registered + */ + public final Scheme getScheme(final HttpHost host) { + Args.notNull(host, "Host"); + return getScheme(host.getSchemeName()); + } + + /** + * Obtains a scheme by name, if registered. + * + * @param name the name of the scheme to look up (in lowercase) + * + * @return the scheme, or + * null if there is none by this name + */ + public final Scheme get(final String name) { + Args.notNull(name, "Scheme name"); + // leave it to the caller to use the correct name - all lowercase + //name = name.toLowerCase(Locale.ENGLISH); + final Scheme found = registeredSchemes.get(name); + return found; + } + + /** + * Registers a scheme. + * The scheme can later be retrieved by its name + * using {@link #getScheme(String) getScheme} or {@link #get get}. + * + * @param sch the scheme to register + * + * @return the scheme previously registered with that name, or + * null if none was registered + */ + public final Scheme register(final Scheme sch) { + Args.notNull(sch, "Scheme"); + final Scheme old = registeredSchemes.put(sch.getName(), sch); + return old; + } + + /** + * Unregisters a scheme. + * + * @param name the name of the scheme to unregister (in lowercase) + * + * @return the unregistered scheme, or + * null if there was none + */ + public final Scheme unregister(final String name) { + Args.notNull(name, "Scheme name"); + // leave it to the caller to use the correct name - all lowercase + //name = name.toLowerCase(Locale.ENGLISH); + final Scheme gone = registeredSchemes.remove(name); + return gone; + } + + /** + * Obtains the names of the registered schemes. + * + * @return List containing registered scheme names. + */ + public final List getSchemeNames() { + return new ArrayList(registeredSchemes.keySet()); + } + + /** + * Populates the internal collection of registered {@link Scheme protocol schemes} + * with the content of the map passed as a parameter. + * + * @param map protocol schemes + */ + public void setItems(final Map map) { + if (map == null) { + return; + } + registeredSchemes.clear(); + registeredSchemes.putAll(map); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactory.java new file mode 100644 index 000000000..81cac0ed1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactory.java @@ -0,0 +1,130 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * A factory for creating, initializing and connecting sockets. The factory encapsulates the logic + * for establishing a socket connection. + * + * @since 4.1 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory} + */ +@Deprecated +public interface SchemeSocketFactory { + + /** + * Creates a new, unconnected socket. The socket should subsequently be passed to + * {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)}. + * + * @param params Optional {@link HttpParams parameters}. In most cases these parameters + * will not be required and will have no effect, as usually socket + * initialization should take place in the + * {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)} + * method. However, in rare cases one may want to pass additional parameters + * to this method in order to create a customized {@link Socket} instance, + * for instance bound to a SOCKS proxy server. + * + * @return a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + */ + Socket createSocket(HttpParams params) throws IOException; + + /** + * Connects a socket to the target host with the given remote address. + *

    + * Please note that {@link ch.boye.httpclientandroidlib.conn.HttpInetSocketAddress} class should + * be used in order to pass the target remote address along with the original + * {@link ch.boye.httpclientandroidlib.HttpHost} value used to resolve the address. The use of + * {@link ch.boye.httpclientandroidlib.conn.HttpInetSocketAddress} can also ensure that no reverse + * DNS lookup will be performed if the target remote address was specified + * as an IP address. + * + * @param sock the socket to connect, as obtained from + * {@link #createSocket(HttpParams) createSocket}. + * null indicates that a new socket + * should be created and connected. + * @param remoteAddress the remote address to connect to. + * @param localAddress the local address to bind the socket to, or + * null for any + * @param params additional {@link HttpParams parameters} for connecting + * + * @return the connected socket. The returned object may be different + * from the sock argument if this factory supports + * a layered protocol. + * + * @throws IOException if an I/O error occurs + * @throws UnknownHostException if the IP address of the target host + * can not be determined + * @throws ConnectTimeoutException if the socket cannot be connected + * within the time limit defined in the params + * + * @see ch.boye.httpclientandroidlib.conn.HttpInetSocketAddress + */ + Socket connectSocket( + Socket sock, + InetSocketAddress remoteAddress, + InetSocketAddress localAddress, + HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException; + + /** + * Checks whether a socket provides a secure connection. The socket must be + * {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams) connected} + * by this factory. The factory will not perform I/O operations in this method. + *

    + * As a rule of thumb, plain sockets are not secure and TLS/SSL sockets are secure. However, + * there may be application specific deviations. For example, a plain socket to a host in the + * same intranet ("trusted zone") could be considered secure. On the other hand, a TLS/SSL + * socket could be considered insecure based on the cipher suite chosen for the connection. + * + * @param sock the connected socket to check + * + * @return true if the connection of the socket + * should be considered secure, or + * false if it should not + * + * @throws IllegalArgumentException + * if the argument is invalid, for example because it is + * not a connected socket or was created by a different + * socket factory. + * Note that socket factories are not required to + * check these conditions, they may simply return a default + * value when called with an invalid socket argument. + */ + boolean isSecure(Socket sock) throws IllegalArgumentException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactoryAdaptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactoryAdaptor.java new file mode 100644 index 000000000..85ff5e6b2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SchemeSocketFactoryAdaptor.java @@ -0,0 +1,100 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * @deprecated (4.1) do not use + */ +@Deprecated +class SchemeSocketFactoryAdaptor implements SchemeSocketFactory { + + private final SocketFactory factory; + + SchemeSocketFactoryAdaptor(final SocketFactory factory) { + super(); + this.factory = factory; + } + + public Socket connectSocket( + final Socket sock, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { + final String host = remoteAddress.getHostName(); + final int port = remoteAddress.getPort(); + InetAddress local = null; + int localPort = 0; + if (localAddress != null) { + local = localAddress.getAddress(); + localPort = localAddress.getPort(); + } + return this.factory.connectSocket(sock, host, port, local, localPort, params); + } + + public Socket createSocket(final HttpParams params) throws IOException { + return this.factory.createSocket(); + } + + public boolean isSecure(final Socket sock) throws IllegalArgumentException { + return this.factory.isSecure(sock); + } + + public SocketFactory getFactory() { + return this.factory; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof SchemeSocketFactoryAdaptor) { + return this.factory.equals(((SchemeSocketFactoryAdaptor)obj).factory); + } else { + return this.factory.equals(obj); + } + } + + @Override + public int hashCode() { + return this.factory.hashCode(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactory.java new file mode 100644 index 000000000..71960194b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactory.java @@ -0,0 +1,127 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * A factory for creating, initializing and connecting sockets. + * The factory encapsulates the logic for establishing a socket connection. + * + * @since 4.0 + * + * @deprecated (4.1) use {@link SchemeSocketFactory} + */ +@Deprecated +public interface SocketFactory { + + /** + * Creates a new, unconnected socket. + * The socket should subsequently be passed to + * {@link #connectSocket connectSocket}. + * + * @return a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + */ + Socket createSocket() + throws IOException; + + /** + * Connects a socket to the given host. + * + * @param sock the socket to connect, as obtained from + * {@link #createSocket createSocket}. + * null indicates that a new socket + * should be created and connected. + * @param host the host to connect to + * @param port the port to connect to on the host + * @param localAddress the local address to bind the socket to, or + * null for any + * @param localPort the port on the local machine, + * 0 or a negative number for any + * @param params additional {@link HttpParams parameters} for connecting + * + * @return the connected socket. The returned object may be different + * from the sock argument if this factory supports + * a layered protocol. + * + * @throws IOException if an I/O error occurs + * @throws UnknownHostException if the IP address of the target host + * can not be determined + * @throws ConnectTimeoutException if the socket cannot be connected + * within the time limit defined in the params + */ + Socket connectSocket( + Socket sock, + String host, + int port, + InetAddress localAddress, + int localPort, + HttpParams params + ) throws IOException, UnknownHostException, ConnectTimeoutException; + + /** + * Checks whether a socket provides a secure connection. + * The socket must be {@link #connectSocket connected} + * by this factory. + * The factory will not perform I/O operations + * in this method. + *
    + * As a rule of thumb, plain sockets are not secure and + * TLS/SSL sockets are secure. However, there may be + * application specific deviations. For example, a plain + * socket to a host in the same intranet ("trusted zone") + * could be considered secure. On the other hand, a + * TLS/SSL socket could be considered insecure based on + * the cipher suite chosen for the connection. + * + * @param sock the connected socket to check + * + * @return true if the connection of the socket + * should be considered secure, or + * false if it should not + * + * @throws IllegalArgumentException + * if the argument is invalid, for example because it is + * not a connected socket or was created by a different + * socket factory. + * Note that socket factories are not required to + * check these conditions, they may simply return a default + * value when called with an invalid socket argument. + */ + boolean isSecure(Socket sock) + throws IllegalArgumentException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactoryAdaptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactoryAdaptor.java new file mode 100644 index 000000000..0e9aab355 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/SocketFactoryAdaptor.java @@ -0,0 +1,97 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.scheme; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; + +@Deprecated +class SocketFactoryAdaptor implements SocketFactory { + + private final SchemeSocketFactory factory; + + SocketFactoryAdaptor(final SchemeSocketFactory factory) { + super(); + this.factory = factory; + } + + public Socket createSocket() throws IOException { + final HttpParams params = new BasicHttpParams(); + return this.factory.createSocket(params); + } + + public Socket connectSocket( + final Socket socket, + final String host, final int port, + final InetAddress localAddress, final int localPort, + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { + InetSocketAddress local = null; + if (localAddress != null || localPort > 0) { + local = new InetSocketAddress(localAddress, localPort > 0 ? localPort : 0); + } + final InetAddress remoteAddress = InetAddress.getByName(host); + final InetSocketAddress remote = new InetSocketAddress(remoteAddress, port); + return this.factory.connectSocket(socket, remote, local, params); + } + + public boolean isSecure(final Socket socket) throws IllegalArgumentException { + return this.factory.isSecure(socket); + } + + public SchemeSocketFactory getFactory() { + return this.factory; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof SocketFactoryAdaptor) { + return this.factory.equals(((SocketFactoryAdaptor)obj).factory); + } else { + return this.factory.equals(obj); + } + } + + @Override + public int hashCode() { + return this.factory.hashCode(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/package-info.java new file mode 100644 index 000000000..2e6409084 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/scheme/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Deprecated. + * @deprecated (4.3). + */ +package ch.boye.httpclientandroidlib.conn.scheme; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/ConnectionSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/ConnectionSocketFactory.java new file mode 100644 index 000000000..98274f213 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/ConnectionSocketFactory.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.socket; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A factory for creating and connecting connection sockets. + * + * @since 4.3 + */ +public interface ConnectionSocketFactory { + + /** + * Creates new, unconnected socket. The socket should subsequently be passed to + * {@link #connectSocket(int, Socket, HttpHost, InetSocketAddress, InetSocketAddress, + * HttpContext) connectSocket} method. + * + * @return a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + */ + Socket createSocket(HttpContext context) throws IOException; + + /** + * Connects the socket to the target host with the given resolved remote address. + * + * @param connectTimeout connect timeout. + * @param sock the socket to connect, as obtained from {@link #createSocket(HttpContext)}. + * null indicates that a new socket should be created and connected. + * @param host target host as specified by the caller (end user). + * @param remoteAddress the resolved remote address to connect to. + * @param localAddress the local address to bind the socket to, or null for any. + * @param context the actual HTTP context. + * + * @return the connected socket. The returned object may be different + * from the sock argument if this factory supports + * a layered protocol. + * + * @throws IOException if an I/O error occurs + */ + Socket connectSocket( + int connectTimeout, + Socket sock, + HttpHost host, + InetSocketAddress remoteAddress, + InetSocketAddress localAddress, + HttpContext context) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/LayeredConnectionSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/LayeredConnectionSocketFactory.java new file mode 100644 index 000000000..40c54f8fa --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/LayeredConnectionSocketFactory.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.socket; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Extended {@link ConnectionSocketFactory} interface for layered sockets such as SSL/TLS. + * + * @since 4.3 + */ +public interface LayeredConnectionSocketFactory extends ConnectionSocketFactory { + + /** + * Returns a socket connected to the given host that is layered over an + * existing socket. Used primarily for creating secure sockets through + * proxies. + * + * @param socket the existing socket + * @param target the name of the target host. + * @param port the port to connect to on the target host. + * @param context the actual HTTP context. + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + */ + Socket createLayeredSocket( + Socket socket, + String target, + int port, + HttpContext context) throws IOException, UnknownHostException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/PlainConnectionSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/PlainConnectionSocketFactory.java new file mode 100644 index 000000000..d8fa807c5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/PlainConnectionSocketFactory.java @@ -0,0 +1,83 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.socket; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * The default class for creating plain (unencrypted) sockets. + * + * @since 4.3 + */ +@Immutable +public class PlainConnectionSocketFactory implements ConnectionSocketFactory { + + public static final PlainConnectionSocketFactory INSTANCE = new PlainConnectionSocketFactory(); + + public static PlainConnectionSocketFactory getSocketFactory() { + return INSTANCE; + } + + public PlainConnectionSocketFactory() { + super(); + } + + public Socket createSocket(final HttpContext context) throws IOException { + return new Socket(); + } + + public Socket connectSocket( + final int connectTimeout, + final Socket socket, + final HttpHost host, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpContext context) throws IOException { + final Socket sock = socket != null ? socket : createSocket(context); + if (localAddress != null) { + sock.bind(localAddress); + } + try { + sock.connect(remoteAddress, connectTimeout); + } catch (final IOException ex) { + try { + sock.close(); + } catch (final IOException ignore) { + } + throw ex; + } + return sock; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/package-info.java new file mode 100644 index 000000000..fb73d60de --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/socket/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client connection socket APIs. + */ +package ch.boye.httpclientandroidlib.conn.socket; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AbstractVerifier.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AbstractVerifier.java new file mode 100644 index 000000000..24a7b40a8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AbstractVerifier.java @@ -0,0 +1,386 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.cert.Certificate; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.NoSuchElementException; + +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.util.InetAddressUtils; +import ch.boye.httpclientandroidlib.util.TextUtils; + +import ch.boye.httpclientandroidlib.NameValuePair; + + +/** + * Abstract base class for all standard {@link X509HostnameVerifier} + * implementations. + * + * @since 4.0 + */ +@Immutable +public abstract class AbstractVerifier implements X509HostnameVerifier { + + /** + * This contains a list of 2nd-level domains that aren't allowed to + * have wildcards when combined with country-codes. + * For example: [*.co.uk]. + *

    + * The [*.co.uk] problem is an interesting one. Should we just hope + * that CA's would never foolishly allow such a certificate to happen? + * Looks like we're the only implementation guarding against this. + * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check. + */ + private final static String[] BAD_COUNTRY_2LDS = + { "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info", + "lg", "ne", "net", "or", "org" }; + + static { + // Just in case developer forgot to manually sort the array. :-) + Arrays.sort(BAD_COUNTRY_2LDS); + } + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public AbstractVerifier() { + super(); + } + + public final void verify(final String host, final SSLSocket ssl) + throws IOException { + if(host == null) { + throw new NullPointerException("host to verify is null"); + } + + SSLSession session = ssl.getSession(); + if(session == null) { + // In our experience this only happens under IBM 1.4.x when + // spurious (unrelated) certificates show up in the server' + // chain. Hopefully this will unearth the real problem: + final InputStream in = ssl.getInputStream(); + in.available(); + /* + If you're looking at the 2 lines of code above because + you're running into a problem, you probably have two + options: + + #1. Clean up the certificate chain that your server + is presenting (e.g. edit "/etc/apache2/server.crt" + or wherever it is your server's certificate chain + is defined). + + OR + + #2. Upgrade to an IBM 1.5.x or greater JVM, or switch + to a non-IBM JVM. + */ + + // If ssl.getInputStream().available() didn't cause an + // exception, maybe at least now the session is available? + session = ssl.getSession(); + if(session == null) { + // If it's still null, probably a startHandshake() will + // unearth the real problem. + ssl.startHandshake(); + + // Okay, if we still haven't managed to cause an exception, + // might as well go for the NPE. Or maybe we're okay now? + session = ssl.getSession(); + } + } + + final Certificate[] certs = session.getPeerCertificates(); + final X509Certificate x509 = (X509Certificate) certs[0]; + verify(host, x509); + } + + public final boolean verify(final String host, final SSLSession session) { + try { + final Certificate[] certs = session.getPeerCertificates(); + final X509Certificate x509 = (X509Certificate) certs[0]; + verify(host, x509); + return true; + } + catch(final SSLException e) { + return false; + } + } + + public final void verify(final String host, final X509Certificate cert) + throws SSLException { + final String[] cns = getCNs(cert); + final String[] subjectAlts = getSubjectAlts(cert, host); + verify(host, cns, subjectAlts); + } + + public final void verify(final String host, final String[] cns, + final String[] subjectAlts, + final boolean strictWithSubDomains) + throws SSLException { + + // Build the list of names we're going to check. Our DEFAULT and + // STRICT implementations of the HostnameVerifier only use the + // first CN provided. All other CNs are ignored. + // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way). + final LinkedList names = new LinkedList(); + if(cns != null && cns.length > 0 && cns[0] != null) { + names.add(cns[0]); + } + if(subjectAlts != null) { + for (final String subjectAlt : subjectAlts) { + if (subjectAlt != null) { + names.add(subjectAlt); + } + } + } + + if(names.isEmpty()) { + final String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt"; + throw new SSLException(msg); + } + + // StringBuilder for building the error message. + final StringBuilder buf = new StringBuilder(); + + // We're can be case-insensitive when comparing the host we used to + // establish the socket to the hostname in the certificate. + final String hostName = normaliseIPv6Address(host.trim().toLowerCase(Locale.ENGLISH)); + boolean match = false; + for(final Iterator it = names.iterator(); it.hasNext();) { + // Don't trim the CN, though! + String cn = it.next(); + cn = cn.toLowerCase(Locale.ENGLISH); + // Store CN in StringBuilder in case we need to report an error. + buf.append(" <"); + buf.append(cn); + buf.append('>'); + if(it.hasNext()) { + buf.append(" OR"); + } + + // The CN better have at least two dots if it wants wildcard + // action. It also can't be [*.co.uk] or [*.co.jp] or + // [*.org.uk], etc... + final String parts[] = cn.split("\\."); + final boolean doWildcard = + parts.length >= 3 && parts[0].endsWith("*") && + validCountryWildcard(cn) && !isIPAddress(host); + + if(doWildcard) { + final String firstpart = parts[0]; + if (firstpart.length() > 1) { // e.g. server* + final String prefix = firstpart.substring(0, firstpart.length() - 1); // e.g. server + final String suffix = cn.substring(firstpart.length()); // skip wildcard part from cn + final String hostSuffix = hostName.substring(prefix.length()); // skip wildcard part from host + match = hostName.startsWith(prefix) && hostSuffix.endsWith(suffix); + } else { + match = hostName.endsWith(cn.substring(1)); + } + if(match && strictWithSubDomains) { + // If we're in strict mode, then [*.foo.com] is not + // allowed to match [a.b.foo.com] + match = countDots(hostName) == countDots(cn); + } + } else { + match = hostName.equals(normaliseIPv6Address(cn)); + } + if(match) { + break; + } + } + if(!match) { + throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf); + } + } + + /** + * @deprecated (4.3.1) should not be a part of public APIs. + */ + @Deprecated + public static boolean acceptableCountryWildcard(final String cn) { + final String parts[] = cn.split("\\."); + if (parts.length != 3 || parts[2].length() != 2) { + return true; // it's not an attempt to wildcard a 2TLD within a country code + } + return Arrays.binarySearch(BAD_COUNTRY_2LDS, parts[1]) < 0; + } + + boolean validCountryWildcard(final String cn) { + final String parts[] = cn.split("\\."); + if (parts.length != 3 || parts[2].length() != 2) { + return true; // it's not an attempt to wildcard a 2TLD within a country code + } + return Arrays.binarySearch(BAD_COUNTRY_2LDS, parts[1]) < 0; + } + + public static String[] getCNs(final X509Certificate cert) { + final String subjectPrincipal = cert.getSubjectX500Principal().toString(); + try { + return extractCNs(subjectPrincipal); + } catch (SSLException ex) { + return null; + } + } + + static String[] extractCNs(final String subjectPrincipal) throws SSLException { + if (subjectPrincipal == null) { + return null; + } + final List cns = new ArrayList(); + final List nvps = DistinguishedNameParser.INSTANCE.parse(subjectPrincipal); + for (int i = 0; i < nvps.size(); i++) { + final NameValuePair nvp = nvps.get(i); + final String attribName = nvp.getName(); + final String attribValue = nvp.getValue(); + if (TextUtils.isBlank(attribValue)) { + throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name"); + } + if (attribName.equalsIgnoreCase("cn")) { + cns.add(attribValue); + } + } + return cns.isEmpty() ? null : cns.toArray(new String[ cns.size() ]); + } + + /** + * Extracts the array of SubjectAlt DNS or IP names from an X509Certificate. + * Returns null if there aren't any. + * + * @param cert X509Certificate + * @param hostname + * @return Array of SubjectALT DNS or IP names stored in the certificate. + */ + private static String[] getSubjectAlts( + final X509Certificate cert, final String hostname) { + final int subjectType; + if (isIPAddress(hostname)) { + subjectType = 7; + } else { + subjectType = 2; + } + + final LinkedList subjectAltList = new LinkedList(); + Collection> c = null; + try { + c = cert.getSubjectAlternativeNames(); + } + catch(final CertificateParsingException cpe) { + } + if(c != null) { + for (final List aC : c) { + final List list = aC; + final int type = ((Integer) list.get(0)).intValue(); + if (type == subjectType) { + final String s = (String) list.get(1); + subjectAltList.add(s); + } + } + } + if(!subjectAltList.isEmpty()) { + final String[] subjectAlts = new String[subjectAltList.size()]; + subjectAltList.toArray(subjectAlts); + return subjectAlts; + } else { + return null; + } + } + + /** + * Extracts the array of SubjectAlt DNS names from an X509Certificate. + * Returns null if there aren't any. + *

    + * Note: Java doesn't appear able to extract international characters + * from the SubjectAlts. It can only extract international characters + * from the CN field. + *

    + * (Or maybe the version of OpenSSL I'm using to test isn't storing the + * international characters correctly in the SubjectAlts?). + * + * @param cert X509Certificate + * @return Array of SubjectALT DNS names stored in the certificate. + */ + public static String[] getDNSSubjectAlts(final X509Certificate cert) { + return getSubjectAlts(cert, null); + } + + /** + * Counts the number of dots "." in a string. + * @param s string to count dots from + * @return number of dots + */ + public static int countDots(final String s) { + int count = 0; + for(int i = 0; i < s.length(); i++) { + if(s.charAt(i) == '.') { + count++; + } + } + return count; + } + + private static boolean isIPAddress(final String hostname) { + return hostname != null && + (InetAddressUtils.isIPv4Address(hostname) || + InetAddressUtils.isIPv6Address(hostname)); + } + + /* + * Check if hostname is IPv6, and if so, convert to standard format. + */ + private String normaliseIPv6Address(final String hostname) { + if (hostname == null || !InetAddressUtils.isIPv6Address(hostname)) { + return hostname; + } + try { + final InetAddress inetAddress = InetAddress.getByName(hostname); + return inetAddress.getHostAddress(); + } catch (final UnknownHostException uhe) { // Should not happen, because we check for IPv6 address above + log.error("Unexpected error converting "+hostname, uhe); + return hostname; + } + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AllowAllHostnameVerifier.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AllowAllHostnameVerifier.java new file mode 100644 index 000000000..8ca1fad5a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/AllowAllHostnameVerifier.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * The ALLOW_ALL HostnameVerifier essentially turns hostname verification + * off. This implementation is a no-op, and never throws the SSLException. + * + * + * @since 4.0 + */ +@Immutable +public class AllowAllHostnameVerifier extends AbstractVerifier { + + public final void verify( + final String host, + final String[] cns, + final String[] subjectAlts) { + // Allow everything - so never blowup. + } + + @Override + public final String toString() { + return "ALLOW_ALL"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/BrowserCompatHostnameVerifier.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/BrowserCompatHostnameVerifier.java new file mode 100644 index 000000000..05f7d8c8c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/BrowserCompatHostnameVerifier.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import javax.net.ssl.SSLException; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * The HostnameVerifier that works the same way as Curl and Firefox. + *

    + * The hostname must match either the first CN, or any of the subject-alts. + * A wildcard can occur in the CN, and in any of the subject-alts. + *

    + * The only difference between BROWSER_COMPATIBLE and STRICT is that a wildcard + * (such as "*.foo.com") with BROWSER_COMPATIBLE matches all subdomains, + * including "a.b.foo.com". + * + * + * @since 4.0 + */ +@Immutable +public class BrowserCompatHostnameVerifier extends AbstractVerifier { + + public final void verify( + final String host, + final String[] cns, + final String[] subjectAlts) throws SSLException { + verify(host, cns, subjectAlts, false); + } + + @Override + boolean validCountryWildcard(final String cn) { + return true; + } + + @Override + public final String toString() { + return "BROWSER_COMPATIBLE"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/DistinguishedNameParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/DistinguishedNameParser.java new file mode 100644 index 000000000..98b2ec3a9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/DistinguishedNameParser.java @@ -0,0 +1,131 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +@Immutable +final class DistinguishedNameParser { + + public final static DistinguishedNameParser INSTANCE = new DistinguishedNameParser(); + + private static final BitSet EQUAL_OR_COMMA_OR_PLUS = TokenParser.INIT_BITSET('=', ',', '+'); + private static final BitSet COMMA_OR_PLUS = TokenParser.INIT_BITSET(',', '+'); + + private final TokenParser tokenParser; + + DistinguishedNameParser() { + this.tokenParser = new InternalTokenParser(); + } + + String parseToken(final CharArrayBuffer buf, final ParserCursor cursor, final BitSet delimiters) { + return tokenParser.parseToken(buf, cursor, delimiters); + } + + String parseValue(final CharArrayBuffer buf, final ParserCursor cursor, final BitSet delimiters) { + return tokenParser.parseValue(buf, cursor, delimiters); + } + + NameValuePair parseParameter(final CharArrayBuffer buf, final ParserCursor cursor) { + final String name = parseToken(buf, cursor, EQUAL_OR_COMMA_OR_PLUS); + if (cursor.atEnd()) { + return new BasicNameValuePair(name, null); + } + final int delim = buf.charAt(cursor.getPos()); + cursor.updatePos(cursor.getPos() + 1); + if (delim == ',') { + return new BasicNameValuePair(name, null); + } + final String value = parseValue(buf, cursor, COMMA_OR_PLUS); + if (!cursor.atEnd()) { + cursor.updatePos(cursor.getPos() + 1); + } + return new BasicNameValuePair(name, value); + } + + public List parse(final CharArrayBuffer buf, final ParserCursor cursor) { + final List params = new ArrayList(); + tokenParser.skipWhiteSpace(buf, cursor); + while (!cursor.atEnd()) { + final NameValuePair param = parseParameter(buf, cursor); + params.add(param); + } + return params; + } + + public List parse(final String s) { + if (s == null) { + return null; + } + final CharArrayBuffer buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + final ParserCursor cursor = new ParserCursor(0, s.length()); + return parse(buffer, cursor); + } + + static class InternalTokenParser extends TokenParser { + + @Override + public void copyUnquotedContent( + final CharArrayBuffer buf, + final ParserCursor cursor, + final BitSet delimiters, + final StringBuilder dst) { + int pos = cursor.getPos(); + final int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + boolean escaped = false; + for (int i = indexFrom; i < indexTo; i++, pos++) { + final char current = buf.charAt(i); + if (escaped) { + dst.append(current); + escaped = false; + } else { + if ((delimiters != null && delimiters.get(current)) + || TokenParser.isWhitespace(current) || current == '\"') { + break; + } else if (current == '\\') { + escaped = true; + } else { + dst.append(current); + } + } + } + cursor.updatePos(pos); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyDetails.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyDetails.java new file mode 100644 index 000000000..c85978550 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyDetails.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.ssl; + +import ch.boye.httpclientandroidlib.util.Args; + +import java.security.cert.X509Certificate; +import java.util.Arrays; + +/** + * Private key details. + * + * @since 4.3 + */ +public final class PrivateKeyDetails { + + private final String type; + private final X509Certificate[] certChain; + + public PrivateKeyDetails(final String type, final X509Certificate[] certChain) { + super(); + this.type = Args.notNull(type, "Private key type"); + this.certChain = certChain; + } + + public String getType() { + return type; + } + + public X509Certificate[] getCertChain() { + return certChain; + } + + @Override + public String toString() { + return type + ':' + Arrays.toString(certChain); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyStrategy.java new file mode 100644 index 000000000..ba3ba40c5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/PrivateKeyStrategy.java @@ -0,0 +1,44 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.net.Socket; +import java.util.Map; + +/** + * A strategy allowing for a choice of an alias during SSL authentication. + * + * @since 4.3 + */ +public interface PrivateKeyStrategy { + + /** + * Determines what key material to use for SSL authentication. + */ + String chooseAlias(Map aliases, Socket socket); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLConnectionSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLConnectionSocketFactory.java new file mode 100644 index 000000000..341fbe385 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLConnectionSocketFactory.java @@ -0,0 +1,295 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.TextUtils; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +/** + * Layered socket factory for TLS/SSL connections. + *

    + * SSLSocketFactory can be used to validate the identity of the HTTPS server against a list of + * trusted certificates and to authenticate to the HTTPS server using a private key. + *

    + * SSLSocketFactory will enable server authentication when supplied with + * a {@link java.security.KeyStore trust-store} file containing one or several trusted certificates. The client + * secure socket will reject the connection during the SSL session handshake if the target HTTPS + * server attempts to authenticate itself with a non-trusted certificate. + *

    + * Use JDK keytool utility to import a trusted certificate and generate a trust-store file: + *

    + *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
    + *    
    + *

    + * In special cases the standard trust verification process can be bypassed by using a custom + * {@link ch.boye.httpclientandroidlib.conn.ssl.TrustStrategy}. This interface is primarily intended for allowing self-signed + * certificates to be accepted as trusted without having to add them to the trust-store file. + *

    + * SSLSocketFactory will enable client authentication when supplied with + * a {@link java.security.KeyStore key-store} file containing a private key/public certificate + * pair. The client secure socket will use the private key to authenticate + * itself to the target HTTPS server during the SSL session handshake if + * requested to do so by the server. + * The target HTTPS server will in its turn verify the certificate presented + * by the client in order to establish client's authenticity. + *

    + * Use the following sequence of actions to generate a key-store file + *

    + *
      + *
    • + *

      + * Use JDK keytool utility to generate a new key + *

      keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore
      + * For simplicity use the same password for the key as that of the key-store + *

      + *
    • + *
    • + *

      + * Issue a certificate signing request (CSR) + *

      keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore
      + *

      + *
    • + *
    • + *

      + * Send the certificate request to the trusted Certificate Authority for signature. + * One may choose to act as her own CA and sign the certificate request using a PKI + * tool, such as OpenSSL. + *

      + *
    • + *
    • + *

      + * Import the trusted CA root certificate + *

      keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore
      + *

      + *
    • + *
    • + *

      + * Import the PKCS#7 file containg the complete certificate chain + *

      keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore
      + *

      + *
    • + *
    • + *

      + * Verify the content the resultant keystore file + *

      keytool -list -v -keystore my.keystore
      + *

      + *
    • + *
    + * + * @since 4.0 + */ +@ThreadSafe +public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactory { + + public static final String TLS = "TLS"; + public static final String SSL = "SSL"; + public static final String SSLV2 = "SSLv2"; + + public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER + = new AllowAllHostnameVerifier(); + + public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER + = new BrowserCompatHostnameVerifier(); + + public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER + = new StrictHostnameVerifier(); + + /** + * Obtains default SSL socket factory with an SSL context based on the standard JSSE + * trust material (cacerts file in the security properties directory). + * System properties are not taken into consideration. + * + * @return default SSL socket factory + */ + public static SSLConnectionSocketFactory getSocketFactory() throws SSLInitializationException { + return new SSLConnectionSocketFactory( + SSLContexts.createDefault(), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + private static String[] split(final String s) { + if (TextUtils.isBlank(s)) { + return null; + } + return s.split(" *, *"); + } + + /** + * Obtains default SSL socket factory with an SSL context based on system properties + * as described in + * + * "JavaTM Secure Socket Extension (JSSE) Reference Guide for the JavaTM 2 Platform + * Standard Edition 5 + * + * @return default system SSL socket factory + */ + public static SSLConnectionSocketFactory getSystemSocketFactory() throws SSLInitializationException { + return new SSLConnectionSocketFactory( + (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(), + split(System.getProperty("https.protocols")), + split(System.getProperty("https.cipherSuites")), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + private final javax.net.ssl.SSLSocketFactory socketfactory; + private final X509HostnameVerifier hostnameVerifier; + private final String[] supportedProtocols; + private final String[] supportedCipherSuites; + + public SSLConnectionSocketFactory(final SSLContext sslContext) { + this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + public SSLConnectionSocketFactory( + final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) { + this(Args.notNull(sslContext, "SSL context").getSocketFactory(), + null, null, hostnameVerifier); + } + + public SSLConnectionSocketFactory( + final SSLContext sslContext, + final String[] supportedProtocols, + final String[] supportedCipherSuites, + final X509HostnameVerifier hostnameVerifier) { + this(Args.notNull(sslContext, "SSL context").getSocketFactory(), + supportedProtocols, supportedCipherSuites, hostnameVerifier); + } + + public SSLConnectionSocketFactory( + final javax.net.ssl.SSLSocketFactory socketfactory, + final X509HostnameVerifier hostnameVerifier) { + this(socketfactory, null, null, hostnameVerifier); + } + + public SSLConnectionSocketFactory( + final javax.net.ssl.SSLSocketFactory socketfactory, + final String[] supportedProtocols, + final String[] supportedCipherSuites, + final X509HostnameVerifier hostnameVerifier) { + this.socketfactory = Args.notNull(socketfactory, "SSL socket factory"); + this.supportedProtocols = supportedProtocols; + this.supportedCipherSuites = supportedCipherSuites; + this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; + } + + /** + * Performs any custom initialization for a newly created SSLSocket + * (before the SSL handshake happens). + * + * The default implementation is a no-op, but could be overridden to, e.g., + * call {@link javax.net.ssl.SSLSocket#setEnabledCipherSuites(String[])}. + */ + protected void prepareSocket(final SSLSocket socket) throws IOException { + } + + public Socket createSocket(final HttpContext context) throws IOException { + return SocketFactory.getDefault().createSocket(); + } + + public Socket connectSocket( + final int connectTimeout, + final Socket socket, + final HttpHost host, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpContext context) throws IOException { + Args.notNull(host, "HTTP host"); + Args.notNull(remoteAddress, "Remote address"); + final Socket sock = socket != null ? socket : createSocket(context); + if (localAddress != null) { + sock.bind(localAddress); + } + try { + sock.connect(remoteAddress, connectTimeout); + } catch (final IOException ex) { + try { + sock.close(); + } catch (final IOException ignore) { + } + throw ex; + } + // Setup SSL layering if necessary + if (sock instanceof SSLSocket) { + final SSLSocket sslsock = (SSLSocket) sock; + sslsock.startHandshake(); + verifyHostname(sslsock, host.getHostName()); + return sock; + } else { + return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), context); + } + } + + public Socket createLayeredSocket( + final Socket socket, + final String target, + final int port, + final HttpContext context) throws IOException { + final SSLSocket sslsock = (SSLSocket) this.socketfactory.createSocket( + socket, + target, + port, + true); + if (supportedProtocols != null) { + sslsock.setEnabledProtocols(supportedProtocols); + } + if (supportedCipherSuites != null) { + sslsock.setEnabledCipherSuites(supportedCipherSuites); + } + prepareSocket(sslsock); + sslsock.startHandshake(); + verifyHostname(sslsock, target); + return sslsock; + } + + X509HostnameVerifier getHostnameVerifier() { + return this.hostnameVerifier; + } + + private void verifyHostname(final SSLSocket sslsock, final String hostname) throws IOException { + try { + this.hostnameVerifier.verify(hostname, sslsock); + // verifyHostName() didn't blowup - good! + } catch (final IOException iox) { + // close the socket before re-throwing the exception + try { sslsock.close(); } catch (final Exception x) { /*ignore*/ } + throw iox; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContextBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContextBuilder.java new file mode 100644 index 000000000..89751a166 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContextBuilder.java @@ -0,0 +1,259 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * Builder for {@link SSLContext} instances. + * + * @since 4.3 + */ +@NotThreadSafe +public class SSLContextBuilder { + + static final String TLS = "TLS"; + static final String SSL = "SSL"; + + private String protocol; + private Set keymanagers; + private Set trustmanagers; + private SecureRandom secureRandom; + + public SSLContextBuilder() { + super(); + this.keymanagers = new HashSet(); + this.trustmanagers = new HashSet(); + } + + public SSLContextBuilder useTLS() { + this.protocol = TLS; + return this; + } + + public SSLContextBuilder useSSL() { + this.protocol = SSL; + return this; + } + + public SSLContextBuilder useProtocol(final String protocol) { + this.protocol = protocol; + return this; + } + + public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) { + this.secureRandom = secureRandom; + return this; + } + + public SSLContextBuilder loadTrustMaterial( + final KeyStore truststore, + final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { + final TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmfactory.init(truststore); + final TrustManager[] tms = tmfactory.getTrustManagers(); + if (tms != null) { + if (trustStrategy != null) { + for (int i = 0; i < tms.length; i++) { + final TrustManager tm = tms[i]; + if (tm instanceof X509TrustManager) { + tms[i] = new TrustManagerDelegate( + (X509TrustManager) tm, trustStrategy); + } + } + } + for (final TrustManager tm : tms) { + this.trustmanagers.add(tm); + } + } + return this; + } + + public SSLContextBuilder loadTrustMaterial( + final KeyStore truststore) throws NoSuchAlgorithmException, KeyStoreException { + return loadTrustMaterial(truststore, null); + } + + public SSLContextBuilder loadKeyMaterial( + final KeyStore keystore, + final char[] keyPassword) + throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { + loadKeyMaterial(keystore, keyPassword, null); + return this; + } + + public SSLContextBuilder loadKeyMaterial( + final KeyStore keystore, + final char[] keyPassword, + final PrivateKeyStrategy aliasStrategy) + throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { + final KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( + KeyManagerFactory.getDefaultAlgorithm()); + kmfactory.init(keystore, keyPassword); + final KeyManager[] kms = kmfactory.getKeyManagers(); + if (kms != null) { + if (aliasStrategy != null) { + for (int i = 0; i < kms.length; i++) { + final KeyManager km = kms[i]; + if (km instanceof X509KeyManager) { + kms[i] = new KeyManagerDelegate( + (X509KeyManager) km, aliasStrategy); + } + } + } + for (final KeyManager km : kms) { + keymanagers.add(km); + } + } + return this; + } + + public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException { + final SSLContext sslcontext = SSLContext.getInstance( + this.protocol != null ? this.protocol : TLS); + sslcontext.init( + !keymanagers.isEmpty() ? keymanagers.toArray(new KeyManager[keymanagers.size()]) : null, + !trustmanagers.isEmpty() ? trustmanagers.toArray(new TrustManager[trustmanagers.size()]) : null, + secureRandom); + return sslcontext; + } + + static class TrustManagerDelegate implements X509TrustManager { + + private final X509TrustManager trustManager; + private final TrustStrategy trustStrategy; + + TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) { + super(); + this.trustManager = trustManager; + this.trustStrategy = trustStrategy; + } + + public void checkClientTrusted( + final X509Certificate[] chain, final String authType) throws CertificateException { + this.trustManager.checkClientTrusted(chain, authType); + } + + public void checkServerTrusted( + final X509Certificate[] chain, final String authType) throws CertificateException { + if (!this.trustStrategy.isTrusted(chain, authType)) { + this.trustManager.checkServerTrusted(chain, authType); + } + } + + public X509Certificate[] getAcceptedIssuers() { + return this.trustManager.getAcceptedIssuers(); + } + + } + + static class KeyManagerDelegate implements X509KeyManager { + + private final X509KeyManager keyManager; + private final PrivateKeyStrategy aliasStrategy; + + KeyManagerDelegate(final X509KeyManager keyManager, final PrivateKeyStrategy aliasStrategy) { + super(); + this.keyManager = keyManager; + this.aliasStrategy = aliasStrategy; + } + + public String[] getClientAliases( + final String keyType, final Principal[] issuers) { + return this.keyManager.getClientAliases(keyType, issuers); + } + + public String chooseClientAlias( + final String[] keyTypes, final Principal[] issuers, final Socket socket) { + final Map validAliases = new HashMap(); + for (final String keyType: keyTypes) { + final String[] aliases = this.keyManager.getClientAliases(keyType, issuers); + if (aliases != null) { + for (final String alias: aliases) { + validAliases.put(alias, + new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); + } + } + } + return this.aliasStrategy.chooseAlias(validAliases, socket); + } + + public String[] getServerAliases( + final String keyType, final Principal[] issuers) { + return this.keyManager.getServerAliases(keyType, issuers); + } + + public String chooseServerAlias( + final String keyType, final Principal[] issuers, final Socket socket) { + final Map validAliases = new HashMap(); + final String[] aliases = this.keyManager.getServerAliases(keyType, issuers); + if (aliases != null) { + for (final String alias: aliases) { + validAliases.put(alias, + new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); + } + } + return this.aliasStrategy.chooseAlias(validAliases, socket); + } + + public X509Certificate[] getCertificateChain(final String alias) { + return this.keyManager.getCertificateChain(alias); + } + + public PrivateKey getPrivateKey(final String alias) { + return this.keyManager.getPrivateKey(alias); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContexts.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContexts.java new file mode 100644 index 000000000..a87a50168 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLContexts.java @@ -0,0 +1,90 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * {@link SSLContext} factory methods. + * + * @since 4.3 + */ +@Immutable +public class SSLContexts { + + /** + * Creates default factory based on the standard JSSE trust material + * (cacerts file in the security properties directory). System properties + * are not taken into consideration. + * + * @return the default SSL socket factory + */ + public static SSLContext createDefault() throws SSLInitializationException { + try { + final SSLContext sslcontext = SSLContext.getInstance(SSLContextBuilder.TLS); + sslcontext.init(null, null, null); + return sslcontext; + } catch (final NoSuchAlgorithmException ex) { + throw new SSLInitializationException(ex.getMessage(), ex); + } catch (final KeyManagementException ex) { + throw new SSLInitializationException(ex.getMessage(), ex); + } + } + + /** + * Creates default SSL context based on system properties. This method obtains + * default SSL context by calling SSLContext.getInstance("Default"). + * Please note that Default algorithm is supported as of Java 6. + * This method will fall back onto {@link #createDefault()} when + * Default algorithm is not available. + * + * @return default system SSL context + */ + public static SSLContext createSystemDefault() throws SSLInitializationException { + try { + return SSLContext.getInstance("Default"); + } catch (final NoSuchAlgorithmException ex) { + return createDefault(); + } + } + + /** + * Creates custom SSL context. + * + * @return default system SSL context + */ + public static SSLContextBuilder custom() { + return new SSLContextBuilder(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLInitializationException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLInitializationException.java new file mode 100644 index 000000000..98cb5f9c3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLInitializationException.java @@ -0,0 +1,37 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.ssl; + +public class SSLInitializationException extends IllegalStateException { + + private static final long serialVersionUID = -8243587425648536702L; + + public SSLInitializationException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLSocketFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLSocketFactory.java new file mode 100644 index 000000000..5c514d780 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/SSLSocketFactory.java @@ -0,0 +1,570 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.conn.HttpInetSocketAddress; +import ch.boye.httpclientandroidlib.conn.scheme.HostNameResolver; +import ch.boye.httpclientandroidlib.conn.scheme.LayeredSchemeSocketFactory; +import ch.boye.httpclientandroidlib.conn.scheme.LayeredSocketFactory; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeLayeredSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.TextUtils; + +/** + * Layered socket factory for TLS/SSL connections. + *

    + * SSLSocketFactory can be used to validate the identity of the HTTPS server against a list of + * trusted certificates and to authenticate to the HTTPS server using a private key. + *

    + * SSLSocketFactory will enable server authentication when supplied with + * a {@link KeyStore trust-store} file containing one or several trusted certificates. The client + * secure socket will reject the connection during the SSL session handshake if the target HTTPS + * server attempts to authenticate itself with a non-trusted certificate. + *

    + * Use JDK keytool utility to import a trusted certificate and generate a trust-store file: + *

    + *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
    + *    
    + *

    + * In special cases the standard trust verification process can be bypassed by using a custom + * {@link TrustStrategy}. This interface is primarily intended for allowing self-signed + * certificates to be accepted as trusted without having to add them to the trust-store file. + *

    + * SSLSocketFactory will enable client authentication when supplied with + * a {@link KeyStore key-store} file containing a private key/public certificate + * pair. The client secure socket will use the private key to authenticate + * itself to the target HTTPS server during the SSL session handshake if + * requested to do so by the server. + * The target HTTPS server will in its turn verify the certificate presented + * by the client in order to establish client's authenticity. + *

    + * Use the following sequence of actions to generate a key-store file + *

    + *
      + *
    • + *

      + * Use JDK keytool utility to generate a new key + *

      keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore
      + * For simplicity use the same password for the key as that of the key-store + *

      + *
    • + *
    • + *

      + * Issue a certificate signing request (CSR) + *

      keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore
      + *

      + *
    • + *
    • + *

      + * Send the certificate request to the trusted Certificate Authority for signature. + * One may choose to act as her own CA and sign the certificate request using a PKI + * tool, such as OpenSSL. + *

      + *
    • + *
    • + *

      + * Import the trusted CA root certificate + *

      keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore
      + *

      + *
    • + *
    • + *

      + * Import the PKCS#7 file containg the complete certificate chain + *

      keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore
      + *

      + *
    • + *
    • + *

      + * Verify the content the resultant keystore file + *

      keytool -list -v -keystore my.keystore
      + *

      + *
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link SSLConnectionSocketFactory}. + */ +@ThreadSafe +@Deprecated +public class SSLSocketFactory implements LayeredConnectionSocketFactory, SchemeLayeredSocketFactory, + LayeredSchemeSocketFactory, LayeredSocketFactory { + + public static final String TLS = "TLS"; + public static final String SSL = "SSL"; + public static final String SSLV2 = "SSLv2"; + + public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER + = new AllowAllHostnameVerifier(); + + public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER + = new BrowserCompatHostnameVerifier(); + + public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER + = new StrictHostnameVerifier(); + + /** + * Obtains default SSL socket factory with an SSL context based on the standard JSSE + * trust material (cacerts file in the security properties directory). + * System properties are not taken into consideration. + * + * @return default SSL socket factory + */ + public static SSLSocketFactory getSocketFactory() throws SSLInitializationException { + return new SSLSocketFactory( + SSLContexts.createDefault(), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + private static String[] split(final String s) { + if (TextUtils.isBlank(s)) { + return null; + } + return s.split(" *, *"); + } + + /** + * Obtains default SSL socket factory with an SSL context based on system properties + * as described in + * + * "JavaTM Secure Socket Extension (JSSE) Reference Guide for the JavaTM 2 Platform + * Standard Edition 5 + * + * @return default system SSL socket factory + */ + public static SSLSocketFactory getSystemSocketFactory() throws SSLInitializationException { + return new SSLSocketFactory( + (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(), + split(System.getProperty("https.protocols")), + split(System.getProperty("https.cipherSuites")), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + private final javax.net.ssl.SSLSocketFactory socketfactory; + private final HostNameResolver nameResolver; + // TODO: make final + private volatile X509HostnameVerifier hostnameVerifier; + private final String[] supportedProtocols; + private final String[] supportedCipherSuites; + + public SSLSocketFactory( + final String algorithm, + final KeyStore keystore, + final String keyPassword, + final KeyStore truststore, + final SecureRandom random, + final HostNameResolver nameResolver) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + this(SSLContexts.custom() + .useProtocol(algorithm) + .setSecureRandom(random) + .loadKeyMaterial(keystore, keyPassword != null ? keyPassword.toCharArray() : null) + .loadTrustMaterial(truststore) + .build(), + nameResolver); + } + + /** + * @since 4.1 + */ + public SSLSocketFactory( + final String algorithm, + final KeyStore keystore, + final String keyPassword, + final KeyStore truststore, + final SecureRandom random, + final TrustStrategy trustStrategy, + final X509HostnameVerifier hostnameVerifier) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + this(SSLContexts.custom() + .useProtocol(algorithm) + .setSecureRandom(random) + .loadKeyMaterial(keystore, keyPassword != null ? keyPassword.toCharArray() : null) + .loadTrustMaterial(truststore, trustStrategy) + .build(), + hostnameVerifier); + } + + /** + * @since 4.1 + */ + public SSLSocketFactory( + final String algorithm, + final KeyStore keystore, + final String keyPassword, + final KeyStore truststore, + final SecureRandom random, + final X509HostnameVerifier hostnameVerifier) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + this(SSLContexts.custom() + .useProtocol(algorithm) + .setSecureRandom(random) + .loadKeyMaterial(keystore, keyPassword != null ? keyPassword.toCharArray() : null) + .loadTrustMaterial(truststore) + .build(), + hostnameVerifier); + } + + public SSLSocketFactory( + final KeyStore keystore, + final String keystorePassword, + final KeyStore truststore) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + this(SSLContexts.custom() + .loadKeyMaterial(keystore, keystorePassword != null ? keystorePassword.toCharArray() : null) + .loadTrustMaterial(truststore) + .build(), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + public SSLSocketFactory( + final KeyStore keystore, + final String keystorePassword) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{ + this(SSLContexts.custom() + .loadKeyMaterial(keystore, keystorePassword != null ? keystorePassword.toCharArray() : null) + .build(), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + public SSLSocketFactory( + final KeyStore truststore) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + this(SSLContexts.custom() + .loadTrustMaterial(truststore) + .build(), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + /** + * @since 4.1 + */ + public SSLSocketFactory( + final TrustStrategy trustStrategy, + final X509HostnameVerifier hostnameVerifier) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + this(SSLContexts.custom() + .loadTrustMaterial(null, trustStrategy) + .build(), + hostnameVerifier); + } + + /** + * @since 4.1 + */ + public SSLSocketFactory( + final TrustStrategy trustStrategy) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + this(SSLContexts.custom() + .loadTrustMaterial(null, trustStrategy) + .build(), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + public SSLSocketFactory(final SSLContext sslContext) { + this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + public SSLSocketFactory( + final SSLContext sslContext, final HostNameResolver nameResolver) { + super(); + this.socketfactory = sslContext.getSocketFactory(); + this.hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; + this.nameResolver = nameResolver; + this.supportedProtocols = null; + this.supportedCipherSuites = null; + } + + /** + * @since 4.1 + */ + public SSLSocketFactory( + final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) { + this(Args.notNull(sslContext, "SSL context").getSocketFactory(), + null, null, hostnameVerifier); + } + + /** + * @since 4.3 + */ + public SSLSocketFactory( + final SSLContext sslContext, + final String[] supportedProtocols, + final String[] supportedCipherSuites, + final X509HostnameVerifier hostnameVerifier) { + this(Args.notNull(sslContext, "SSL context").getSocketFactory(), + supportedProtocols, supportedCipherSuites, hostnameVerifier); + } + + /** + * @since 4.2 + */ + public SSLSocketFactory( + final javax.net.ssl.SSLSocketFactory socketfactory, + final X509HostnameVerifier hostnameVerifier) { + this(socketfactory, null, null, hostnameVerifier); + } + + /** + * @since 4.3 + */ + public SSLSocketFactory( + final javax.net.ssl.SSLSocketFactory socketfactory, + final String[] supportedProtocols, + final String[] supportedCipherSuites, + final X509HostnameVerifier hostnameVerifier) { + this.socketfactory = Args.notNull(socketfactory, "SSL socket factory"); + this.supportedProtocols = supportedProtocols; + this.supportedCipherSuites = supportedCipherSuites; + this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; + this.nameResolver = null; + } + + /** + * @param params Optional parameters. Parameters passed to this method will have no effect. + * This method will create a unconnected instance of {@link Socket} class. + * @since 4.1 + */ + public Socket createSocket(final HttpParams params) throws IOException { + return createSocket((HttpContext) null); + } + + public Socket createSocket() throws IOException { + return createSocket((HttpContext) null); + } + + /** + * @since 4.1 + */ + public Socket connectSocket( + final Socket socket, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { + Args.notNull(remoteAddress, "Remote address"); + Args.notNull(params, "HTTP parameters"); + final HttpHost host; + if (remoteAddress instanceof HttpInetSocketAddress) { + host = ((HttpInetSocketAddress) remoteAddress).getHttpHost(); + } else { + host = new HttpHost(remoteAddress.getHostName(), remoteAddress.getPort(), "https"); + } + final int socketTimeout = HttpConnectionParams.getSoTimeout(params); + final int connectTimeout = HttpConnectionParams.getConnectionTimeout(params); + socket.setSoTimeout(socketTimeout); + return connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, null); + } + + /** + * Checks whether a socket connection is secure. + * This factory creates TLS/SSL socket connections + * which, by default, are considered secure. + *
    + * Derived classes may override this method to perform + * runtime checks, for example based on the cypher suite. + * + * @param sock the connected socket + * + * @return true + * + * @throws IllegalArgumentException if the argument is invalid + */ + public boolean isSecure(final Socket sock) throws IllegalArgumentException { + Args.notNull(sock, "Socket"); + Asserts.check(sock instanceof SSLSocket, "Socket not created by this factory"); + Asserts.check(!sock.isClosed(), "Socket is closed"); + return true; + } + + /** + * @since 4.2 + */ + public Socket createLayeredSocket( + final Socket socket, + final String host, + final int port, + final HttpParams params) throws IOException, UnknownHostException { + return createLayeredSocket(socket, host, port, (HttpContext) null); + } + + public Socket createLayeredSocket( + final Socket socket, + final String host, + final int port, + final boolean autoClose) throws IOException, UnknownHostException { + return createLayeredSocket(socket, host, port, (HttpContext) null); + } + + public void setHostnameVerifier(final X509HostnameVerifier hostnameVerifier) { + Args.notNull(hostnameVerifier, "Hostname verifier"); + this.hostnameVerifier = hostnameVerifier; + } + + public X509HostnameVerifier getHostnameVerifier() { + return this.hostnameVerifier; + } + + public Socket connectSocket( + final Socket socket, + final String host, final int port, + final InetAddress local, final int localPort, + final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { + final InetAddress remote; + if (this.nameResolver != null) { + remote = this.nameResolver.resolve(host); + } else { + remote = InetAddress.getByName(host); + } + InetSocketAddress localAddress = null; + if (local != null || localPort > 0) { + localAddress = new InetSocketAddress(local, localPort > 0 ? localPort : 0); + } + final InetSocketAddress remoteAddress = new HttpInetSocketAddress( + new HttpHost(host, port), remote, port); + return connectSocket(socket, remoteAddress, localAddress, params); + } + + public Socket createSocket( + final Socket socket, + final String host, final int port, + final boolean autoClose) throws IOException, UnknownHostException { + return createLayeredSocket(socket, host, port, autoClose); + } + + /** + * Performs any custom initialization for a newly created SSLSocket + * (before the SSL handshake happens). + * + * The default implementation is a no-op, but could be overridden to, e.g., + * call {@link SSLSocket#setEnabledCipherSuites(java.lang.String[])}. + * + * @since 4.2 + */ + protected void prepareSocket(final SSLSocket socket) throws IOException { + } + + private void internalPrepareSocket(final SSLSocket socket) throws IOException { + if (supportedProtocols != null) { + socket.setEnabledProtocols(supportedProtocols); + } + if (supportedCipherSuites != null) { + socket.setEnabledCipherSuites(supportedCipherSuites); + } + prepareSocket(socket); + } + + public Socket createSocket(final HttpContext context) throws IOException { + final SSLSocket sock = (SSLSocket) this.socketfactory.createSocket(); + internalPrepareSocket(sock); + return sock; + } + + public Socket connectSocket( + final int connectTimeout, + final Socket socket, + final HttpHost host, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpContext context) throws IOException { + Args.notNull(host, "HTTP host"); + Args.notNull(remoteAddress, "Remote address"); + final Socket sock = socket != null ? socket : createSocket(context); + if (localAddress != null) { + sock.bind(localAddress); + } + try { + sock.connect(remoteAddress, connectTimeout); + } catch (final IOException ex) { + try { + sock.close(); + } catch (final IOException ignore) { + } + throw ex; + } + // Setup SSL layering if necessary + if (sock instanceof SSLSocket) { + final SSLSocket sslsock = (SSLSocket) sock; + sslsock.startHandshake(); + verifyHostname(sslsock, host.getHostName()); + return sock; + } else { + return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), context); + } + } + + public Socket createLayeredSocket( + final Socket socket, + final String target, + final int port, + final HttpContext context) throws IOException { + final SSLSocket sslsock = (SSLSocket) this.socketfactory.createSocket( + socket, + target, + port, + true); + internalPrepareSocket(sslsock); + sslsock.startHandshake(); + verifyHostname(sslsock, target); + return sslsock; + } + + private void verifyHostname(final SSLSocket sslsock, final String hostname) throws IOException { + try { + this.hostnameVerifier.verify(hostname, sslsock); + // verifyHostName() didn't blowup - good! + } catch (final IOException iox) { + // close the socket before re-throwing the exception + try { sslsock.close(); } catch (final Exception x) { /*ignore*/ } + throw iox; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/StrictHostnameVerifier.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/StrictHostnameVerifier.java new file mode 100644 index 000000000..a6328a10c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/StrictHostnameVerifier.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import javax.net.ssl.SSLException; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * The Strict HostnameVerifier works the same way as Sun Java 1.4, Sun + * Java 5, Sun Java 6-rc. It's also pretty close to IE6. This + * implementation appears to be compliant with RFC 2818 for dealing with + * wildcards. + *

    + * The hostname must match either the first CN, or any of the subject-alts. + * A wildcard can occur in the CN, and in any of the subject-alts. The + * one divergence from IE6 is how we only check the first CN. IE6 allows + * a match against any of the CNs present. We decided to follow in + * Sun Java 1.4's footsteps and only check the first CN. (If you need + * to check all the CN's, feel free to write your own implementation!). + *

    + * A wildcard such as "*.foo.com" matches only subdomains in the same + * level, for example "a.foo.com". It does not match deeper subdomains + * such as "a.b.foo.com". + * + * + * @since 4.0 + */ +@Immutable +public class StrictHostnameVerifier extends AbstractVerifier { + + public final void verify( + final String host, + final String[] cns, + final String[] subjectAlts) throws SSLException { + verify(host, cns, subjectAlts, true); + } + + @Override + public final String toString() { + return "STRICT"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TokenParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TokenParser.java new file mode 100644 index 000000000..25bdcc5e6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TokenParser.java @@ -0,0 +1,266 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.util.BitSet; + +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Low level parser for header field elements. The parsing routines of this class are designed + * to produce near zero intermediate garbage and make no intermediate copies of input data. + *

    + * This class is immutable and thread safe. + * + * Temporary package-private copy of ch.boye.httpclientandroidlib.message.TokenParser + */ +class TokenParser { + + public static BitSet INIT_BITSET(final int ... b) { + final BitSet bitset = new BitSet(); + for (final int aB : b) { + bitset.set(aB); + } + return bitset; + } + + /** US-ASCII CR, carriage return (13) */ + public static final char CR = '\r'; + + /** US-ASCII LF, line feed (10) */ + public static final char LF = '\n'; + + /** US-ASCII SP, space (32) */ + public static final char SP = ' '; + + /** US-ASCII HT, horizontal-tab (9) */ + public static final char HT = '\t'; + + /** Double quote */ + public static final char DQUOTE = '\"'; + + /** Backward slash / escape character */ + public static final char ESCAPE = '\\'; + + public static boolean isWhitespace(final char ch) { + return ch == SP || ch == HT || ch == CR || ch == LF; + } + + public static final TokenParser INSTANCE = new TokenParser(); + + /** + * Extracts from the sequence of chars a token terminated with any of the given delimiters + * discarding semantically insignificant whitespace characters. + * + * @param buf buffer with the sequence of chars to be parsed + * @param cursor defines the bounds and current position of the buffer + * @param delimiters set of delimiting characters. Can be null if the token + * is not delimited by any character. + */ + public String parseToken(final CharArrayBuffer buf, final ParserCursor cursor, final BitSet delimiters) { + final StringBuilder dst = new StringBuilder(); + boolean whitespace = false; + while (!cursor.atEnd()) { + final char current = buf.charAt(cursor.getPos()); + if (delimiters != null && delimiters.get(current)) { + break; + } else if (isWhitespace(current)) { + skipWhiteSpace(buf, cursor); + whitespace = true; + } else { + if (whitespace && dst.length() > 0) { + dst.append(' '); + } + copyContent(buf, cursor, delimiters, dst); + whitespace = false; + } + } + return dst.toString(); + } + + /** + * Extracts from the sequence of chars a value which can be enclosed in quote marks and + * terminated with any of the given delimiters discarding semantically insignificant + * whitespace characters. + * + * @param buf buffer with the sequence of chars to be parsed + * @param cursor defines the bounds and current position of the buffer + * @param delimiters set of delimiting characters. Can be null if the value + * is not delimited by any character. + */ + public String parseValue(final CharArrayBuffer buf, final ParserCursor cursor, final BitSet delimiters) { + final StringBuilder dst = new StringBuilder(); + boolean whitespace = false; + while (!cursor.atEnd()) { + final char current = buf.charAt(cursor.getPos()); + if (delimiters != null && delimiters.get(current)) { + break; + } else if (isWhitespace(current)) { + skipWhiteSpace(buf, cursor); + whitespace = true; + } else if (current == DQUOTE) { + if (whitespace && dst.length() > 0) { + dst.append(' '); + } + copyQuotedContent(buf, cursor, dst); + whitespace = false; + } else { + if (whitespace && dst.length() > 0) { + dst.append(' '); + } + copyUnquotedContent(buf, cursor, delimiters, dst); + whitespace = false; + } + } + return dst.toString(); + } + + /** + * Skips semantically insignificant whitespace characters and moves the cursor to the closest + * non-whitespace character. + * + * @param buf buffer with the sequence of chars to be parsed + * @param cursor defines the bounds and current position of the buffer + */ + public void skipWhiteSpace(final CharArrayBuffer buf, final ParserCursor cursor) { + int pos = cursor.getPos(); + final int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + for (int i = indexFrom; i < indexTo; i++) { + final char current = buf.charAt(i); + if (!isWhitespace(current)) { + break; + } else { + pos++; + } + } + cursor.updatePos(pos); + } + + /** + * Transfers content into the destination buffer until a whitespace character or any of + * the given delimiters is encountered. + * + * @param buf buffer with the sequence of chars to be parsed + * @param cursor defines the bounds and current position of the buffer + * @param delimiters set of delimiting characters. Can be null if the value + * is delimited by a whitespace only. + * @param dst destination buffer + */ + public void copyContent(final CharArrayBuffer buf, final ParserCursor cursor, final BitSet delimiters, + final StringBuilder dst) { + int pos = cursor.getPos(); + final int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + for (int i = indexFrom; i < indexTo; i++) { + final char current = buf.charAt(i); + if ((delimiters != null && delimiters.get(current)) || isWhitespace(current)) { + break; + } else { + pos++; + dst.append(current); + } + } + cursor.updatePos(pos); + } + + /** + * Transfers content into the destination buffer until a whitespace character, a quote, + * or any of the given delimiters is encountered. + * + * @param buf buffer with the sequence of chars to be parsed + * @param cursor defines the bounds and current position of the buffer + * @param delimiters set of delimiting characters. Can be null if the value + * is delimited by a whitespace or a quote only. + * @param dst destination buffer + */ + public void copyUnquotedContent(final CharArrayBuffer buf, final ParserCursor cursor, + final BitSet delimiters, final StringBuilder dst) { + int pos = cursor.getPos(); + final int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + for (int i = indexFrom; i < indexTo; i++) { + final char current = buf.charAt(i); + if ((delimiters != null && delimiters.get(current)) + || isWhitespace(current) || current == DQUOTE) { + break; + } else { + pos++; + dst.append(current); + } + } + cursor.updatePos(pos); + } + + /** + * Transfers content enclosed with quote marks into the destination buffer. + * + * @param buf buffer with the sequence of chars to be parsed + * @param cursor defines the bounds and current position of the buffer + * @param dst destination buffer + */ + public void copyQuotedContent(final CharArrayBuffer buf, final ParserCursor cursor, + final StringBuilder dst) { + if (cursor.atEnd()) { + return; + } + int pos = cursor.getPos(); + int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + char current = buf.charAt(pos); + if (current != DQUOTE) { + return; + } + pos++; + indexFrom++; + boolean escaped = false; + for (int i = indexFrom; i < indexTo; i++, pos++) { + current = buf.charAt(i); + if (escaped) { + if (current != DQUOTE && current != ESCAPE) { + dst.append(ESCAPE); + } + dst.append(current); + escaped = false; + } else { + if (current == DQUOTE) { + pos++; + break; + } + if (current == ESCAPE) { + escaped = true; + } else if (current != CR && current != LF) { + dst.append(current); + } + } + } + cursor.updatePos(pos); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustSelfSignedStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustSelfSignedStrategy.java new file mode 100644 index 000000000..a3c23690b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustSelfSignedStrategy.java @@ -0,0 +1,45 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * A trust strategy that accepts self-signed certificates as trusted. Verification of all other + * certificates is done by the trust manager configured in the SSL context. + * + * @since 4.1 + */ +public class TrustSelfSignedStrategy implements TrustStrategy { + + public boolean isTrusted( + final X509Certificate[] chain, final String authType) throws CertificateException { + return chain.length == 1; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustStrategy.java new file mode 100644 index 000000000..7f4914503 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/TrustStrategy.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * A strategy to establish trustworthiness of certificates without consulting the trust manager + * configured in the actual SSL context. This interface can be used to override the standard + * JSSE certificate verification process. + * + * @since 4.1 + */ +public interface TrustStrategy { + + /** + * Determines whether the certificate chain can be trusted without consulting the trust manager + * configured in the actual SSL context. This method can be used to override the standard JSSE + * certificate verification process. + *

    + * Please note that, if this method returns false, the trust manager configured + * in the actual SSL context can still clear the certificate as trusted. + * + * @param chain the peer certificate chain + * @param authType the authentication type based on the client certificate + * @return true if the certificate can be trusted without verification by + * the trust manager, false otherwise. + * @throws CertificateException thrown if the certificate is not trusted or invalid. + */ + boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/X509HostnameVerifier.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/X509HostnameVerifier.java new file mode 100644 index 000000000..cd57c623e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/X509HostnameVerifier.java @@ -0,0 +1,85 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.ssl; + +import java.io.IOException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocket; + +/** + * Interface for checking if a hostname matches the names stored inside the + * server's X.509 certificate. This interface extends + * {@link javax.net.ssl.HostnameVerifier}, but it is recommended to use + * methods added by X509HostnameVerifier. + * + * @since 4.0 + */ +public interface X509HostnameVerifier extends HostnameVerifier { + + /** + * Verifies that the host name is an acceptable match with the server's + * authentication scheme based on the given {@link SSLSocket}. + * + * @param host the host. + * @param ssl the SSL socket. + * @throws IOException if an I/O error occurs or the verification process + * fails. + */ + void verify(String host, SSLSocket ssl) throws IOException; + + /** + * Verifies that the host name is an acceptable match with the server's + * authentication scheme based on the given {@link X509Certificate}. + * + * @param host the host. + * @param cert the certificate. + * @throws SSLException if the verification process fails. + */ + void verify(String host, X509Certificate cert) throws SSLException; + + /** + * Checks to see if the supplied hostname matches any of the supplied CNs + * or "DNS" Subject-Alts. Most implementations only look at the first CN, + * and ignore any additional CNs. Most implementations do look at all of + * the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards + * according to RFC 2818. + * + * @param cns CN fields, in order, as extracted from the X.509 + * certificate. + * @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted + * from the X.509 certificate. + * @param host The hostname to verify. + * @throws SSLException if the verification process fails. + */ + void verify(String host, String[] cns, String[] subjectAlts) + throws SSLException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/package-info.java new file mode 100644 index 000000000..c05bf5be0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/ssl/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client TLS/SSL support. + */ +package ch.boye.httpclientandroidlib.conn.ssl; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/InetAddressUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/InetAddressUtils.java new file mode 100644 index 000000000..ac9a8e2db --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/InetAddressUtils.java @@ -0,0 +1,123 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.conn.util; + +import java.util.regex.Pattern; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * A collection of utilities relating to InetAddresses. + * + * @since 4.0 + */ +@Immutable +public class InetAddressUtils { + + private InetAddressUtils() { + } + + private static final String IPV4_BASIC_PATTERN_STRING = + "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + // initial 3 fields, 0-255 followed by . + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255 + + private static final Pattern IPV4_PATTERN = + Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING + "$"); + + private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros + Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING + "$"); + + private static final Pattern IPV6_STD_PATTERN = + Pattern.compile( + "^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$"); + + private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = + Pattern.compile( + "^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" + // 0-6 hex fields + "::" + + "(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields + + /* + * The above pattern is not totally rigorous as it allows for more than 7 hex fields in total + */ + private static final char COLON_CHAR = ':'; + + // Must not have more than 7 colons (i.e. 8 fields) + private static final int MAX_COLON_COUNT = 7; + + /** + * Checks whether the parameter is a valid IPv4 address + * + * @param input the address string to check for validity + * @return true if the input parameter is a valid IPv4 address + */ + public static boolean isIPv4Address(final String input) { + return IPV4_PATTERN.matcher(input).matches(); + } + + public static boolean isIPv4MappedIPv64Address(final String input) { + return IPV4_MAPPED_IPV6_PATTERN.matcher(input).matches(); + } + + /** + * Checks whether the parameter is a valid standard (non-compressed) IPv6 address + * + * @param input the address string to check for validity + * @return true if the input parameter is a valid standard (non-compressed) IPv6 address + */ + public static boolean isIPv6StdAddress(final String input) { + return IPV6_STD_PATTERN.matcher(input).matches(); + } + + /** + * Checks whether the parameter is a valid compressed IPv6 address + * + * @param input the address string to check for validity + * @return true if the input parameter is a valid compressed IPv6 address + */ + public static boolean isIPv6HexCompressedAddress(final String input) { + int colonCount = 0; + for(int i = 0; i < input.length(); i++) { + if (input.charAt(i) == COLON_CHAR) { + colonCount++; + } + } + return colonCount <= MAX_COLON_COUNT && IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches(); + } + + /** + * Checks whether the parameter is a valid IPv6 address (including compressed). + * + * @param input the address string to check for validity + * @return true if the input parameter is a valid standard or compressed IPv6 address + */ + public static boolean isIPv6Address(final String input) { + return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/package-info.java new file mode 100644 index 000000000..6db59b736 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/util/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Connection utility classes. + */ +package ch.boye.httpclientandroidlib.conn.util; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/ClientCookie.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/ClientCookie.java new file mode 100644 index 000000000..1909f0999 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/ClientCookie.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +/** + * ClientCookie extends the standard {@link Cookie} interface with + * additional client specific functionality such ability to retrieve + * original cookie attributes exactly as they were specified by the + * origin server. This is important for generating the Cookie + * header because some cookie specifications require that the + * Cookie header should include certain attributes only if + * they were specified in the Set-Cookie header. + * + * + * @since 4.0 + */ +public interface ClientCookie extends Cookie { + + // RFC2109 attributes + public static final String VERSION_ATTR = "version"; + public static final String PATH_ATTR = "path"; + public static final String DOMAIN_ATTR = "domain"; + public static final String MAX_AGE_ATTR = "max-age"; + public static final String SECURE_ATTR = "secure"; + public static final String COMMENT_ATTR = "comment"; + public static final String EXPIRES_ATTR = "expires"; + + // RFC2965 attributes + public static final String PORT_ATTR = "port"; + public static final String COMMENTURL_ATTR = "commenturl"; + public static final String DISCARD_ATTR = "discard"; + + String getAttribute(String name); + + boolean containsAttribute(String name); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/Cookie.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/Cookie.java new file mode 100644 index 000000000..9953ab794 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/Cookie.java @@ -0,0 +1,137 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import java.util.Date; + +/** + * Cookie interface represents a token or short packet of state information + * (also referred to as "magic-cookie") that the HTTP agent and the target + * server can exchange to maintain a session. In its simples form an HTTP + * cookie is merely a name / value pair. + * + * @since 4.0 + */ +public interface Cookie { + + /** + * Returns the name. + * + * @return String name The name + */ + String getName(); + + /** + * Returns the value. + * + * @return String value The current value. + */ + String getValue(); + + /** + * Returns the comment describing the purpose of this cookie, or + * null if no such comment has been defined. + * + * @return comment + */ + String getComment(); + + /** + * If a user agent (web browser) presents this cookie to a user, the + * cookie's purpose will be described by the information at this URL. + */ + String getCommentURL(); + + /** + * Returns the expiration {@link Date} of the cookie, or null + * if none exists. + *

    Note: the object returned by this method is + * considered immutable. Changing it (e.g. using setTime()) could result + * in undefined behaviour. Do so at your peril.

    + * @return Expiration {@link Date}, or null. + */ + Date getExpiryDate(); + + /** + * Returns false if the cookie should be discarded at the end + * of the "session"; true otherwise. + * + * @return false if the cookie should be discarded at the end + * of the "session"; true otherwise + */ + boolean isPersistent(); + + /** + * Returns domain attribute of the cookie. The value of the Domain + * attribute specifies the domain for which the cookie is valid. + * + * @return the value of the domain attribute. + */ + String getDomain(); + + /** + * Returns the path attribute of the cookie. The value of the Path + * attribute specifies the subset of URLs on the origin server to which + * this cookie applies. + * + * @return The value of the path attribute. + */ + String getPath(); + + /** + * Get the Port attribute. It restricts the ports to which a cookie + * may be returned in a Cookie request header. + */ + int[] getPorts(); + + /** + * Indicates whether this cookie requires a secure connection. + * + * @return true if this cookie should only be sent + * over secure connections, false otherwise. + */ + boolean isSecure(); + + /** + * Returns the version of the cookie specification to which this + * cookie conforms. + * + * @return the version of the cookie. + */ + int getVersion(); + + /** + * Returns true if this cookie has expired. + * @param date Current time + * + * @return true if the cookie has expired. + */ + boolean isExpired(final Date date); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieAttributeHandler.java new file mode 100644 index 000000000..3d94c8a6f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieAttributeHandler.java @@ -0,0 +1,73 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.cookie; + +/** + * This interface represents a cookie attribute handler responsible + * for parsing, validating, and matching a specific cookie attribute, + * such as path, domain, port, etc. + * + * Different cookie specifications can provide a specific + * implementation for this class based on their cookie handling + * rules. + * + * + * @since 4.0 + */ +public interface CookieAttributeHandler { + + /** + * Parse the given cookie attribute value and update the corresponding + * {@link ch.boye.httpclientandroidlib.cookie.Cookie} property. + * + * @param cookie {@link ch.boye.httpclientandroidlib.cookie.Cookie} to be updated + * @param value cookie attribute value from the cookie response header + */ + void parse(SetCookie cookie, String value) + throws MalformedCookieException; + + /** + * Peforms cookie validation for the given attribute value. + * + * @param cookie {@link ch.boye.httpclientandroidlib.cookie.Cookie} to validate + * @param origin the cookie source to validate against + * @throws MalformedCookieException if cookie validation fails for this attribute + */ + void validate(Cookie cookie, CookieOrigin origin) + throws MalformedCookieException; + + /** + * Matches the given value (property of the destination host where request is being + * submitted) with the corresponding cookie attribute. + * + * @param cookie {@link ch.boye.httpclientandroidlib.cookie.Cookie} to match + * @param origin the cookie source to match against + * @return true if the match is successful; false otherwise + */ + boolean match(Cookie cookie, CookieOrigin origin); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieIdentityComparator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieIdentityComparator.java new file mode 100644 index 000000000..863640e69 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieIdentityComparator.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import java.io.Serializable; +import java.util.Comparator; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * This cookie comparator can be used to compare identity of cookies. + *

    + * Cookies are considered identical if their names are equal and + * their domain attributes match ignoring case. + * + * @since 4.0 + */ +@Immutable +public class CookieIdentityComparator implements Serializable, Comparator { + + private static final long serialVersionUID = 4466565437490631532L; + + public int compare(final Cookie c1, final Cookie c2) { + int res = c1.getName().compareTo(c2.getName()); + if (res == 0) { + // do not differentiate empty and null domains + String d1 = c1.getDomain(); + if (d1 == null) { + d1 = ""; + } else if (d1.indexOf('.') == -1) { + d1 = d1 + ".local"; + } + String d2 = c2.getDomain(); + if (d2 == null) { + d2 = ""; + } else if (d2.indexOf('.') == -1) { + d2 = d2 + ".local"; + } + res = d1.compareToIgnoreCase(d2); + } + if (res == 0) { + String p1 = c1.getPath(); + if (p1 == null) { + p1 = "/"; + } + String p2 = c2.getPath(); + if (p2 == null) { + p2 = "/"; + } + res = p1.compareTo(p2); + } + return res; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieOrigin.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieOrigin.java new file mode 100644 index 000000000..aef29023d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieOrigin.java @@ -0,0 +1,94 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.cookie; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * CookieOrigin class encapsulates details of an origin server that + * are relevant when parsing, validating or matching HTTP cookies. + * + * @since 4.0 + */ +@Immutable +public final class CookieOrigin { + + private final String host; + private final int port; + private final String path; + private final boolean secure; + + public CookieOrigin(final String host, final int port, final String path, final boolean secure) { + super(); + Args.notBlank(host, "Host"); + Args.notNegative(port, "Port"); + Args.notNull(path, "Path"); + this.host = host.toLowerCase(Locale.ENGLISH); + this.port = port; + if (path.trim().length() != 0) { + this.path = path; + } else { + this.path = "/"; + } + this.secure = secure; + } + + public String getHost() { + return this.host; + } + + public String getPath() { + return this.path; + } + + public int getPort() { + return this.port; + } + + public boolean isSecure() { + return this.secure; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append('['); + if (this.secure) { + buffer.append("(secure)"); + } + buffer.append(this.host); + buffer.append(':'); + buffer.append(Integer.toString(this.port)); + buffer.append(this.path); + buffer.append(']'); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookiePathComparator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookiePathComparator.java new file mode 100644 index 000000000..984bb850f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookiePathComparator.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import java.io.Serializable; +import java.util.Comparator; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * This cookie comparator ensures that multiple cookies satisfying + * a common criteria are ordered in the Cookie header such + * that those with more specific Path attributes precede those with + * less specific. + * + *

    + * This comparator assumes that Path attributes of two cookies + * path-match a commmon request-URI. Otherwise, the result of the + * comparison is undefined. + *

    + * + * + * @since 4.0 + */ +@Immutable +public class CookiePathComparator implements Serializable, Comparator { + + private static final long serialVersionUID = 7523645369616405818L; + + private String normalizePath(final Cookie cookie) { + String path = cookie.getPath(); + if (path == null) { + path = "/"; + } + if (!path.endsWith("/")) { + path = path + '/'; + } + return path; + } + + public int compare(final Cookie c1, final Cookie c2) { + final String path1 = normalizePath(c1); + final String path2 = normalizePath(c2); + if (path1.equals(path2)) { + return 0; + } else if (path1.startsWith(path2)) { + return -1; + } else if (path2.startsWith(path1)) { + return 1; + } else { + // Does not really matter + return 0; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieRestrictionViolationException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieRestrictionViolationException.java new file mode 100644 index 000000000..1e3f2d70c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieRestrictionViolationException.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that a cookie violates a restriction imposed by the cookie + * specification. + * + * @since 4.1 + */ +@Immutable +public class CookieRestrictionViolationException extends MalformedCookieException { + + private static final long serialVersionUID = 7371235577078589013L; + + /** + * Creates a new CookeFormatViolationException with a null detail + * message. + */ + public CookieRestrictionViolationException() { + super(); + } + + /** + * Creates a new CookeRestrictionViolationException with a specified + * message string. + * + * @param message The exception detail message + */ + public CookieRestrictionViolationException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpec.java new file mode 100644 index 000000000..c222e5e72 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpec.java @@ -0,0 +1,109 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import java.util.List; + +import ch.boye.httpclientandroidlib.Header; + +/** + * Defines the cookie management specification. + *

    Cookie management specification must define + *

      + *
    • rules of parsing "Set-Cookie" header + *
    • rules of validation of parsed cookies + *
    • formatting of "Cookie" header + *
    + * for a given host, port and path of origin + * + * + * @since 4.0 + */ +public interface CookieSpec { + + /** + * Returns version of the state management this cookie specification + * conforms to. + * + * @return version of the state management specification + */ + int getVersion(); + + /** + * Parse the "Set-Cookie" Header into an array of Cookies. + * + *

    This method will not perform the validation of the resultant + * {@link Cookie}s

    + * + * @see #validate + * + * @param header the Set-Cookie received from the server + * @param origin details of the cookie origin + * @return an array of Cookies parsed from the header + * @throws MalformedCookieException if an exception occurs during parsing + */ + List parse(Header header, CookieOrigin origin) throws MalformedCookieException; + + /** + * Validate the cookie according to validation rules defined by the + * cookie specification. + * + * @param cookie the Cookie to validate + * @param origin details of the cookie origin + * @throws MalformedCookieException if the cookie is invalid + */ + void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException; + + /** + * Determines if a Cookie matches the target location. + * + * @param cookie the Cookie to be matched + * @param origin the target to test against + * + * @return true if the cookie should be submitted with a request + * with given attributes, false otherwise. + */ + boolean match(Cookie cookie, CookieOrigin origin); + + /** + * Create "Cookie" headers for an array of Cookies. + * + * @param cookies the Cookies format into a Cookie header + * @return a Header for the given Cookies. + * @throws IllegalArgumentException if an input parameter is illegal + */ + List
    formatCookies(List cookies); + + /** + * Returns a request header identifying what version of the state management + * specification is understood. May be null if the cookie + * specification does not support Cookie2 header. + */ + Header getVersionHeader(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecFactory.java new file mode 100644 index 000000000..fd7851626 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecFactory.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * Factory for {@link CookieSpec} implementations. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link CookieSpecProvider} + */ +@Deprecated +public interface CookieSpecFactory { + + /** + * Creates an instance of {@link CookieSpec} using given HTTP parameters. + * + * @param params HTTP parameters. + * + * @return cookie spec. + */ + CookieSpec newInstance(HttpParams params); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecProvider.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecProvider.java new file mode 100644 index 000000000..921adfba4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecProvider.java @@ -0,0 +1,46 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Factory for {@link CookieSpec} implementations. + * + * @since 4.3 + */ +public interface CookieSpecProvider { + + /** + * Creates an instance of {@link CookieSpec}. + * + * @return auth scheme. + */ + CookieSpec create(HttpContext context); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecRegistry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecRegistry.java new file mode 100644 index 000000000..adbfe863a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/CookieSpecRegistry.java @@ -0,0 +1,167 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.ExecutionContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Cookie specification registry that can be used to obtain the corresponding + * cookie specification implementation for a given type of type or version of + * cookie. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.config.Registry}. + */ +@ThreadSafe +@Deprecated +public final class CookieSpecRegistry implements Lookup { + + private final ConcurrentHashMap registeredSpecs; + + public CookieSpecRegistry() { + super(); + this.registeredSpecs = new ConcurrentHashMap(); + } + + /** + * Registers a {@link CookieSpecFactory} with the given identifier. + * If a specification with the given name already exists it will be overridden. + * This nameis the same one used to retrieve the {@link CookieSpecFactory} + * from {@link #getCookieSpec(String)}. + * + * @param name the identifier for this specification + * @param factory the {@link CookieSpecFactory} class to register + * + * @see #getCookieSpec(String) + */ + public void register(final String name, final CookieSpecFactory factory) { + Args.notNull(name, "Name"); + Args.notNull(factory, "Cookie spec factory"); + registeredSpecs.put(name.toLowerCase(Locale.ENGLISH), factory); + } + + /** + * Unregisters the {@link CookieSpecFactory} with the given ID. + * + * @param id the identifier of the {@link CookieSpec cookie specification} to unregister + */ + public void unregister(final String id) { + Args.notNull(id, "Id"); + registeredSpecs.remove(id.toLowerCase(Locale.ENGLISH)); + } + + /** + * Gets the {@link CookieSpec cookie specification} with the given ID. + * + * @param name the {@link CookieSpec cookie specification} identifier + * @param params the {@link HttpParams HTTP parameters} for the cookie + * specification. + * + * @return {@link CookieSpec cookie specification} + * + * @throws IllegalStateException if a policy with the given name cannot be found + */ + public CookieSpec getCookieSpec(final String name, final HttpParams params) + throws IllegalStateException { + + Args.notNull(name, "Name"); + final CookieSpecFactory factory = registeredSpecs.get(name.toLowerCase(Locale.ENGLISH)); + if (factory != null) { + return factory.newInstance(params); + } else { + throw new IllegalStateException("Unsupported cookie spec: " + name); + } + } + + /** + * Gets the {@link CookieSpec cookie specification} with the given name. + * + * @param name the {@link CookieSpec cookie specification} identifier + * + * @return {@link CookieSpec cookie specification} + * + * @throws IllegalStateException if a policy with the given name cannot be found + */ + public CookieSpec getCookieSpec(final String name) + throws IllegalStateException { + return getCookieSpec(name, null); + } + + /** + * Obtains a list containing the names of all registered {@link CookieSpec cookie + * specs}. + * + * Note that the DEFAULT policy (if present) is likely to be the same + * as one of the other policies, but does not have to be. + * + * @return list of registered cookie spec names + */ + public List getSpecNames(){ + return new ArrayList(registeredSpecs.keySet()); + } + + /** + * Populates the internal collection of registered {@link CookieSpec cookie + * specs} with the content of the map passed as a parameter. + * + * @param map cookie specs + */ + public void setItems(final Map map) { + if (map == null) { + return; + } + registeredSpecs.clear(); + registeredSpecs.putAll(map); + } + + public CookieSpecProvider lookup(final String name) { + return new CookieSpecProvider() { + + public CookieSpec create(final HttpContext context) { + final HttpRequest request = (HttpRequest) context.getAttribute( + ExecutionContext.HTTP_REQUEST); + return getCookieSpec(name, request.getParams()); + } + + }; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/MalformedCookieException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/MalformedCookieException.java new file mode 100644 index 000000000..dba7b9b25 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/MalformedCookieException.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that a cookie is in some way invalid or illegal in a given + * context + * + * + * @since 4.0 + */ +@Immutable +public class MalformedCookieException extends ProtocolException { + + private static final long serialVersionUID = -6695462944287282185L; + + /** + * Creates a new MalformedCookieException with a null detail message. + */ + public MalformedCookieException() { + super(); + } + + /** + * Creates a new MalformedCookieException with a specified message string. + * + * @param message The exception detail message + */ + public MalformedCookieException(final String message) { + super(message); + } + + /** + * Creates a new MalformedCookieException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public MalformedCookieException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SM.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SM.java new file mode 100644 index 000000000..463da4d25 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SM.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +/** + * Constants and static helpers related to the HTTP state management. + * + * + * @since 4.0 + */ +public interface SM { + + public static final String COOKIE = "Cookie"; + public static final String COOKIE2 = "Cookie2"; + public static final String SET_COOKIE = "Set-Cookie"; + public static final String SET_COOKIE2 = "Set-Cookie2"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie.java new file mode 100644 index 000000000..5bd5b7121 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie.java @@ -0,0 +1,109 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +import java.util.Date; + +/** + * This interface represents a Set-Cookie response header sent by the + * origin server to the HTTP agent in order to maintain a conversational state. + * + * @since 4.0 + */ +public interface SetCookie extends Cookie { + + void setValue(String value); + + /** + * If a user agent (web browser) presents this cookie to a user, the + * cookie's purpose will be described using this comment. + * + * @param comment + * + * @see #getComment() + */ + void setComment(String comment); + + /** + * Sets expiration date. + *

    Note: the object returned by this method is considered + * immutable. Changing it (e.g. using setTime()) could result in undefined + * behaviour. Do so at your peril.

    + * + * @param expiryDate the {@link Date} after which this cookie is no longer valid. + * + * @see Cookie#getExpiryDate + * + */ + void setExpiryDate (Date expiryDate); + + /** + * Sets the domain attribute. + * + * @param domain The value of the domain attribute + * + * @see Cookie#getDomain + */ + void setDomain(String domain); + + /** + * Sets the path attribute. + * + * @param path The value of the path attribute + * + * @see Cookie#getPath + * + */ + void setPath(String path); + + /** + * Sets the secure attribute of the cookie. + *

    + * When true the cookie should only be sent + * using a secure protocol (https). This should only be set when + * the cookie's originating server used a secure protocol to set the + * cookie's value. + * + * @param secure The value of the secure attribute + * + * @see #isSecure() + */ + void setSecure (boolean secure); + + /** + * Sets the version of the cookie specification to which this + * cookie conforms. + * + * @param version the version of the cookie. + * + * @see Cookie#getVersion + */ + void setVersion(int version); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie2.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie2.java new file mode 100644 index 000000000..bc3508059 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/SetCookie2.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie; + +/** + * This interface represents a Set-Cookie2 response header sent by the + * origin server to the HTTP agent in order to maintain a conversational state. + * + * @since 4.0 + */ +public interface SetCookie2 extends SetCookie { + + /** + * If a user agent (web browser) presents this cookie to a user, the + * cookie's purpose will be described by the information at this URL. + */ + void setCommentURL(String commentURL); + + /** + * Sets the Port attribute. It restricts the ports to which a cookie + * may be returned in a Cookie request header. + */ + void setPorts(int[] ports); + + /** + * Set the Discard attribute. + * + * Note: Discard attribute overrides Max-age. + * + * @see #isPersistent() + */ + void setDiscard(boolean discard); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/package-info.java new file mode 100644 index 000000000..80a831709 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client HTTP state management APIs. + */ +package ch.boye.httpclientandroidlib.cookie; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecPNames.java new file mode 100644 index 000000000..a93a26f16 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecPNames.java @@ -0,0 +1,65 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie.params; + +/** + * Parameter names for HTTP cookie management classes. + * + * @since 4.0 + * + * @deprecated (4.3) use constructor parameters of {@link + * ch.boye.httpclientandroidlib.cookie.CookieSpecProvider}s. + */ +@Deprecated +public interface CookieSpecPNames { + + /** + * Defines valid date patterns to be used for parsing non-standard + * expires attribute. Only required for compatibility + * with non-compliant servers that still use expires + * defined in the Netscape draft instead of the standard + * max-age attribute. + *

    + * This parameter expects a value of type {@link java.util.Collection}. + * The collection elements must be of type {@link String} compatible + * with the syntax of {@link java.text.SimpleDateFormat}. + *

    + */ + public static final String DATE_PATTERNS = "http.protocol.cookie-datepatterns"; + + /** + * Defines whether cookies should be forced into a single + * Cookie request header. Otherwise, each cookie is formatted + * as a separate Cookie header. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + */ + public static final String SINGLE_COOKIE_HEADER = "http.protocol.single-cookie-header"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecParamBean.java new file mode 100644 index 000000000..0e007a54f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/CookieSpecParamBean.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.cookie.params; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.HttpAbstractParamBean; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate HTTP cookie parameters using Java Beans + * conventions. + * + * @since 4.0 + * + * @deprecated (4.3) use constructor parameters of {@link + * ch.boye.httpclientandroidlib.cookie.CookieSpecProvider}s. + */ +@Deprecated +@NotThreadSafe +public class CookieSpecParamBean extends HttpAbstractParamBean { + + public CookieSpecParamBean (final HttpParams params) { + super(params); + } + + public void setDatePatterns (final Collection patterns) { + params.setParameter(CookieSpecPNames.DATE_PATTERNS, patterns); + } + + public void setSingleHeader (final boolean singleHeader) { + params.setBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, singleHeader); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/package-info.java new file mode 100644 index 000000000..9314e1e63 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/cookie/params/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Deprecated. + * @deprecated (4.3). + */ +package ch.boye.httpclientandroidlib.cookie.params; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/AbstractHttpEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/AbstractHttpEntity.java new file mode 100644 index 000000000..9fc32fa29 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/AbstractHttpEntity.java @@ -0,0 +1,191 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.message.BasicHeader; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * Abstract base class for entities. + * Provides the commonly used attributes for streamed and self-contained + * implementations of {@link HttpEntity HttpEntity}. + * + * @since 4.0 + */ +@NotThreadSafe +public abstract class AbstractHttpEntity implements HttpEntity { + + /** + * Buffer size for output stream processing. + * + * @since 4.3 + */ + protected static final int OUTPUT_BUFFER_SIZE = 4096; + + protected Header contentType; + protected Header contentEncoding; + protected boolean chunked; + + /** + * Protected default constructor. + * The contentType, contentEncoding and chunked attributes of the created object are set to + * null, null and false, respectively. + */ + protected AbstractHttpEntity() { + super(); + } + + + /** + * Obtains the Content-Type header. + * The default implementation returns the value of the + * {@link #contentType contentType} attribute. + * + * @return the Content-Type header, or null + */ + public Header getContentType() { + return this.contentType; + } + + + /** + * Obtains the Content-Encoding header. + * The default implementation returns the value of the + * {@link #contentEncoding contentEncoding} attribute. + * + * @return the Content-Encoding header, or null + */ + public Header getContentEncoding() { + return this.contentEncoding; + } + + /** + * Obtains the 'chunked' flag. + * The default implementation returns the value of the + * {@link #chunked chunked} attribute. + * + * @return the 'chunked' flag + */ + public boolean isChunked() { + return this.chunked; + } + + + /** + * Specifies the Content-Type header. + * The default implementation sets the value of the + * {@link #contentType contentType} attribute. + * + * @param contentType the new Content-Encoding header, or + * null to unset + */ + public void setContentType(final Header contentType) { + this.contentType = contentType; + } + + /** + * Specifies the Content-Type header, as a string. + * The default implementation calls + * {@link #setContentType(Header) setContentType(Header)}. + * + * @param ctString the new Content-Type header, or + * null to unset + */ + public void setContentType(final String ctString) { + Header h = null; + if (ctString != null) { + h = new BasicHeader(HTTP.CONTENT_TYPE, ctString); + } + setContentType(h); + } + + + /** + * Specifies the Content-Encoding header. + * The default implementation sets the value of the + * {@link #contentEncoding contentEncoding} attribute. + * + * @param contentEncoding the new Content-Encoding header, or + * null to unset + */ + public void setContentEncoding(final Header contentEncoding) { + this.contentEncoding = contentEncoding; + } + + /** + * Specifies the Content-Encoding header, as a string. + * The default implementation calls + * {@link #setContentEncoding(Header) setContentEncoding(Header)}. + * + * @param ceString the new Content-Encoding header, or + * null to unset + */ + public void setContentEncoding(final String ceString) { + Header h = null; + if (ceString != null) { + h = new BasicHeader(HTTP.CONTENT_ENCODING, ceString); + } + setContentEncoding(h); + } + + + /** + * Specifies the 'chunked' flag. + *

    + * Note that the chunked setting is a hint only. + * If using HTTP/1.0, chunking is never performed. + * Otherwise, even if chunked is false, HttpClient must + * use chunk coding if the entity content length is + * unknown (-1). + *

    + * The default implementation sets the value of the + * {@link #chunked chunked} attribute. + * + * @param b the new 'chunked' flag + */ + public void setChunked(final boolean b) { + this.chunked = b; + } + + + /** + * The default implementation does not consume anything. + * + * @deprecated (4.1) Either use {@link #getContent()} and call {@link java.io.InputStream#close()} on that; + * otherwise call {@link #writeTo(java.io.OutputStream)} which is required to free the resources. + */ + @Deprecated + public void consumeContent() throws IOException { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BasicHttpEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BasicHttpEntity.java new file mode 100644 index 000000000..74a281b2a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BasicHttpEntity.java @@ -0,0 +1,125 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A generic streamed, non-repeatable entity that obtains its content + * from an {@link InputStream}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicHttpEntity extends AbstractHttpEntity { + + private InputStream content; + private long length; + + /** + * Creates a new basic entity. + * The content is initially missing, the content length + * is set to a negative number. + */ + public BasicHttpEntity() { + super(); + this.length = -1; + } + + public long getContentLength() { + return this.length; + } + + /** + * Obtains the content, once only. + * + * @return the content, if this is the first call to this method + * since {@link #setContent setContent} has been called + * + * @throws IllegalStateException + * if the content has not been provided + */ + public InputStream getContent() throws IllegalStateException { + Asserts.check(this.content != null, "Content has not been provided"); + return this.content; + } + + /** + * Tells that this entity is not repeatable. + * + * @return false + */ + public boolean isRepeatable() { + return false; + } + + /** + * Specifies the length of the content. + * + * @param len the number of bytes in the content, or + * a negative number to indicate an unknown length + */ + public void setContentLength(final long len) { + this.length = len; + } + + /** + * Specifies the content. + * + * @param instream the stream to return with the next call to + * {@link #getContent getContent} + */ + public void setContent(final InputStream instream) { + this.content = instream; + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + final InputStream instream = getContent(); + try { + int l; + final byte[] tmp = new byte[OUTPUT_BUFFER_SIZE]; + while ((l = instream.read(tmp)) != -1) { + outstream.write(tmp, 0, l); + } + } finally { + instream.close(); + } + } + + public boolean isStreaming() { + return this.content != null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BufferedHttpEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BufferedHttpEntity.java new file mode 100644 index 000000000..6cb7495d7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/BufferedHttpEntity.java @@ -0,0 +1,125 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * A wrapping entity that buffers it content if necessary. + * The buffered entity is always repeatable. + * If the wrapped entity is repeatable itself, calls are passed through. + * If the wrapped entity is not repeatable, the content is read into a + * buffer once and provided from there as often as required. + * + * @since 4.0 + */ +@NotThreadSafe +public class BufferedHttpEntity extends HttpEntityWrapper { + + private final byte[] buffer; + + /** + * Creates a new buffered entity wrapper. + * + * @param entity the entity to wrap, not null + * @throws IllegalArgumentException if wrapped is null + */ + public BufferedHttpEntity(final HttpEntity entity) throws IOException { + super(entity); + if (!entity.isRepeatable() || entity.getContentLength() < 0) { + this.buffer = EntityUtils.toByteArray(entity); + } else { + this.buffer = null; + } + } + + @Override + public long getContentLength() { + if (this.buffer != null) { + return this.buffer.length; + } else { + return super.getContentLength(); + } + } + + @Override + public InputStream getContent() throws IOException { + if (this.buffer != null) { + return new ByteArrayInputStream(this.buffer); + } else { + return super.getContent(); + } + } + + /** + * Tells that this entity does not have to be chunked. + * + * @return false + */ + @Override + public boolean isChunked() { + return (buffer == null) && super.isChunked(); + } + + /** + * Tells that this entity is repeatable. + * + * @return true + */ + @Override + public boolean isRepeatable() { + return true; + } + + + @Override + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + if (this.buffer != null) { + outstream.write(this.buffer); + } else { + super.writeTo(outstream); + } + } + + + // non-javadoc, see interface HttpEntity + @Override + public boolean isStreaming() { + return (buffer == null) && super.isStreaming(); + } + +} // class BufferedHttpEntity diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ByteArrayEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ByteArrayEntity.java new file mode 100644 index 000000000..9f0af3ab8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ByteArrayEntity.java @@ -0,0 +1,131 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A self contained, repeatable entity that obtains its content from a byte array. + * + * @since 4.0 + */ +@NotThreadSafe +public class ByteArrayEntity extends AbstractHttpEntity implements Cloneable { + + /** + * @deprecated (4.2) + */ + @Deprecated + protected final byte[] content; + private final byte[] b; + private final int off, len; + + /** + * @since 4.2 + */ + @SuppressWarnings("deprecation") + public ByteArrayEntity(final byte[] b, final ContentType contentType) { + super(); + Args.notNull(b, "Source byte array"); + this.content = b; + this.b = b; + this.off = 0; + this.len = this.b.length; + if (contentType != null) { + setContentType(contentType.toString()); + } + } + + /** + * @since 4.2 + */ + @SuppressWarnings("deprecation") + public ByteArrayEntity(final byte[] b, final int off, final int len, final ContentType contentType) { + super(); + Args.notNull(b, "Source byte array"); + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: " + off + " len: " + len + " b.length: " + b.length); + } + this.content = b; + this.b = b; + this.off = off; + this.len = len; + if (contentType != null) { + setContentType(contentType.toString()); + } + } + + public ByteArrayEntity(final byte[] b) { + this(b, null); + } + + public ByteArrayEntity(final byte[] b, final int off, final int len) { + this(b, off, len, null); + } + + public boolean isRepeatable() { + return true; + } + + public long getContentLength() { + return this.len; + } + + public InputStream getContent() { + return new ByteArrayInputStream(this.b, this.off, this.len); + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + outstream.write(this.b, this.off, this.len); + outstream.flush(); + } + + + /** + * Tells that this entity is not streaming. + * + * @return false + */ + public boolean isStreaming() { + return false; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} // class ByteArrayEntity diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentLengthStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentLengthStrategy.java new file mode 100644 index 000000000..4dbc1afaf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentLengthStrategy.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; + +/** + * Represents a strategy to determine length of the enclosed content entity + * based on properties of the HTTP message. + * + * @since 4.0 + */ +public interface ContentLengthStrategy { + + public static final int IDENTITY = -1; + public static final int CHUNKED = -2; + + /** + * Returns length of the given message in bytes. The returned value + * must be a non-negative number, {@link #IDENTITY} if the end of the + * message will be delimited by the end of connection, or {@link #CHUNKED} + * if the message is chunk coded + * + * @param message HTTP message + * @return content length, {@link #IDENTITY}, or {@link #CHUNKED} + * + * @throws HttpException in case of HTTP protocol violation + */ + long determineLength(HttpMessage message) throws HttpException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentProducer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentProducer.java new file mode 100644 index 000000000..3ced2e48b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentProducer.java @@ -0,0 +1,44 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * An abstract entity content producer. + *

    Content producers are expected to be able to produce their + * content multiple times

    + * + * @since 4.0 + */ +public interface ContentProducer { + + void writeTo(OutputStream outstream) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentType.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentType.java new file mode 100644 index 000000000..e486b83b3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/ContentType.java @@ -0,0 +1,305 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueFormatter; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; +import ch.boye.httpclientandroidlib.util.TextUtils; + +/** + * Content type information consisting of a MIME type and an optional charset. + *

    + * This class makes no attempts to verify validity of the MIME type. + * The input parameters of the {@link #create(String, String)} method, however, may not + * contain characters <">, <;>, <,> reserved by the HTTP specification. + * + * @since 4.2 + */ +@Immutable +public final class ContentType implements Serializable { + + private static final long serialVersionUID = -7768694718232371896L; + + // constants + public static final ContentType APPLICATION_ATOM_XML = create( + "application/atom+xml", Consts.ISO_8859_1); + public static final ContentType APPLICATION_FORM_URLENCODED = create( + "application/x-www-form-urlencoded", Consts.ISO_8859_1); + public static final ContentType APPLICATION_JSON = create( + "application/json", Consts.UTF_8); + public static final ContentType APPLICATION_OCTET_STREAM = create( + "application/octet-stream", (Charset) null); + public static final ContentType APPLICATION_SVG_XML = create( + "application/svg+xml", Consts.ISO_8859_1); + public static final ContentType APPLICATION_XHTML_XML = create( + "application/xhtml+xml", Consts.ISO_8859_1); + public static final ContentType APPLICATION_XML = create( + "application/xml", Consts.ISO_8859_1); + public static final ContentType MULTIPART_FORM_DATA = create( + "multipart/form-data", Consts.ISO_8859_1); + public static final ContentType TEXT_HTML = create( + "text/html", Consts.ISO_8859_1); + public static final ContentType TEXT_PLAIN = create( + "text/plain", Consts.ISO_8859_1); + public static final ContentType TEXT_XML = create( + "text/xml", Consts.ISO_8859_1); + public static final ContentType WILDCARD = create( + "*/*", (Charset) null); + + // defaults + public static final ContentType DEFAULT_TEXT = TEXT_PLAIN; + public static final ContentType DEFAULT_BINARY = APPLICATION_OCTET_STREAM; + + private final String mimeType; + private final Charset charset; + private final NameValuePair[] params; + + ContentType( + final String mimeType, + final Charset charset) { + this.mimeType = mimeType; + this.charset = charset; + this.params = null; + } + + ContentType( + final String mimeType, + final NameValuePair[] params) throws UnsupportedCharsetException { + this.mimeType = mimeType; + this.params = params; + final String s = getParameter("charset"); + this.charset = !TextUtils.isBlank(s) ? Charset.forName(s) : null; + } + + public String getMimeType() { + return this.mimeType; + } + + public Charset getCharset() { + return this.charset; + } + + /** + * @since 4.3 + */ + public String getParameter(final String name) { + Args.notEmpty(name, "Parameter name"); + if (this.params == null) { + return null; + } + for (final NameValuePair param: this.params) { + if (param.getName().equalsIgnoreCase(name)) { + return param.getValue(); + } + } + return null; + } + + /** + * Generates textual representation of this content type which can be used as the value + * of a Content-Type header. + */ + @Override + public String toString() { + final CharArrayBuffer buf = new CharArrayBuffer(64); + buf.append(this.mimeType); + if (this.params != null) { + buf.append("; "); + BasicHeaderValueFormatter.INSTANCE.formatParameters(buf, this.params, false); + } else if (this.charset != null) { + buf.append("; charset="); + buf.append(this.charset.name()); + } + return buf.toString(); + } + + private static boolean valid(final String s) { + for (int i = 0; i < s.length(); i++) { + final char ch = s.charAt(i); + if (ch == '"' || ch == ',' || ch == ';') { + return false; + } + } + return true; + } + + /** + * Creates a new instance of {@link ContentType}. + * + * @param mimeType MIME type. It may not be null or empty. It may not contain + * characters <">, <;>, <,> reserved by the HTTP specification. + * @param charset charset. + * @return content type + */ + public static ContentType create(final String mimeType, final Charset charset) { + final String type = Args.notBlank(mimeType, "MIME type").toLowerCase(Locale.US); + Args.check(valid(type), "MIME type may not contain reserved characters"); + return new ContentType(type, charset); + } + + /** + * Creates a new instance of {@link ContentType} without a charset. + * + * @param mimeType MIME type. It may not be null or empty. It may not contain + * characters <">, <;>, <,> reserved by the HTTP specification. + * @return content type + */ + public static ContentType create(final String mimeType) { + return new ContentType(mimeType, (Charset) null); + } + + /** + * Creates a new instance of {@link ContentType}. + * + * @param mimeType MIME type. It may not be null or empty. It may not contain + * characters <">, <;>, <,> reserved by the HTTP specification. + * @param charset charset. It may not contain characters <">, <;>, <,> reserved by the HTTP + * specification. This parameter is optional. + * @return content type + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public static ContentType create( + final String mimeType, final String charset) throws UnsupportedCharsetException { + return create(mimeType, !TextUtils.isBlank(charset) ? Charset.forName(charset) : null); + } + + private static ContentType create(final HeaderElement helem) { + final String mimeType = helem.getName(); + final NameValuePair[] params = helem.getParameters(); + return new ContentType(mimeType, params != null && params.length > 0 ? params : null); + } + + /** + * Parses textual representation of Content-Type value. + * + * @param s text + * @return content type + * @throws ParseException if the given text does not represent a valid + * Content-Type value. + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public static ContentType parse( + final String s) throws ParseException, UnsupportedCharsetException { + Args.notNull(s, "Content type"); + final CharArrayBuffer buf = new CharArrayBuffer(s.length()); + buf.append(s); + final ParserCursor cursor = new ParserCursor(0, s.length()); + final HeaderElement[] elements = BasicHeaderValueParser.INSTANCE.parseElements(buf, cursor); + if (elements.length > 0) { + return create(elements[0]); + } else { + throw new ParseException("Invalid content type: " + s); + } + } + + /** + * Extracts Content-Type value from {@link HttpEntity} exactly as + * specified by the Content-Type header of the entity. Returns null + * if not specified. + * + * @param entity HTTP entity + * @return content type + * @throws ParseException if the given text does not represent a valid + * Content-Type value. + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public static ContentType get( + final HttpEntity entity) throws ParseException, UnsupportedCharsetException { + if (entity == null) { + return null; + } + final Header header = entity.getContentType(); + if (header != null) { + final HeaderElement[] elements = header.getElements(); + if (elements.length > 0) { + return create(elements[0]); + } + } + return null; + } + + /** + * Extracts Content-Type value from {@link HttpEntity} or returns the default value + * {@link #DEFAULT_TEXT} if not explicitly specified. + * + * @param entity HTTP entity + * @return content type + * @throws ParseException if the given text does not represent a valid + * Content-Type value. + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public static ContentType getOrDefault( + final HttpEntity entity) throws ParseException, UnsupportedCharsetException { + final ContentType contentType = get(entity); + return contentType != null ? contentType : DEFAULT_TEXT; + } + + /** + * Creates a new instance with this MIME type and the given Charset. + * + * @param charset charset + * @return a new instance with this MIME type and the given Charset. + * @since 4.3 + */ + public ContentType withCharset(final Charset charset) { + return create(this.getMimeType(), charset); + } + + /** + * Creates a new instance with this MIME type and the given Charset name. + * + * @param charset name + * @return a new instance with this MIME type and the given Charset name. + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + * @since 4.3 + */ + public ContentType withCharset(final String charset) { + return create(this.getMimeType(), charset); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/EntityTemplate.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/EntityTemplate.java new file mode 100644 index 000000000..8e5a79431 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/EntityTemplate.java @@ -0,0 +1,76 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Entity that delegates the process of content generation + * to a {@link ContentProducer}. + * + * @since 4.0 + */ +public class EntityTemplate extends AbstractHttpEntity { + + private final ContentProducer contentproducer; + + public EntityTemplate(final ContentProducer contentproducer) { + super(); + this.contentproducer = Args.notNull(contentproducer, "Content producer"); + } + + public long getContentLength() { + return -1; + } + + public InputStream getContent() throws IOException { + final ByteArrayOutputStream buf = new ByteArrayOutputStream(); + writeTo(buf); + return new ByteArrayInputStream(buf.toByteArray()); + } + + public boolean isRepeatable() { + return true; + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + this.contentproducer.writeTo(outstream); + } + + public boolean isStreaming() { + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/FileEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/FileEntity.java new file mode 100644 index 000000000..8801d3733 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/FileEntity.java @@ -0,0 +1,121 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A self contained, repeatable entity that obtains its content from a file. + * + * @since 4.0 + */ +@NotThreadSafe +public class FileEntity extends AbstractHttpEntity implements Cloneable { + + protected final File file; + + /** + * @deprecated (4.1.3) {@link #FileEntity(File, ContentType)} + */ + @Deprecated + public FileEntity(final File file, final String contentType) { + super(); + this.file = Args.notNull(file, "File"); + setContentType(contentType); + } + + /** + * @since 4.2 + */ + public FileEntity(final File file, final ContentType contentType) { + super(); + this.file = Args.notNull(file, "File"); + if (contentType != null) { + setContentType(contentType.toString()); + } + } + + /** + * @since 4.2 + */ + public FileEntity(final File file) { + super(); + this.file = Args.notNull(file, "File"); + } + + public boolean isRepeatable() { + return true; + } + + public long getContentLength() { + return this.file.length(); + } + + public InputStream getContent() throws IOException { + return new FileInputStream(this.file); + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + final InputStream instream = new FileInputStream(this.file); + try { + final byte[] tmp = new byte[OUTPUT_BUFFER_SIZE]; + int l; + while ((l = instream.read(tmp)) != -1) { + outstream.write(tmp, 0, l); + } + outstream.flush(); + } finally { + instream.close(); + } + } + + /** + * Tells that this entity is not streaming. + * + * @return false + */ + public boolean isStreaming() { + return false; + } + + @Override + public Object clone() throws CloneNotSupportedException { + // File instance is considered immutable + // No need to make a copy of it + return super.clone(); + } + +} // class FileEntity diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/HttpEntityWrapper.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/HttpEntityWrapper.java new file mode 100644 index 000000000..77b1245f4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/HttpEntityWrapper.java @@ -0,0 +1,105 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Base class for wrapping entities. + * Keeps a {@link #wrappedEntity wrappedEntity} and delegates all + * calls to it. Implementations of wrapping entities can derive + * from this class and need to override only those methods that + * should not be delegated to the wrapped entity. + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpEntityWrapper implements HttpEntity { + + /** The wrapped entity. */ + protected HttpEntity wrappedEntity; + + /** + * Creates a new entity wrapper. + */ + public HttpEntityWrapper(final HttpEntity wrappedEntity) { + super(); + this.wrappedEntity = Args.notNull(wrappedEntity, "Wrapped entity"); + } // constructor + + public boolean isRepeatable() { + return wrappedEntity.isRepeatable(); + } + + public boolean isChunked() { + return wrappedEntity.isChunked(); + } + + public long getContentLength() { + return wrappedEntity.getContentLength(); + } + + public Header getContentType() { + return wrappedEntity.getContentType(); + } + + public Header getContentEncoding() { + return wrappedEntity.getContentEncoding(); + } + + public InputStream getContent() + throws IOException { + return wrappedEntity.getContent(); + } + + public void writeTo(final OutputStream outstream) + throws IOException { + wrappedEntity.writeTo(outstream); + } + + public boolean isStreaming() { + return wrappedEntity.isStreaming(); + } + + /** + * @deprecated (4.1) Either use {@link #getContent()} and call {@link java.io.InputStream#close()} on that; + * otherwise call {@link #writeTo(OutputStream)} which is required to free the resources. + */ + @Deprecated + public void consumeContent() throws IOException { + wrappedEntity.consumeContent(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/InputStreamEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/InputStreamEntity.java new file mode 100644 index 000000000..8a53ef68c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/InputStreamEntity.java @@ -0,0 +1,155 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A streamed, non-repeatable entity that obtains its content from + * an {@link InputStream}. + * + * @since 4.0 + */ +@NotThreadSafe +public class InputStreamEntity extends AbstractHttpEntity { + + private final InputStream content; + private final long length; + + /** + * Creates an entity with an unknown length. + * Equivalent to {@code new InputStreamEntity(instream, -1)}. + * + * @param instream input stream + * @throws IllegalArgumentException if {@code instream} is {@code null} + * @since 4.3 + */ + public InputStreamEntity(final InputStream instream) { + this(instream, -1); + } + + /** + * Creates an entity with a specified content length. + * + * @param instream input stream + * @param length of the input stream, {@code -1} if unknown + * @throws IllegalArgumentException if {@code instream} is {@code null} + */ + public InputStreamEntity(final InputStream instream, final long length) { + this(instream, length, null); + } + + /** + * Creates an entity with a content type and unknown length. + * Equivalent to {@code new InputStreamEntity(instream, -1, contentType)}. + * + * @param instream input stream + * @param contentType content type + * @throws IllegalArgumentException if {@code instream} is {@code null} + * @since 4.3 + */ + public InputStreamEntity(final InputStream instream, final ContentType contentType) { + this(instream, -1, contentType); + } + + /** + * @param instream input stream + * @param length of the input stream, {@code -1} if unknown + * @param contentType for specifying the {@code Content-Type} header, may be {@code null} + * @throws IllegalArgumentException if {@code instream} is {@code null} + * @since 4.2 + */ + public InputStreamEntity(final InputStream instream, final long length, final ContentType contentType) { + super(); + this.content = Args.notNull(instream, "Source input stream"); + this.length = length; + if (contentType != null) { + setContentType(contentType.toString()); + } + } + + public boolean isRepeatable() { + return false; + } + + /** + * @return the content length or {@code -1} if unknown + */ + public long getContentLength() { + return this.length; + } + + public InputStream getContent() throws IOException { + return this.content; + } + + /** + * Writes bytes from the {@code InputStream} this entity was constructed + * with to an {@code OutputStream}. The content length + * determines how many bytes are written. If the length is unknown ({@code -1}), the + * stream will be completely consumed (to the end of the stream). + * + */ + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + final InputStream instream = this.content; + try { + final byte[] buffer = new byte[OUTPUT_BUFFER_SIZE]; + int l; + if (this.length < 0) { + // consume until EOF + while ((l = instream.read(buffer)) != -1) { + outstream.write(buffer, 0, l); + } + } else { + // consume no more than length + long remaining = this.length; + while (remaining > 0) { + l = instream.read(buffer, 0, (int)Math.min(OUTPUT_BUFFER_SIZE, remaining)); + if (l == -1) { + break; + } + outstream.write(buffer, 0, l); + remaining -= l; + } + } + } finally { + instream.close(); + } + } + + public boolean isStreaming() { + return true; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/SerializableEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/SerializableEntity.java new file mode 100644 index 000000000..f5e125309 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/SerializableEntity.java @@ -0,0 +1,126 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A streamed entity that obtains its content from a {@link Serializable}. + * The content obtained from the {@link Serializable} instance can + * optionally be buffered in a byte array in order to make the + * entity self-contained and repeatable. + * + * @since 4.0 + */ +@NotThreadSafe +public class SerializableEntity extends AbstractHttpEntity { + + private byte[] objSer; + + private Serializable objRef; + + /** + * Creates new instance of this class. + * + * @param ser input + * @param bufferize tells whether the content should be + * stored in an internal buffer + * @throws IOException in case of an I/O error + */ + public SerializableEntity(final Serializable ser, final boolean bufferize) throws IOException { + super(); + Args.notNull(ser, "Source object"); + if (bufferize) { + createBytes(ser); + } else { + this.objRef = ser; + } + } + + /** + * @since 4.3 + */ + public SerializableEntity(final Serializable ser) { + super(); + Args.notNull(ser, "Source object"); + this.objRef = ser; + } + + private void createBytes(final Serializable ser) throws IOException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(ser); + out.flush(); + this.objSer = baos.toByteArray(); + } + + public InputStream getContent() throws IOException, IllegalStateException { + if (this.objSer == null) { + createBytes(this.objRef); + } + return new ByteArrayInputStream(this.objSer); + } + + public long getContentLength() { + if (this.objSer == null) { + return -1; + } else { + return this.objSer.length; + } + } + + public boolean isRepeatable() { + return true; + } + + public boolean isStreaming() { + return this.objSer == null; + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + if (this.objSer == null) { + final ObjectOutputStream out = new ObjectOutputStream(outstream); + out.writeObject(this.objRef); + out.flush(); + } else { + outstream.write(this.objSer); + outstream.flush(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/StringEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/StringEntity.java new file mode 100644 index 000000000..bf4889119 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/StringEntity.java @@ -0,0 +1,188 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A self contained, repeatable entity that obtains its content from + * a {@link String}. + * + * @since 4.0 + */ +@NotThreadSafe +public class StringEntity extends AbstractHttpEntity implements Cloneable { + + protected final byte[] content; + + /** + * Creates a StringEntity with the specified content and content type. + * + * @param string content to be used. Not {@code null}. + * @param contentType content type to be used. May be {@code null}, in which case the default + * MIME type {@link ContentType#TEXT_PLAIN} is assumed. + * + * @throws IllegalArgumentException if the string parameter is null + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + * @since 4.2 + */ + public StringEntity(final String string, final ContentType contentType) throws UnsupportedCharsetException { + super(); + Args.notNull(string, "Source string"); + Charset charset = contentType != null ? contentType.getCharset() : null; + if (charset == null) { + charset = HTTP.DEF_CONTENT_CHARSET; + } + try { + this.content = string.getBytes(charset.name()); + } catch (final UnsupportedEncodingException ex) { + // should never happen + throw new UnsupportedCharsetException(charset.name()); + } + if (contentType != null) { + setContentType(contentType.toString()); + } + } + + /** + * Creates a StringEntity with the specified content, MIME type and charset + * + * @param string content to be used. Not {@code null}. + * @param mimeType MIME type to be used. May be {@code null}, in which case the default + * is {@link HTTP#PLAIN_TEXT_TYPE} i.e. "text/plain" + * @param charset character set to be used. May be {@code null}, in which case the default + * is {@link HTTP#DEF_CONTENT_CHARSET} i.e. "ISO-8859-1" + * @throws UnsupportedEncodingException If the named charset is not supported. + * + * @since 4.1 + * @throws IllegalArgumentException if the string parameter is null + * + * @deprecated (4.1.3) use {@link #StringEntity(String, ContentType)} + */ + @Deprecated + public StringEntity( + final String string, final String mimeType, final String charset) throws UnsupportedEncodingException { + super(); + Args.notNull(string, "Source string"); + final String mt = mimeType != null ? mimeType : HTTP.PLAIN_TEXT_TYPE; + final String cs = charset != null ? charset :HTTP.DEFAULT_CONTENT_CHARSET; + this.content = string.getBytes(cs); + setContentType(mt + HTTP.CHARSET_PARAM + cs); + } + + /** + * Creates a StringEntity with the specified content and charset. The MIME type defaults + * to "text/plain". + * + * @param string content to be used. Not {@code null}. + * @param charset character set to be used. May be {@code null}, in which case the default + * is {@link HTTP#DEF_CONTENT_CHARSET} is assumed + * + * @throws IllegalArgumentException if the string parameter is null + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public StringEntity(final String string, final String charset) + throws UnsupportedCharsetException { + this(string, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), charset)); + } + + /** + * Creates a StringEntity with the specified content and charset. The MIME type defaults + * to "text/plain". + * + * @param string content to be used. Not {@code null}. + * @param charset character set to be used. May be {@code null}, in which case the default + * is {@link HTTP#DEF_CONTENT_CHARSET} is assumed + * + * @throws IllegalArgumentException if the string parameter is null + * + * @since 4.2 + */ + public StringEntity(final String string, final Charset charset) { + this(string, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), charset)); + } + + /** + * Creates a StringEntity with the specified content. The content type defaults to + * {@link ContentType#TEXT_PLAIN}. + * + * @param string content to be used. Not {@code null}. + * + * @throws IllegalArgumentException if the string parameter is null + * @throws UnsupportedEncodingException if the default HTTP charset is not supported. + */ + public StringEntity(final String string) + throws UnsupportedEncodingException { + this(string, ContentType.DEFAULT_TEXT); + } + + public boolean isRepeatable() { + return true; + } + + public long getContentLength() { + return this.content.length; + } + + public InputStream getContent() throws IOException { + return new ByteArrayInputStream(this.content); + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + outstream.write(this.content); + outstream.flush(); + } + + /** + * Tells that this entity is not streaming. + * + * @return false + */ + public boolean isStreaming() { + return false; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} // class StringEntity diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/AbstractMultipartForm.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/AbstractMultipartForm.java new file mode 100644 index 000000000..ade6bfe4b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/AbstractMultipartForm.java @@ -0,0 +1,211 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.List; + +import ch.boye.httpclientandroidlib.entity.mime.content.ContentBody; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.ByteArrayBuffer; + +/** + * HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is + * capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or + * the browser compatible modes. + * + * @since 4.3 + */ +abstract class AbstractMultipartForm { + + private static ByteArrayBuffer encode( + final Charset charset, final String string) { + final ByteBuffer encoded = charset.encode(CharBuffer.wrap(string)); + final ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining()); + bab.append(encoded.array(), encoded.position(), encoded.remaining()); + return bab; + } + + private static void writeBytes( + final ByteArrayBuffer b, final OutputStream out) throws IOException { + out.write(b.buffer(), 0, b.length()); + } + + private static void writeBytes( + final String s, final Charset charset, final OutputStream out) throws IOException { + final ByteArrayBuffer b = encode(charset, s); + writeBytes(b, out); + } + + private static void writeBytes( + final String s, final OutputStream out) throws IOException { + final ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s); + writeBytes(b, out); + } + + protected static void writeField( + final MinimalField field, final OutputStream out) throws IOException { + writeBytes(field.getName(), out); + writeBytes(FIELD_SEP, out); + writeBytes(field.getBody(), out); + writeBytes(CR_LF, out); + } + + protected static void writeField( + final MinimalField field, final Charset charset, final OutputStream out) throws IOException { + writeBytes(field.getName(), charset, out); + writeBytes(FIELD_SEP, out); + writeBytes(field.getBody(), charset, out); + writeBytes(CR_LF, out); + } + + private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": "); + private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n"); + private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--"); + + private final String subType; + protected final Charset charset; + private final String boundary; + + /** + * Creates an instance with the specified settings. + * + * @param subType MIME subtype - must not be {@code null} + * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used. + * @param boundary to use - must not be {@code null} + * @throws IllegalArgumentException if charset is null or boundary is null + */ + public AbstractMultipartForm(final String subType, final Charset charset, final String boundary) { + super(); + Args.notNull(subType, "Multipart subtype"); + Args.notNull(boundary, "Multipart boundary"); + this.subType = subType; + this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET; + this.boundary = boundary; + } + + public AbstractMultipartForm(final String subType, final String boundary) { + this(subType, null, boundary); + } + + public String getSubType() { + return this.subType; + } + + public Charset getCharset() { + return this.charset; + } + + public abstract List getBodyParts(); + + public String getBoundary() { + return this.boundary; + } + + void doWriteTo( + final OutputStream out, + final boolean writeContent) throws IOException { + + final ByteArrayBuffer boundary = encode(this.charset, getBoundary()); + for (final FormBodyPart part: getBodyParts()) { + writeBytes(TWO_DASHES, out); + writeBytes(boundary, out); + writeBytes(CR_LF, out); + + formatMultipartHeader(part, out); + + writeBytes(CR_LF, out); + + if (writeContent) { + part.getBody().writeTo(out); + } + writeBytes(CR_LF, out); + } + writeBytes(TWO_DASHES, out); + writeBytes(boundary, out); + writeBytes(TWO_DASHES, out); + writeBytes(CR_LF, out); + } + + /** + * Write the multipart header fields; depends on the style. + */ + protected abstract void formatMultipartHeader( + final FormBodyPart part, + final OutputStream out) throws IOException; + + /** + * Writes out the content in the multipart/form encoding. This method + * produces slightly different formatting depending on its compatibility + * mode. + */ + public void writeTo(final OutputStream out) throws IOException { + doWriteTo(out, true); + } + + /** + * Determines the total length of the multipart content (content length of + * individual parts plus that of extra elements required to delimit the parts + * from one another). If any of the @{link BodyPart}s contained in this object + * is of a streaming entity of unknown length the total length is also unknown. + *

    + * This method buffers only a small amount of data in order to determine the + * total length of the entire entity. The content of individual parts is not + * buffered. + * + * @return total length of the multipart entity if known, -1 + * otherwise. + */ + public long getTotalLength() { + long contentLen = 0; + for (final FormBodyPart part: getBodyParts()) { + final ContentBody body = part.getBody(); + final long len = body.getContentLength(); + if (len >= 0) { + contentLen += len; + } else { + return -1; + } + } + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + doWriteTo(out, false); + final byte[] extra = out.toByteArray(); + return contentLen + extra.length; + } catch (final IOException ex) { + // Should never happen + return -1; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/FormBodyPart.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/FormBodyPart.java new file mode 100644 index 000000000..a3d2c0954 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/FormBodyPart.java @@ -0,0 +1,116 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.mime.content.AbstractContentBody; +import ch.boye.httpclientandroidlib.entity.mime.content.ContentBody; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * FormBodyPart class represents a content body that can be used as a part of multipart encoded + * entities. This class automatically populates the header with standard fields based on + * the content description of the enclosed body. + * + * @since 4.0 + */ +public class FormBodyPart { + + private final String name; + private final Header header; + + private final ContentBody body; + + public FormBodyPart(final String name, final ContentBody body) { + super(); + Args.notNull(name, "Name"); + Args.notNull(body, "Body"); + this.name = name; + this.body = body; + this.header = new Header(); + + generateContentDisp(body); + generateContentType(body); + generateTransferEncoding(body); + } + + public String getName() { + return this.name; + } + + public ContentBody getBody() { + return this.body; + } + + public Header getHeader() { + return this.header; + } + + public void addField(final String name, final String value) { + Args.notNull(name, "Field name"); + this.header.addField(new MinimalField(name, value)); + } + + protected void generateContentDisp(final ContentBody body) { + final StringBuilder buffer = new StringBuilder(); + buffer.append("form-data; name=\""); + buffer.append(getName()); + buffer.append("\""); + if (body.getFilename() != null) { + buffer.append("; filename=\""); + buffer.append(body.getFilename()); + buffer.append("\""); + } + addField(MIME.CONTENT_DISPOSITION, buffer.toString()); + } + + protected void generateContentType(final ContentBody body) { + final ContentType contentType; + if (body instanceof AbstractContentBody) { + contentType = ((AbstractContentBody) body).getContentType(); + } else { + contentType = null; + } + if (contentType != null) { + addField(MIME.CONTENT_TYPE, contentType.toString()); + } else { + final StringBuilder buffer = new StringBuilder(); + buffer.append(body.getMimeType()); // MimeType cannot be null + if (body.getCharset() != null) { // charset may legitimately be null + buffer.append("; charset="); + buffer.append(body.getCharset()); + } + addField(MIME.CONTENT_TYPE, buffer.toString()); + } + } + + protected void generateTransferEncoding(final ContentBody body) { + addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/Header.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/Header.java new file mode 100644 index 000000000..15d70acc9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/Header.java @@ -0,0 +1,144 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * The header of an entity (see RFC 2045). + */ +public class Header implements Iterable { + + private final List fields; + private final Map> fieldMap; + + public Header() { + super(); + this.fields = new LinkedList(); + this.fieldMap = new HashMap>(); + } + + public void addField(final MinimalField field) { + if (field == null) { + return; + } + final String key = field.getName().toLowerCase(Locale.ENGLISH); + List values = this.fieldMap.get(key); + if (values == null) { + values = new LinkedList(); + this.fieldMap.put(key, values); + } + values.add(field); + this.fields.add(field); + } + + public List getFields() { + return new ArrayList(this.fields); + } + + public MinimalField getField(final String name) { + if (name == null) { + return null; + } + final String key = name.toLowerCase(Locale.ENGLISH); + final List list = this.fieldMap.get(key); + if (list != null && !list.isEmpty()) { + return list.get(0); + } + return null; + } + + public List getFields(final String name) { + if (name == null) { + return null; + } + final String key = name.toLowerCase(Locale.ENGLISH); + final List list = this.fieldMap.get(key); + if (list == null || list.isEmpty()) { + return Collections.emptyList(); + } else { + return new ArrayList(list); + } + } + + public int removeFields(final String name) { + if (name == null) { + return 0; + } + final String key = name.toLowerCase(Locale.ENGLISH); + final List removed = fieldMap.remove(key); + if (removed == null || removed.isEmpty()) { + return 0; + } + this.fields.removeAll(removed); + return removed.size(); + } + + public void setField(final MinimalField field) { + if (field == null) { + return; + } + final String key = field.getName().toLowerCase(Locale.ENGLISH); + final List list = fieldMap.get(key); + if (list == null || list.isEmpty()) { + addField(field); + return; + } + list.clear(); + list.add(field); + int firstOccurrence = -1; + int index = 0; + for (final Iterator it = this.fields.iterator(); it.hasNext(); index++) { + final MinimalField f = it.next(); + if (f.getName().equalsIgnoreCase(field.getName())) { + it.remove(); + if (firstOccurrence == -1) { + firstOccurrence = index; + } + } + } + this.fields.add(firstOccurrence, field); + } + + public Iterator iterator() { + return Collections.unmodifiableList(fields).iterator(); + } + + @Override + public String toString() { + return this.fields.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpBrowserCompatibleMultipart.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpBrowserCompatibleMultipart.java new file mode 100644 index 000000000..dfc0a657e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpBrowserCompatibleMultipart.java @@ -0,0 +1,79 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.List; + +/** + * HttpBrowserCompatibleMultipart represents a collection of MIME multipart encoded + * content bodies. This class is emulates browser compatibility, e.g. IE 5 or earlier. + * + * @since 4.3 + */ +class HttpBrowserCompatibleMultipart extends AbstractMultipartForm { + + private final List parts; + + public HttpBrowserCompatibleMultipart( + final String subType, + final Charset charset, + final String boundary, + final List parts) { + super(subType, charset, boundary); + this.parts = parts; + } + + @Override + public List getBodyParts() { + return this.parts; + } + + /** + * Write the multipart header fields; depends on the style. + */ + @Override + protected void formatMultipartHeader( + final FormBodyPart part, + final OutputStream out) throws IOException { + // For browser-compatible, only write Content-Disposition + // Use content charset + final Header header = part.getHeader(); + final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION); + writeField(cd, this.charset, out); + final String filename = part.getBody().getFilename(); + if (filename != null) { + final MinimalField ct = header.getField(MIME.CONTENT_TYPE); + writeField(ct, this.charset, out); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpMultipartMode.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpMultipartMode.java new file mode 100644 index 000000000..ab73e4983 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpMultipartMode.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +/** + * + * @since 4.0 + */ +public enum HttpMultipartMode { + + /** RFC 822, RFC 2045, RFC 2046 compliant */ + STRICT, + /** browser-compatible mode, i.e. only write Content-Disposition; use content charset */ + BROWSER_COMPATIBLE, + /** RFC 6532 compliant */ + RFC6532 + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpRFC6532Multipart.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpRFC6532Multipart.java new file mode 100644 index 000000000..634a12b23 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpRFC6532Multipart.java @@ -0,0 +1,72 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.List; + +/** + * HttpRFC6532Multipart represents a collection of MIME multipart encoded content bodies, + * implementing the strict (RFC 822, RFC 2045, RFC 2046 compliant) interpretation + * of the spec, with the exception of allowing UTF-8 headers, as per RFC6532. + * + * @since 4.3 + */ +class HttpRFC6532Multipart extends AbstractMultipartForm { + + private final List parts; + + public HttpRFC6532Multipart( + final String subType, + final Charset charset, + final String boundary, + final List parts) { + super(subType, charset, boundary); + this.parts = parts; + } + + @Override + public List getBodyParts() { + return this.parts; + } + + @Override + protected void formatMultipartHeader( + final FormBodyPart part, + final OutputStream out) throws IOException { + + // For RFC6532, we output all fields with UTF-8 encoding. + final Header header = part.getHeader(); + for (final MinimalField field: header) { + writeField(field, MIME.UTF8_CHARSET, out); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpStrictMultipart.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpStrictMultipart.java new file mode 100644 index 000000000..d6e68c6e2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/HttpStrictMultipart.java @@ -0,0 +1,72 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.List; + +/** + * HttpStrictMultipart represents a collection of MIME multipart encoded content bodies, + * implementing the strict (RFC 822, RFC 2045, RFC 2046 compliant) interpretation + * of the spec. + * + * @since 4.3 + */ +class HttpStrictMultipart extends AbstractMultipartForm { + + private final List parts; + + public HttpStrictMultipart( + final String subType, + final Charset charset, + final String boundary, + final List parts) { + super(subType, charset, boundary); + this.parts = parts; + } + + @Override + public List getBodyParts() { + return this.parts; + } + + @Override + protected void formatMultipartHeader( + final FormBodyPart part, + final OutputStream out) throws IOException { + + // For strict, we output all fields with MIME-standard encoding. + final Header header = part.getHeader(); + for (final MinimalField field: header) { + writeField(field, out); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MIME.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MIME.java new file mode 100644 index 000000000..d5896312d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MIME.java @@ -0,0 +1,53 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import ch.boye.httpclientandroidlib.Consts; + +import java.nio.charset.Charset; + +/** + * + * @since 4.0 + */ +public final class MIME { + + public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_TRANSFER_ENC = "Content-Transfer-Encoding"; + public static final String CONTENT_DISPOSITION = "Content-Disposition"; + + public static final String ENC_8BIT = "8bit"; + public static final String ENC_BINARY = "binary"; + + /** The default character set to be used, i.e. "US-ASCII" */ + public static final Charset DEFAULT_CHARSET = Consts.ASCII; + + /** UTF-8 is used for RFC6532 */ + public static final Charset UTF8_CHARSET = Consts.UTF_8; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MinimalField.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MinimalField.java new file mode 100644 index 000000000..35af51233 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MinimalField.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +/** + * Minimal MIME field. + * + * @since 4.0 + */ +public class MinimalField { + + private final String name; + private final String value; + + public MinimalField(final String name, final String value) { + super(); + this.name = name; + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getBody() { + return this.value; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append(this.name); + buffer.append(": "); + buffer.append(this.value); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartEntityBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartEntityBuilder.java new file mode 100644 index 000000000..5074001d7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartEntityBuilder.java @@ -0,0 +1,207 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import java.io.File; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.mime.content.ByteArrayBody; +import ch.boye.httpclientandroidlib.entity.mime.content.ContentBody; +import ch.boye.httpclientandroidlib.entity.mime.content.FileBody; +import ch.boye.httpclientandroidlib.entity.mime.content.InputStreamBody; +import ch.boye.httpclientandroidlib.entity.mime.content.StringBody; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Builder for multipart {@link HttpEntity}s. + * + * @since 4.3 + */ +public class MultipartEntityBuilder { + + /** + * The pool of ASCII chars to be used for generating a multipart boundary. + */ + private final static char[] MULTIPART_CHARS = + "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + .toCharArray(); + + private final static String DEFAULT_SUBTYPE = "form-data"; + + private String subType = DEFAULT_SUBTYPE; + private HttpMultipartMode mode = HttpMultipartMode.STRICT; + private String boundary = null; + private Charset charset = null; + private List bodyParts = null; + + public static MultipartEntityBuilder create() { + return new MultipartEntityBuilder(); + } + + MultipartEntityBuilder() { + super(); + } + + public MultipartEntityBuilder setMode(final HttpMultipartMode mode) { + this.mode = mode; + return this; + } + + public MultipartEntityBuilder setLaxMode() { + this.mode = HttpMultipartMode.BROWSER_COMPATIBLE; + return this; + } + + public MultipartEntityBuilder setStrictMode() { + this.mode = HttpMultipartMode.STRICT; + return this; + } + + public MultipartEntityBuilder setBoundary(final String boundary) { + this.boundary = boundary; + return this; + } + + public MultipartEntityBuilder setCharset(final Charset charset) { + this.charset = charset; + return this; + } + + MultipartEntityBuilder addPart(final FormBodyPart bodyPart) { + if (bodyPart == null) { + return this; + } + if (this.bodyParts == null) { + this.bodyParts = new ArrayList(); + } + this.bodyParts.add(bodyPart); + return this; + } + + public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) { + Args.notNull(name, "Name"); + Args.notNull(contentBody, "Content body"); + return addPart(new FormBodyPart(name, contentBody)); + } + + public MultipartEntityBuilder addTextBody( + final String name, final String text, final ContentType contentType) { + return addPart(name, new StringBody(text, contentType)); + } + + public MultipartEntityBuilder addTextBody( + final String name, final String text) { + return addTextBody(name, text, ContentType.DEFAULT_TEXT); + } + + public MultipartEntityBuilder addBinaryBody( + final String name, final byte[] b, final ContentType contentType, final String filename) { + return addPart(name, new ByteArrayBody(b, contentType, filename)); + } + + public MultipartEntityBuilder addBinaryBody( + final String name, final byte[] b) { + return addBinaryBody(name, b, ContentType.DEFAULT_BINARY, null); + } + + public MultipartEntityBuilder addBinaryBody( + final String name, final File file, final ContentType contentType, final String filename) { + return addPart(name, new FileBody(file, contentType, filename)); + } + + public MultipartEntityBuilder addBinaryBody( + final String name, final File file) { + return addBinaryBody(name, file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null); + } + + public MultipartEntityBuilder addBinaryBody( + final String name, final InputStream stream, final ContentType contentType, + final String filename) { + return addPart(name, new InputStreamBody(stream, contentType, filename)); + } + + public MultipartEntityBuilder addBinaryBody(final String name, final InputStream stream) { + return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null); + } + + private String generateContentType( + final String boundary, + final Charset charset) { + final StringBuilder buffer = new StringBuilder(); + buffer.append("multipart/form-data; boundary="); + buffer.append(boundary); + if (charset != null) { + buffer.append("; charset="); + buffer.append(charset.name()); + } + return buffer.toString(); + } + + private String generateBoundary() { + final StringBuilder buffer = new StringBuilder(); + final Random rand = new Random(); + final int count = rand.nextInt(11) + 30; // a random size from 30 to 40 + for (int i = 0; i < count; i++) { + buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]); + } + return buffer.toString(); + } + + MultipartFormEntity buildEntity() { + final String st = subType != null ? subType : DEFAULT_SUBTYPE; + final Charset cs = charset; + final String b = boundary != null ? boundary : generateBoundary(); + final List bps = bodyParts != null ? new ArrayList(bodyParts) : + Collections.emptyList(); + final HttpMultipartMode m = mode != null ? mode : HttpMultipartMode.STRICT; + final AbstractMultipartForm form; + switch (m) { + case BROWSER_COMPATIBLE: + form = new HttpBrowserCompatibleMultipart(st, cs, b, bps); + break; + case RFC6532: + form = new HttpRFC6532Multipart(st, cs, b, bps); + break; + default: + form = new HttpStrictMultipart(st, cs, b, bps); + } + return new MultipartFormEntity(form, generateContentType(b, cs), form.getTotalLength()); + } + + public HttpEntity build() { + return buildEntity(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartFormEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartFormEntity.java new file mode 100644 index 000000000..9da2f4724 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/MultipartFormEntity.java @@ -0,0 +1,100 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.message.BasicHeader; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +class MultipartFormEntity implements HttpEntity { + + private final AbstractMultipartForm multipart; + private final Header contentType; + private final long contentLength; + + MultipartFormEntity( + final AbstractMultipartForm multipart, + final String contentType, + final long contentLength) { + super(); + this.multipart = multipart; + this.contentType = new BasicHeader(HTTP.CONTENT_TYPE, contentType); + this.contentLength = contentLength; + } + + AbstractMultipartForm getMultipart() { + return this.multipart; + } + + public boolean isRepeatable() { + return this.contentLength != -1; + } + + public boolean isChunked() { + return !isRepeatable(); + } + + public boolean isStreaming() { + return !isRepeatable(); + } + + public long getContentLength() { + return this.contentLength; + } + + public Header getContentType() { + return this.contentType; + } + + public Header getContentEncoding() { + return null; + } + + public void consumeContent() + throws IOException, UnsupportedOperationException{ + if (isStreaming()) { + throw new UnsupportedOperationException( + "Streaming entity does not implement #consumeContent()"); + } + } + + public InputStream getContent() throws IOException { + throw new UnsupportedOperationException( + "Multipart form entity does not implement #getContent()"); + } + + public void writeTo(final OutputStream outstream) throws IOException { + this.multipart.writeTo(outstream); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/AbstractContentBody.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/AbstractContentBody.java new file mode 100644 index 000000000..b6b58ad35 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/AbstractContentBody.java @@ -0,0 +1,96 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime.content; + +import java.nio.charset.Charset; + +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +public abstract class AbstractContentBody implements ContentBody { + + private final ContentType contentType; + + /** + * @since 4.3 + */ + public AbstractContentBody(final ContentType contentType) { + super(); + Args.notNull(contentType, "Content type"); + this.contentType = contentType; + } + + /** + * @deprecated (4.3) use {@link AbstractContentBody#AbstractContentBody(ContentType)} + */ + @Deprecated + public AbstractContentBody(final String mimeType) { + this(ContentType.parse(mimeType)); + } + + /** + * @since 4.3 + */ + public ContentType getContentType() { + return this.contentType; + } + + public String getMimeType() { + return this.contentType.getMimeType(); + } + + public String getMediaType() { + final String mimeType = this.contentType.getMimeType(); + final int i = mimeType.indexOf('/'); + if (i != -1) { + return mimeType.substring(0, i); + } else { + return mimeType; + } + } + + public String getSubType() { + final String mimeType = this.contentType.getMimeType(); + final int i = mimeType.indexOf('/'); + if (i != -1) { + return mimeType.substring(i + 1); + } else { + return null; + } + } + + public String getCharset() { + final Charset charset = this.contentType.getCharset(); + return charset != null ? charset.name() : null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ByteArrayBody.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ByteArrayBody.java new file mode 100644 index 000000000..ea60fa047 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ByteArrayBody.java @@ -0,0 +1,111 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.entity.mime.content; + +import java.io.IOException; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.mime.MIME; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Binary body part backed by a byte array. + * + * @see ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder + * + * @since 4.1 + */ +public class ByteArrayBody extends AbstractContentBody { + + /** + * The contents of the file contained in this part. + */ + private final byte[] data; + + /** + * The name of the file contained in this part. + */ + private final String filename; + + /** + * Creates a new ByteArrayBody. + * + * @param data The contents of the file contained in this part. + * @param mimeType The MIME type of the file contained in this part. + * @param filename The name of the file contained in this part. + * + * @deprecated (4.3) use {@link ByteArrayBody#ByteArrayBody(byte[], ContentType, String)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public ByteArrayBody(final byte[] data, final String mimeType, final String filename) { + this(data, ContentType.create(mimeType), filename); + } + + /** + * @since 4.3 + */ + public ByteArrayBody(final byte[] data, final ContentType contentType, final String filename) { + super(contentType); + Args.notNull(data, "byte[]"); + this.data = data; + this.filename = filename; + } + + /** + * Creates a new ByteArrayBody. + * + * @param data The contents of the file contained in this part. + * @param filename The name of the file contained in this part. + */ + public ByteArrayBody(final byte[] data, final String filename) { + this(data, "application/octet-stream", filename); + } + + public String getFilename() { + return filename; + } + + public void writeTo(final OutputStream out) throws IOException { + out.write(data); + } + + @Override + public String getCharset() { + return null; + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public long getContentLength() { + return data.length; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentBody.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentBody.java new file mode 100644 index 000000000..d62aaff54 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentBody.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime.content; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @since 4.0 + */ +public interface ContentBody extends ContentDescriptor { + + String getFilename(); + + void writeTo(OutputStream out) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentDescriptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentDescriptor.java new file mode 100644 index 000000000..87237ec23 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/ContentDescriptor.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime.content; + +/** + * Represents common content properties. + */ +public interface ContentDescriptor { + + /** + * Returns the body descriptors MIME type. + * @see #getMediaType() + * @see #getSubType() + * @return The MIME type, which has been parsed from the + * content-type definition. Must not be null, but + * "text/plain", if no content-type was specified. + */ + String getMimeType(); + + /** + * Gets the defaulted MIME media type for this content. + * For example TEXT, IMAGE, MULTIPART + * @see #getMimeType() + * @return the MIME media type when content-type specified, + * otherwise the correct default (TEXT) + */ + String getMediaType(); + + /** + * Gets the defaulted MIME sub type for this content. + * @see #getMimeType() + * @return the MIME media type when content-type is specified, + * otherwise the correct default (PLAIN) + */ + String getSubType(); + + /** + *

    The body descriptors character set, defaulted appropriately for the MIME type.

    + *

    + * For TEXT types, this will be defaulted to us-ascii. + * For other types, when the charset parameter is missing this property will be null. + *

    + * @return Character set, which has been parsed from the + * content-type definition. Not null for TEXT types, when unset will + * be set to default us-ascii. For other types, when unset, + * null will be returned. + */ + String getCharset(); + + /** + * Returns the body descriptors transfer encoding. + * @return The transfer encoding. Must not be null, but "7bit", + * if no transfer-encoding was specified. + */ + String getTransferEncoding(); + + /** + * Returns the body descriptors content-length. + * @return Content length, if known, or -1, to indicate the absence of a + * content-length header. + */ + long getContentLength(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/FileBody.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/FileBody.java new file mode 100644 index 000000000..7641236a4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/FileBody.java @@ -0,0 +1,144 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime.content; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.mime.MIME; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Binary body part backed by a file. + * + * @see ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder + * + * @since 4.0 + */ +public class FileBody extends AbstractContentBody { + + private final File file; + private final String filename; + + /** + * @since 4.1 + * + * @deprecated (4.3) use {@link FileBody#FileBody(File, ContentType, String)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public FileBody(final File file, + final String filename, + final String mimeType, + final String charset) { + this(file, ContentType.create(mimeType, charset), filename); + } + + /** + * @since 4.1 + * + * @deprecated (4.3) use {@link FileBody#FileBody(File, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public FileBody(final File file, + final String mimeType, + final String charset) { + this(file, null, mimeType, charset); + } + + /** + * @deprecated (4.3) use {@link FileBody#FileBody(File, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public FileBody(final File file, final String mimeType) { + this(file, ContentType.create(mimeType), null); + } + + public FileBody(final File file) { + this(file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null); + } + + /** + * @since 4.3 + */ + public FileBody(final File file, final ContentType contentType, final String filename) { + super(contentType); + Args.notNull(file, "File"); + this.file = file; + this.filename = filename; + } + + /** + * @since 4.3 + */ + public FileBody(final File file, final ContentType contentType) { + this(file, contentType, null); + } + + public InputStream getInputStream() throws IOException { + return new FileInputStream(this.file); + } + + public void writeTo(final OutputStream out) throws IOException { + Args.notNull(out, "Output stream"); + final InputStream in = new FileInputStream(this.file); + try { + final byte[] tmp = new byte[4096]; + int l; + while ((l = in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } finally { + in.close(); + } + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public long getContentLength() { + return this.file.length(); + } + + public String getFilename() { + return filename; + } + + public File getFile() { + return this.file; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/InputStreamBody.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/InputStreamBody.java new file mode 100644 index 000000000..267a78ea5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/InputStreamBody.java @@ -0,0 +1,112 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime.content; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.mime.MIME; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Binary body part backed by an input stream. + * + * @see ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder + * + * @since 4.0 + */ +public class InputStreamBody extends AbstractContentBody { + + private final InputStream in; + private final String filename; + + /** + * @since 4.1 + * + * @deprecated (4.3) use {@link InputStreamBody#InputStreamBody(InputStream, ContentType, + * String)} or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public InputStreamBody(final InputStream in, final String mimeType, final String filename) { + this(in, ContentType.create(mimeType), filename); + } + + public InputStreamBody(final InputStream in, final String filename) { + this(in, ContentType.DEFAULT_BINARY, filename); + } + + /** + * @since 4.3 + */ + public InputStreamBody(final InputStream in, final ContentType contentType, final String filename) { + super(contentType); + Args.notNull(in, "Input stream"); + this.in = in; + this.filename = filename; + } + + /** + * @since 4.3 + */ + public InputStreamBody(final InputStream in, final ContentType contentType) { + this(in, contentType, null); + } + + public InputStream getInputStream() { + return this.in; + } + + public void writeTo(final OutputStream out) throws IOException { + Args.notNull(out, "Output stream"); + try { + final byte[] tmp = new byte[4096]; + int l; + while ((l = this.in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } finally { + this.in.close(); + } + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public long getContentLength() { + return -1; + } + + public String getFilename() { + return this.filename; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/StringBody.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/StringBody.java new file mode 100644 index 000000000..c5ad5632d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/StringBody.java @@ -0,0 +1,197 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.entity.mime.content; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.entity.mime.MIME; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Text body part backed by a byte array. + * + * @see ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder + * + * @since 4.0 + */ +public class StringBody extends AbstractContentBody { + + private final byte[] content; + + /** + * @since 4.1 + * + * @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public static StringBody create( + final String text, + final String mimeType, + final Charset charset) throws IllegalArgumentException { + try { + return new StringBody(text, mimeType, charset); + } catch (final UnsupportedEncodingException ex) { + throw new IllegalArgumentException("Charset " + charset + " is not supported", ex); + } + } + + /** + * @since 4.1 + * + * @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public static StringBody create( + final String text, final Charset charset) throws IllegalArgumentException { + return create(text, null, charset); + } + + /** + * @since 4.1 + * + * @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public static StringBody create(final String text) throws IllegalArgumentException { + return create(text, null, null); + } + + /** + * Create a StringBody from the specified text, MIME type and character set. + * + * @param text to be used for the body, not {@code null} + * @param mimeType the MIME type, not {@code null} + * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + * + * @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public StringBody( + final String text, + final String mimeType, + final Charset charset) throws UnsupportedEncodingException { + this(text, ContentType.create(mimeType, charset)); + } + + /** + * Create a StringBody from the specified text and character set. + * The MIME type is set to "text/plain". + * + * @param text to be used for the body, not {@code null} + * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + * + * @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public StringBody(final String text, final Charset charset) throws UnsupportedEncodingException { + this(text, "text/plain", charset); + } + + /** + * Create a StringBody from the specified text. + * The MIME type is set to "text/plain". + * The {@linkplain Consts#ASCII ASCII} charset is used. + * + * @param text to be used for the body, not {@code null} + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + * + * @deprecated (4.3) use {@link StringBody#StringBody(String, ContentType)} + * or {@link ch.boye.httpclientandroidlib.entity.mime.MultipartEntityBuilder} + */ + @Deprecated + public StringBody(final String text) throws UnsupportedEncodingException { + this(text, "text/plain", Consts.ASCII); + } + + /** + * @since 4.3 + */ + public StringBody(final String text, final ContentType contentType) { + super(contentType); + Args.notNull(text, "Text"); + final Charset charset = contentType.getCharset(); + final String csname = charset != null ? charset.name() : Consts.ASCII.name(); + try { + this.content = text.getBytes(csname); + } catch (final UnsupportedEncodingException ex) { + // Should never happen + throw new UnsupportedCharsetException(csname); + } + } + + public Reader getReader() { + final Charset charset = getContentType().getCharset(); + return new InputStreamReader( + new ByteArrayInputStream(this.content), + charset != null ? charset : Consts.ASCII); + } + + public void writeTo(final OutputStream out) throws IOException { + Args.notNull(out, "Output stream"); + final InputStream in = new ByteArrayInputStream(this.content); + final byte[] tmp = new byte[4096]; + int l; + while ((l = in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } + + public String getTransferEncoding() { + return MIME.ENC_8BIT; + } + + public long getContentLength() { + return this.content.length; + } + + public String getFilename() { + return null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/package-info.java new file mode 100644 index 000000000..ff5e2ed0d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/content/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * MIME body part implementations. + */ +package ch.boye.httpclientandroidlib.entity.mime.content; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/package-info.java new file mode 100644 index 000000000..0ef01f9ed --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/mime/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * MIME coded HTTP entity implementations. + */ +package ch.boye.httpclientandroidlib.entity.mime; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/package-info.java new file mode 100644 index 000000000..0027ae98d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/entity/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Core HTTP entity implementations. + */ +package ch.boye.httpclientandroidlib.entity; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpClientConnection.java new file mode 100644 index 000000000..3f3c5f406 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpClientConnection.java @@ -0,0 +1,323 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.net.SocketTimeoutException; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.impl.entity.EntityDeserializer; +import ch.boye.httpclientandroidlib.impl.entity.EntitySerializer; +import ch.boye.httpclientandroidlib.impl.entity.LaxContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.entity.StrictContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpResponseParser; +import ch.boye.httpclientandroidlib.impl.io.HttpRequestWriter; +import ch.boye.httpclientandroidlib.io.EofSensor; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageWriter; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Abstract client-side HTTP connection capable of transmitting and receiving + * data using arbitrary {@link SessionInputBuffer} and + * {@link SessionOutputBuffer} implementations. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultBHttpClientConnection} + */ +@NotThreadSafe +@Deprecated +public abstract class AbstractHttpClientConnection implements HttpClientConnection { + + private final EntitySerializer entityserializer; + private final EntityDeserializer entitydeserializer; + + private SessionInputBuffer inbuffer = null; + private SessionOutputBuffer outbuffer = null; + private EofSensor eofSensor = null; + private HttpMessageParser responseParser = null; + private HttpMessageWriter requestWriter = null; + private HttpConnectionMetricsImpl metrics = null; + + /** + * Creates an instance of this class. + *

    + * This constructor will invoke {@link #createEntityDeserializer()} + * and {@link #createEntitySerializer()} methods in order to initialize + * HTTP entity serializer and deserializer implementations for this + * connection. + */ + public AbstractHttpClientConnection() { + super(); + this.entityserializer = createEntitySerializer(); + this.entitydeserializer = createEntityDeserializer(); + } + + /** + * Asserts if the connection is open. + * + * @throws IllegalStateException if the connection is not open. + */ + protected abstract void assertOpen() throws IllegalStateException; + + /** + * Creates an instance of {@link EntityDeserializer} with the + * {@link LaxContentLengthStrategy} implementation to be used for + * de-serializing entities received over this connection. + *

    + * This method can be overridden in a super class in order to create + * instances of {@link EntityDeserializer} using a custom + * {@link ch.boye.httpclientandroidlib.entity.ContentLengthStrategy}. + * + * @return HTTP entity deserializer + */ + protected EntityDeserializer createEntityDeserializer() { + return new EntityDeserializer(new LaxContentLengthStrategy()); + } + + /** + * Creates an instance of {@link EntitySerializer} with the + * {@link StrictContentLengthStrategy} implementation to be used for + * serializing HTTP entities sent over this connection. + *

    + * This method can be overridden in a super class in order to create + * instances of {@link EntitySerializer} using a custom + * {@link ch.boye.httpclientandroidlib.entity.ContentLengthStrategy}. + * + * @return HTTP entity serialzier. + */ + protected EntitySerializer createEntitySerializer() { + return new EntitySerializer(new StrictContentLengthStrategy()); + } + + /** + * Creates an instance of {@link DefaultHttpResponseFactory} to be used + * for creating {@link HttpResponse} objects received by over this + * connection. + *

    + * This method can be overridden in a super class in order to provide + * a different implementation of the {@link HttpResponseFactory} interface. + * + * @return HTTP response factory. + */ + protected HttpResponseFactory createHttpResponseFactory() { + return DefaultHttpResponseFactory.INSTANCE; + } + + /** + * Creates an instance of {@link HttpMessageParser} to be used for parsing + * HTTP responses received over this connection. + *

    + * This method can be overridden in a super class in order to provide + * a different implementation of the {@link HttpMessageParser} interface or + * to pass a different implementation of the + * {@link ch.boye.httpclientandroidlib.message.LineParser} to the the + * {@link DefaultHttpResponseParser} constructor. + * + * @param buffer the session input buffer. + * @param responseFactory the HTTP response factory. + * @param params HTTP parameters. + * @return HTTP message parser. + */ + protected HttpMessageParser createResponseParser( + final SessionInputBuffer buffer, + final HttpResponseFactory responseFactory, + final HttpParams params) { + return new DefaultHttpResponseParser(buffer, null, responseFactory, params); + } + + /** + * Creates an instance of {@link HttpMessageWriter} to be used for + * writing out HTTP requests sent over this connection. + *

    + * This method can be overridden in a super class in order to provide + * a different implementation of the {@link HttpMessageWriter} interface or + * to pass a different implementation of + * {@link ch.boye.httpclientandroidlib.message.LineFormatter} to the the default implementation + * {@link HttpRequestWriter}. + * + * @param buffer the session output buffer + * @param params HTTP parameters + * @return HTTP message writer + */ + protected HttpMessageWriter createRequestWriter( + final SessionOutputBuffer buffer, + final HttpParams params) { + return new HttpRequestWriter(buffer, null, params); + } + + /** + * @since 4.1 + */ + protected HttpConnectionMetricsImpl createConnectionMetrics( + final HttpTransportMetrics inTransportMetric, + final HttpTransportMetrics outTransportMetric) { + return new HttpConnectionMetricsImpl(inTransportMetric, outTransportMetric); + } + + /** + * Initializes this connection object with {@link SessionInputBuffer} and + * {@link SessionOutputBuffer} instances to be used for sending and + * receiving data. These session buffers can be bound to any arbitrary + * physical output medium. + *

    + * This method will invoke {@link #createHttpResponseFactory()}, + * {@link #createRequestWriter(SessionOutputBuffer, HttpParams)} + * and {@link #createResponseParser(SessionInputBuffer, HttpResponseFactory, HttpParams)} + * methods to initialize HTTP request writer and response parser for this + * connection. + * + * @param inbuffer the session input buffer. + * @param outbuffer the session output buffer. + * @param params HTTP parameters. + */ + protected void init( + final SessionInputBuffer inbuffer, + final SessionOutputBuffer outbuffer, + final HttpParams params) { + this.inbuffer = Args.notNull(inbuffer, "Input session buffer"); + this.outbuffer = Args.notNull(outbuffer, "Output session buffer"); + if (inbuffer instanceof EofSensor) { + this.eofSensor = (EofSensor) inbuffer; + } + this.responseParser = createResponseParser( + inbuffer, + createHttpResponseFactory(), + params); + this.requestWriter = createRequestWriter( + outbuffer, params); + this.metrics = createConnectionMetrics( + inbuffer.getMetrics(), + outbuffer.getMetrics()); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + assertOpen(); + try { + return this.inbuffer.isDataAvailable(timeout); + } catch (final SocketTimeoutException ex) { + return false; + } + } + + public void sendRequestHeader(final HttpRequest request) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + assertOpen(); + this.requestWriter.write(request); + this.metrics.incrementRequestCount(); + } + + public void sendRequestEntity(final HttpEntityEnclosingRequest request) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + assertOpen(); + if (request.getEntity() == null) { + return; + } + this.entityserializer.serialize( + this.outbuffer, + request, + request.getEntity()); + } + + protected void doFlush() throws IOException { + this.outbuffer.flush(); + } + + public void flush() throws IOException { + assertOpen(); + doFlush(); + } + + public HttpResponse receiveResponseHeader() + throws HttpException, IOException { + assertOpen(); + final HttpResponse response = this.responseParser.parse(); + if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_OK) { + this.metrics.incrementResponseCount(); + } + return response; + } + + public void receiveResponseEntity(final HttpResponse response) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + assertOpen(); + final HttpEntity entity = this.entitydeserializer.deserialize(this.inbuffer, response); + response.setEntity(entity); + } + + protected boolean isEof() { + return this.eofSensor != null && this.eofSensor.isEof(); + } + + public boolean isStale() { + if (!isOpen()) { + return true; + } + if (isEof()) { + return true; + } + try { + this.inbuffer.isDataAvailable(1); + return isEof(); + } catch (final SocketTimeoutException ex) { + return false; + } catch (final IOException ex) { + return true; + } + } + + public HttpConnectionMetrics getMetrics() { + return this.metrics; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpServerConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpServerConnection.java new file mode 100644 index 000000000..e9e67e5b1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/AbstractHttpServerConnection.java @@ -0,0 +1,310 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestFactory; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpServerConnection; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.impl.entity.DisallowIdentityContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.entity.EntityDeserializer; +import ch.boye.httpclientandroidlib.impl.entity.EntitySerializer; +import ch.boye.httpclientandroidlib.impl.entity.LaxContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.entity.StrictContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpRequestParser; +import ch.boye.httpclientandroidlib.impl.io.HttpResponseWriter; +import ch.boye.httpclientandroidlib.io.EofSensor; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageWriter; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Abstract server-side HTTP connection capable of transmitting and receiving + * data using arbitrary {@link SessionInputBuffer} and + * {@link SessionOutputBuffer} implementations. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultBHttpServerConnection} + */ +@NotThreadSafe +@Deprecated +public abstract class AbstractHttpServerConnection implements HttpServerConnection { + + private final EntitySerializer entityserializer; + private final EntityDeserializer entitydeserializer; + + private SessionInputBuffer inbuffer = null; + private SessionOutputBuffer outbuffer = null; + private EofSensor eofSensor = null; + private HttpMessageParser requestParser = null; + private HttpMessageWriter responseWriter = null; + private HttpConnectionMetricsImpl metrics = null; + + /** + * Creates an instance of this class. + *

    + * This constructor will invoke {@link #createEntityDeserializer()} + * and {@link #createEntitySerializer()} methods in order to initialize + * HTTP entity serializer and deserializer implementations for this + * connection. + */ + public AbstractHttpServerConnection() { + super(); + this.entityserializer = createEntitySerializer(); + this.entitydeserializer = createEntityDeserializer(); + } + + /** + * Asserts if the connection is open. + * + * @throws IllegalStateException if the connection is not open. + */ + protected abstract void assertOpen() throws IllegalStateException; + + /** + * Creates an instance of {@link EntityDeserializer} with the + * {@link LaxContentLengthStrategy} implementation to be used for + * de-serializing entities received over this connection. + *

    + * This method can be overridden in a super class in order to create + * instances of {@link EntityDeserializer} using a custom + * {@link ch.boye.httpclientandroidlib.entity.ContentLengthStrategy}. + * + * @return HTTP entity deserializer + */ + protected EntityDeserializer createEntityDeserializer() { + return new EntityDeserializer(new DisallowIdentityContentLengthStrategy( + new LaxContentLengthStrategy(0))); + } + + /** + * Creates an instance of {@link EntitySerializer} with the + * {@link StrictContentLengthStrategy} implementation to be used for + * serializing HTTP entities sent over this connection. + *

    + * This method can be overridden in a super class in order to create + * instances of {@link EntitySerializer} using a custom + * {@link ch.boye.httpclientandroidlib.entity.ContentLengthStrategy}. + * + * @return HTTP entity serialzier. + */ + protected EntitySerializer createEntitySerializer() { + return new EntitySerializer(new StrictContentLengthStrategy()); + } + + /** + * Creates an instance of {@link DefaultHttpRequestFactory} to be used + * for creating {@link HttpRequest} objects received by over this + * connection. + *

    + * This method can be overridden in a super class in order to provide + * a different implementation of the {@link HttpRequestFactory} interface. + * + * @return HTTP request factory. + */ + protected HttpRequestFactory createHttpRequestFactory() { + return DefaultHttpRequestFactory.INSTANCE; + } + + /** + * Creates an instance of {@link HttpMessageParser} to be used for parsing + * HTTP requests received over this connection. + *

    + * This method can be overridden in a super class in order to provide + * a different implementation of the {@link HttpMessageParser} interface or + * to pass a different implementation of the + * {@link ch.boye.httpclientandroidlib.message.LineParser} to the + * {@link DefaultHttpRequestParser} constructor. + * + * @param buffer the session input buffer. + * @param requestFactory the HTTP request factory. + * @param params HTTP parameters. + * @return HTTP message parser. + */ + protected HttpMessageParser createRequestParser( + final SessionInputBuffer buffer, + final HttpRequestFactory requestFactory, + final HttpParams params) { + return new DefaultHttpRequestParser(buffer, null, requestFactory, params); + } + + /** + * Creates an instance of {@link HttpMessageWriter} to be used for + * writing out HTTP responses sent over this connection. + *

    + * This method can be overridden in a super class in order to provide + * a different implementation of the {@link HttpMessageWriter} interface or + * to pass a different implementation of + * {@link ch.boye.httpclientandroidlib.message.LineFormatter} to the the default + * implementation {@link HttpResponseWriter}. + * + * @param buffer the session output buffer + * @param params HTTP parameters + * @return HTTP message writer + */ + protected HttpMessageWriter createResponseWriter( + final SessionOutputBuffer buffer, + final HttpParams params) { + return new HttpResponseWriter(buffer, null, params); + } + + /** + * @since 4.1 + */ + protected HttpConnectionMetricsImpl createConnectionMetrics( + final HttpTransportMetrics inTransportMetric, + final HttpTransportMetrics outTransportMetric) { + return new HttpConnectionMetricsImpl(inTransportMetric, outTransportMetric); + } + + /** + * Initializes this connection object with {@link SessionInputBuffer} and + * {@link SessionOutputBuffer} instances to be used for sending and + * receiving data. These session buffers can be bound to any arbitrary + * physical output medium. + *

    + * This method will invoke {@link #createHttpRequestFactory}, + * {@link #createRequestParser(SessionInputBuffer, HttpRequestFactory, HttpParams)} + * and {@link #createResponseWriter(SessionOutputBuffer, HttpParams)} + * methods to initialize HTTP request parser and response writer for this + * connection. + * + * @param inbuffer the session input buffer. + * @param outbuffer the session output buffer. + * @param params HTTP parameters. + */ + protected void init( + final SessionInputBuffer inbuffer, + final SessionOutputBuffer outbuffer, + final HttpParams params) { + this.inbuffer = Args.notNull(inbuffer, "Input session buffer"); + this.outbuffer = Args.notNull(outbuffer, "Output session buffer"); + if (inbuffer instanceof EofSensor) { + this.eofSensor = (EofSensor) inbuffer; + } + this.requestParser = createRequestParser( + inbuffer, + createHttpRequestFactory(), + params); + this.responseWriter = createResponseWriter( + outbuffer, params); + this.metrics = createConnectionMetrics( + inbuffer.getMetrics(), + outbuffer.getMetrics()); + } + + public HttpRequest receiveRequestHeader() + throws HttpException, IOException { + assertOpen(); + final HttpRequest request = this.requestParser.parse(); + this.metrics.incrementRequestCount(); + return request; + } + + public void receiveRequestEntity(final HttpEntityEnclosingRequest request) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + assertOpen(); + final HttpEntity entity = this.entitydeserializer.deserialize(this.inbuffer, request); + request.setEntity(entity); + } + + protected void doFlush() throws IOException { + this.outbuffer.flush(); + } + + public void flush() throws IOException { + assertOpen(); + doFlush(); + } + + public void sendResponseHeader(final HttpResponse response) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + assertOpen(); + this.responseWriter.write(response); + if (response.getStatusLine().getStatusCode() >= 200) { + this.metrics.incrementResponseCount(); + } + } + + public void sendResponseEntity(final HttpResponse response) + throws HttpException, IOException { + if (response.getEntity() == null) { + return; + } + this.entityserializer.serialize( + this.outbuffer, + response, + response.getEntity()); + } + + protected boolean isEof() { + return this.eofSensor != null && this.eofSensor.isEof(); + } + + public boolean isStale() { + if (!isOpen()) { + return true; + } + if (isEof()) { + return true; + } + try { + this.inbuffer.isDataAvailable(1); + return isEof(); + } catch (final IOException ex) { + return true; + } + } + + public HttpConnectionMetrics getMetrics() { + return this.metrics; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/BHttpConnectionBase.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/BHttpConnectionBase.java new file mode 100644 index 000000000..2215aa6e2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/BHttpConnectionBase.java @@ -0,0 +1,393 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpConnection; +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpInetConnection; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.entity.BasicHttpEntity; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.entity.LaxContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.entity.StrictContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.io.ChunkedInputStream; +import ch.boye.httpclientandroidlib.impl.io.ChunkedOutputStream; +import ch.boye.httpclientandroidlib.impl.io.ContentLengthInputStream; +import ch.boye.httpclientandroidlib.impl.io.ContentLengthOutputStream; +import ch.boye.httpclientandroidlib.impl.io.HttpTransportMetricsImpl; +import ch.boye.httpclientandroidlib.impl.io.IdentityInputStream; +import ch.boye.httpclientandroidlib.impl.io.IdentityOutputStream; +import ch.boye.httpclientandroidlib.impl.io.SessionInputBufferImpl; +import ch.boye.httpclientandroidlib.impl.io.SessionOutputBufferImpl; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.NetUtils; + +/** + * This class serves as a base for all {@link HttpConnection} implementations and provides + * functionality common to both client and server HTTP connections. + * + * @since 4.0 + */ +@NotThreadSafe +public class BHttpConnectionBase implements HttpConnection, HttpInetConnection { + + private final SessionInputBufferImpl inbuffer; + private final SessionOutputBufferImpl outbuffer; + private final HttpConnectionMetricsImpl connMetrics; + private final ContentLengthStrategy incomingContentStrategy; + private final ContentLengthStrategy outgoingContentStrategy; + + private volatile boolean open; + private volatile Socket socket; + + /** + * Creates new instance of BHttpConnectionBase. + * + * @param buffersize buffer size. Must be a positive number. + * @param fragmentSizeHint fragment size hint. + * @param chardecoder decoder to be used for decoding HTTP protocol elements. + * If null simple type cast will be used for byte to char conversion. + * @param charencoder encoder to be used for encoding HTTP protocol elements. + * If null simple type cast will be used for char to byte conversion. + * @param constraints Message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * @param incomingContentStrategy incoming content length strategy. If null + * {@link LaxContentLengthStrategy#INSTANCE} will be used. + * @param outgoingContentStrategy outgoing content length strategy. If null + * {@link StrictContentLengthStrategy#INSTANCE} will be used. + */ + protected BHttpConnectionBase( + final int buffersize, + final int fragmentSizeHint, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy) { + super(); + Args.positive(buffersize, "Buffer size"); + final HttpTransportMetricsImpl inTransportMetrics = new HttpTransportMetricsImpl(); + final HttpTransportMetricsImpl outTransportMetrics = new HttpTransportMetricsImpl(); + this.inbuffer = new SessionInputBufferImpl(inTransportMetrics, buffersize, -1, + constraints != null ? constraints : MessageConstraints.DEFAULT, chardecoder); + this.outbuffer = new SessionOutputBufferImpl(outTransportMetrics, buffersize, fragmentSizeHint, + charencoder); + this.connMetrics = new HttpConnectionMetricsImpl(inTransportMetrics, outTransportMetrics); + this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : + LaxContentLengthStrategy.INSTANCE; + this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : + StrictContentLengthStrategy.INSTANCE; + } + + protected void ensureOpen() throws IOException { + Asserts.check(this.open, "Connection is not open"); + if (!this.inbuffer.isBound()) { + this.inbuffer.bind(getSocketInputStream(this.socket)); + } + if (!this.outbuffer.isBound()) { + this.outbuffer.bind(getSocketOutputStream(this.socket)); + } + } + + protected InputStream getSocketInputStream(final Socket socket) throws IOException { + return socket.getInputStream(); + } + + protected OutputStream getSocketOutputStream(final Socket socket) throws IOException { + return socket.getOutputStream(); + } + + /** + * Binds this connection to the given {@link Socket}. This socket will be + * used by the connection to send and receive data. + *

    + * After this method's execution the connection status will be reported + * as open and the {@link #isOpen()} will return true. + * + * @param socket the socket. + * @throws IOException in case of an I/O error. + */ + protected void bind(final Socket socket) throws IOException { + Args.notNull(socket, "Socket"); + this.socket = socket; + this.open = true; + this.inbuffer.bind(null); + this.outbuffer.bind(null); + } + + protected SessionInputBuffer getSessionInputBuffer() { + return this.inbuffer; + } + + protected SessionOutputBuffer getSessionOutputBuffer() { + return this.outbuffer; + } + + protected void doFlush() throws IOException { + this.outbuffer.flush(); + } + + public boolean isOpen() { + return this.open; + } + + protected Socket getSocket() { + return this.socket; + } + + protected OutputStream createOutputStream( + final long len, + final SessionOutputBuffer outbuffer) { + if (len == ContentLengthStrategy.CHUNKED) { + return new ChunkedOutputStream(2048, outbuffer); + } else if (len == ContentLengthStrategy.IDENTITY) { + return new IdentityOutputStream(outbuffer); + } else { + return new ContentLengthOutputStream(outbuffer, len); + } + } + + protected OutputStream prepareOutput(final HttpMessage message) throws HttpException { + final long len = this.outgoingContentStrategy.determineLength(message); + return createOutputStream(len, this.outbuffer); + } + + protected InputStream createInputStream( + final long len, + final SessionInputBuffer inbuffer) { + if (len == ContentLengthStrategy.CHUNKED) { + return new ChunkedInputStream(inbuffer); + } else if (len == ContentLengthStrategy.IDENTITY) { + return new IdentityInputStream(inbuffer); + } else { + return new ContentLengthInputStream(inbuffer, len); + } + } + + protected HttpEntity prepareInput(final HttpMessage message) throws HttpException { + final BasicHttpEntity entity = new BasicHttpEntity(); + + final long len = this.incomingContentStrategy.determineLength(message); + final InputStream instream = createInputStream(len, this.inbuffer); + if (len == ContentLengthStrategy.CHUNKED) { + entity.setChunked(true); + entity.setContentLength(-1); + entity.setContent(instream); + } else if (len == ContentLengthStrategy.IDENTITY) { + entity.setChunked(false); + entity.setContentLength(-1); + entity.setContent(instream); + } else { + entity.setChunked(false); + entity.setContentLength(len); + entity.setContent(instream); + } + + final Header contentTypeHeader = message.getFirstHeader(HTTP.CONTENT_TYPE); + if (contentTypeHeader != null) { + entity.setContentType(contentTypeHeader); + } + final Header contentEncodingHeader = message.getFirstHeader(HTTP.CONTENT_ENCODING); + if (contentEncodingHeader != null) { + entity.setContentEncoding(contentEncodingHeader); + } + return entity; + } + + public InetAddress getLocalAddress() { + if (this.socket != null) { + return this.socket.getLocalAddress(); + } else { + return null; + } + } + + public int getLocalPort() { + if (this.socket != null) { + return this.socket.getLocalPort(); + } else { + return -1; + } + } + + public InetAddress getRemoteAddress() { + if (this.socket != null) { + return this.socket.getInetAddress(); + } else { + return null; + } + } + + public int getRemotePort() { + if (this.socket != null) { + return this.socket.getPort(); + } else { + return -1; + } + } + + public void setSocketTimeout(final int timeout) { + if (this.socket != null) { + try { + this.socket.setSoTimeout(timeout); + } catch (final SocketException ignore) { + // It is not quite clear from the Sun's documentation if there are any + // other legitimate cases for a socket exception to be thrown when setting + // SO_TIMEOUT besides the socket being already closed + } + } + } + + public int getSocketTimeout() { + if (this.socket != null) { + try { + return this.socket.getSoTimeout(); + } catch (final SocketException ignore) { + return -1; + } + } else { + return -1; + } + } + + public void shutdown() throws IOException { + this.open = false; + final Socket tmpsocket = this.socket; + if (tmpsocket != null) { + tmpsocket.close(); + } + } + + public void close() throws IOException { + if (!this.open) { + return; + } + this.open = false; + final Socket sock = this.socket; + try { + this.inbuffer.clear(); + this.outbuffer.flush(); + try { + try { + sock.shutdownOutput(); + } catch (final IOException ignore) { + } + try { + sock.shutdownInput(); + } catch (final IOException ignore) { + } + } catch (final UnsupportedOperationException ignore) { + // if one isn't supported, the other one isn't either + } + } finally { + sock.close(); + } + } + + private int fillInputBuffer(final int timeout) throws IOException { + final int oldtimeout = this.socket.getSoTimeout(); + try { + this.socket.setSoTimeout(timeout); + return this.inbuffer.fillBuffer(); + } finally { + this.socket.setSoTimeout(oldtimeout); + } + } + + protected boolean awaitInput(final int timeout) throws IOException { + if (this.inbuffer.hasBufferedData()) { + return true; + } + fillInputBuffer(timeout); + return this.inbuffer.hasBufferedData(); + } + + public boolean isStale() { + if (!isOpen()) { + return true; + } + try { + final int bytesRead = fillInputBuffer(1); + return bytesRead < 0; + } catch (final SocketTimeoutException ex) { + return false; + } catch (final IOException ex) { + return true; + } + } + + protected void incrementRequestCount() { + this.connMetrics.incrementRequestCount(); + } + + protected void incrementResponseCount() { + this.connMetrics.incrementResponseCount(); + } + + public HttpConnectionMetrics getMetrics() { + return this.connMetrics; + } + + @Override + public String toString() { + if (this.socket != null) { + final StringBuilder buffer = new StringBuilder(); + final SocketAddress remoteAddress = this.socket.getRemoteSocketAddress(); + final SocketAddress localAddress = this.socket.getLocalSocketAddress(); + if (remoteAddress != null && localAddress != null) { + NetUtils.formatAddress(buffer, localAddress); + buffer.append("<->"); + NetUtils.formatAddress(buffer, remoteAddress); + } + return buffer.toString(); + } else { + return "[Not bound]"; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/ConnSupport.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/ConnSupport.java new file mode 100644 index 000000000..ba82a2a7c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/ConnSupport.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl; + +import ch.boye.httpclientandroidlib.config.ConnectionConfig; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; + +/** + * Connection support methods. + * + * @since 4.3 + */ +public final class ConnSupport { + + public static CharsetDecoder createDecoder(final ConnectionConfig cconfig) { + if (cconfig == null) { + return null; + } + final Charset charset = cconfig.getCharset(); + final CodingErrorAction malformed = cconfig.getMalformedInputAction(); + final CodingErrorAction unmappable = cconfig.getUnmappableInputAction(); + if (charset != null) { + return charset.newDecoder() + .onMalformedInput(malformed != null ? malformed : CodingErrorAction.REPORT) + .onUnmappableCharacter(unmappable != null ? unmappable: CodingErrorAction.REPORT); + } else { + return null; + } + } + + public static CharsetEncoder createEncoder(final ConnectionConfig cconfig) { + if (cconfig == null) { + return null; + } + final Charset charset = cconfig.getCharset(); + if (charset != null) { + final CodingErrorAction malformed = cconfig.getMalformedInputAction(); + final CodingErrorAction unmappable = cconfig.getUnmappableInputAction(); + return charset.newEncoder() + .onMalformedInput(malformed != null ? malformed : CodingErrorAction.REPORT) + .onUnmappableCharacter(unmappable != null ? unmappable: CodingErrorAction.REPORT); + } else { + return null; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnection.java new file mode 100644 index 000000000..9627e53f2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnection.java @@ -0,0 +1,182 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpRequestWriterFactory; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpResponseParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriter; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of {@link HttpClientConnection}. + * + * @since 4.3 + */ +@NotThreadSafe +public class DefaultBHttpClientConnection extends BHttpConnectionBase + implements HttpClientConnection { + + private final HttpMessageParser responseParser; + private final HttpMessageWriter requestWriter; + + /** + * Creates new instance of DefaultBHttpClientConnection. + * + * @param buffersize buffer size. Must be a positive number. + * @param fragmentSizeHint fragment size hint. + * @param chardecoder decoder to be used for decoding HTTP protocol elements. + * If null simple type cast will be used for byte to char conversion. + * @param charencoder encoder to be used for encoding HTTP protocol elements. + * If null simple type cast will be used for char to byte conversion. + * @param constraints Message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * @param incomingContentStrategy incoming content length strategy. If null + * {@link ch.boye.httpclientandroidlib.impl.entity.LaxContentLengthStrategy#INSTANCE} will be used. + * @param outgoingContentStrategy outgoing content length strategy. If null + * {@link ch.boye.httpclientandroidlib.impl.entity.StrictContentLengthStrategy#INSTANCE} will be used. + * @param requestWriterFactory request writer factory. If null + * {@link DefaultHttpRequestWriterFactory#INSTANCE} will be used. + * @param responseParserFactory response parser factory. If null + * {@link DefaultHttpResponseParserFactory#INSTANCE} will be used. + */ + public DefaultBHttpClientConnection( + final int buffersize, + final int fragmentSizeHint, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageWriterFactory requestWriterFactory, + final HttpMessageParserFactory responseParserFactory) { + super(buffersize, fragmentSizeHint, chardecoder, charencoder, + constraints, incomingContentStrategy, outgoingContentStrategy); + this.requestWriter = (requestWriterFactory != null ? requestWriterFactory : + DefaultHttpRequestWriterFactory.INSTANCE).create(getSessionOutputBuffer()); + this.responseParser = (responseParserFactory != null ? responseParserFactory : + DefaultHttpResponseParserFactory.INSTANCE).create(getSessionInputBuffer(), constraints); + } + + public DefaultBHttpClientConnection( + final int buffersize, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints) { + this(buffersize, buffersize, chardecoder, charencoder, constraints, null, null, null, null); + } + + public DefaultBHttpClientConnection(final int buffersize) { + this(buffersize, buffersize, null, null, null, null, null, null, null); + } + + protected void onResponseReceived(final HttpResponse response) { + } + + protected void onRequestSubmitted(final HttpRequest request) { + } + + @Override + public void bind(final Socket socket) throws IOException { + super.bind(socket); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + ensureOpen(); + try { + return awaitInput(timeout); + } catch (final SocketTimeoutException ex) { + return false; + } + } + + public void sendRequestHeader(final HttpRequest request) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + ensureOpen(); + this.requestWriter.write(request); + onRequestSubmitted(request); + incrementRequestCount(); + } + + public void sendRequestEntity(final HttpEntityEnclosingRequest request) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + ensureOpen(); + final HttpEntity entity = request.getEntity(); + if (entity == null) { + return; + } + final OutputStream outstream = prepareOutput(request); + entity.writeTo(outstream); + outstream.close(); + } + + public HttpResponse receiveResponseHeader() throws HttpException, IOException { + ensureOpen(); + final HttpResponse response = this.responseParser.parse(); + onResponseReceived(response); + if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_OK) { + incrementResponseCount(); + } + return response; + } + + public void receiveResponseEntity( + final HttpResponse response) throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + ensureOpen(); + final HttpEntity entity = prepareInput(response); + response.setEntity(entity); + } + + public void flush() throws IOException { + ensureOpen(); + doFlush(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnectionFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnectionFactory.java new file mode 100644 index 000000000..f337339c3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpClientConnectionFactory.java @@ -0,0 +1,103 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import ch.boye.httpclientandroidlib.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; + +import java.io.IOException; +import java.net.Socket; + +/** + * Default factory for {@link ch.boye.httpclientandroidlib.HttpClientConnection}s. + * + * @since 4.3 + */ +@Immutable +public class DefaultBHttpClientConnectionFactory + implements HttpConnectionFactory { + + public static final DefaultBHttpClientConnectionFactory INSTANCE = new DefaultBHttpClientConnectionFactory(); + + private final ConnectionConfig cconfig; + private final ContentLengthStrategy incomingContentStrategy; + private final ContentLengthStrategy outgoingContentStrategy; + private final HttpMessageWriterFactory requestWriterFactory; + private final HttpMessageParserFactory responseParserFactory; + + public DefaultBHttpClientConnectionFactory( + final ConnectionConfig cconfig, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageWriterFactory requestWriterFactory, + final HttpMessageParserFactory responseParserFactory) { + super(); + this.cconfig = cconfig != null ? cconfig : ConnectionConfig.DEFAULT; + this.incomingContentStrategy = incomingContentStrategy; + this.outgoingContentStrategy = outgoingContentStrategy; + this.requestWriterFactory = requestWriterFactory; + this.responseParserFactory = responseParserFactory; + } + + public DefaultBHttpClientConnectionFactory( + final ConnectionConfig cconfig, + final HttpMessageWriterFactory requestWriterFactory, + final HttpMessageParserFactory responseParserFactory) { + this(cconfig, null, null, requestWriterFactory, responseParserFactory); + } + + public DefaultBHttpClientConnectionFactory(final ConnectionConfig cconfig) { + this(cconfig, null, null, null, null); + } + + public DefaultBHttpClientConnectionFactory() { + this(null, null, null, null, null); + } + + public DefaultBHttpClientConnection createConnection(final Socket socket) throws IOException { + final DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection( + this.cconfig.getBufferSize(), + this.cconfig.getFragmentSizeHint(), + ConnSupport.createDecoder(this.cconfig), + ConnSupport.createEncoder(this.cconfig), + this.cconfig.getMessageConstraints(), + this.incomingContentStrategy, + this.outgoingContentStrategy, + this.requestWriterFactory, + this.responseParserFactory); + conn.bind(socket); + return conn; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnection.java new file mode 100644 index 000000000..6ee4fb740 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnection.java @@ -0,0 +1,174 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpServerConnection; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.entity.DisallowIdentityContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpRequestParserFactory; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpResponseWriterFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriter; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of {@link HttpServerConnection}. + * + * @since 4.3 + */ +@NotThreadSafe +public class DefaultBHttpServerConnection extends BHttpConnectionBase + implements HttpServerConnection { + + private final HttpMessageParser requestParser; + private final HttpMessageWriter responseWriter; + + /** + * Creates new instance of DefaultBHttpServerConnection. + * + * @param buffersize buffer size. Must be a positive number. + * @param fragmentSizeHint fragment size hint. + * @param chardecoder decoder to be used for decoding HTTP protocol elements. + * If null simple type cast will be used for byte to char conversion. + * @param charencoder encoder to be used for encoding HTTP protocol elements. + * If null simple type cast will be used for char to byte conversion. + * @param constraints Message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * @param incomingContentStrategy incoming content length strategy. If null + * {@link DisallowIdentityContentLengthStrategy#INSTANCE} will be used. + * @param outgoingContentStrategy outgoing content length strategy. If null + * {@link ch.boye.httpclientandroidlib.impl.entity.StrictContentLengthStrategy#INSTANCE} will be used. + * @param requestParserFactory request parser factory. If null + * {@link DefaultHttpRequestParserFactory#INSTANCE} will be used. + * @param responseWriterFactory response writer factory. If null + * {@link DefaultHttpResponseWriterFactory#INSTANCE} will be used. + */ + public DefaultBHttpServerConnection( + final int buffersize, + final int fragmentSizeHint, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageParserFactory requestParserFactory, + final HttpMessageWriterFactory responseWriterFactory) { + super(buffersize, fragmentSizeHint, chardecoder, charencoder, constraints, + incomingContentStrategy != null ? incomingContentStrategy : + DisallowIdentityContentLengthStrategy.INSTANCE, outgoingContentStrategy); + this.requestParser = (requestParserFactory != null ? requestParserFactory : + DefaultHttpRequestParserFactory.INSTANCE).create(getSessionInputBuffer(), constraints); + this.responseWriter = (responseWriterFactory != null ? responseWriterFactory : + DefaultHttpResponseWriterFactory.INSTANCE).create(getSessionOutputBuffer()); + } + + public DefaultBHttpServerConnection( + final int buffersize, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints) { + this(buffersize, buffersize, chardecoder, charencoder, constraints, null, null, null, null); + } + + public DefaultBHttpServerConnection(final int buffersize) { + this(buffersize, buffersize, null, null, null, null, null, null, null); + } + + protected void onRequestReceived(final HttpRequest request) { + } + + protected void onResponseSubmitted(final HttpResponse response) { + } + + @Override + public void bind(final Socket socket) throws IOException { + super.bind(socket); + } + + public HttpRequest receiveRequestHeader() + throws HttpException, IOException { + ensureOpen(); + final HttpRequest request = this.requestParser.parse(); + onRequestReceived(request); + incrementRequestCount(); + return request; + } + + public void receiveRequestEntity(final HttpEntityEnclosingRequest request) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + ensureOpen(); + final HttpEntity entity = prepareInput(request); + request.setEntity(entity); + } + + public void sendResponseHeader(final HttpResponse response) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + ensureOpen(); + this.responseWriter.write(response); + onResponseSubmitted(response); + if (response.getStatusLine().getStatusCode() >= 200) { + incrementResponseCount(); + } + } + + public void sendResponseEntity(final HttpResponse response) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + ensureOpen(); + final HttpEntity entity = response.getEntity(); + if (entity == null) { + return; + } + final OutputStream outstream = prepareOutput(response); + entity.writeTo(outstream); + outstream.close(); + } + + public void flush() throws IOException { + ensureOpen(); + doFlush(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnectionFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnectionFactory.java new file mode 100644 index 000000000..a312320aa --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultBHttpServerConnectionFactory.java @@ -0,0 +1,103 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import ch.boye.httpclientandroidlib.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; + +import java.io.IOException; +import java.net.Socket; + +/** + * Default factory for {@link ch.boye.httpclientandroidlib.HttpServerConnection}s. + * + * @since 4.3 + */ +@Immutable +public class DefaultBHttpServerConnectionFactory + implements HttpConnectionFactory { + + public static final DefaultBHttpServerConnectionFactory INSTANCE = new DefaultBHttpServerConnectionFactory(); + + private final ConnectionConfig cconfig; + private final ContentLengthStrategy incomingContentStrategy; + private final ContentLengthStrategy outgoingContentStrategy; + private final HttpMessageParserFactory requestParserFactory; + private final HttpMessageWriterFactory responseWriterFactory; + + public DefaultBHttpServerConnectionFactory( + final ConnectionConfig cconfig, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageParserFactory requestParserFactory, + final HttpMessageWriterFactory responseWriterFactory) { + super(); + this.cconfig = cconfig != null ? cconfig : ConnectionConfig.DEFAULT; + this.incomingContentStrategy = incomingContentStrategy; + this.outgoingContentStrategy = outgoingContentStrategy; + this.requestParserFactory = requestParserFactory; + this.responseWriterFactory = responseWriterFactory; + } + + public DefaultBHttpServerConnectionFactory( + final ConnectionConfig cconfig, + final HttpMessageParserFactory requestParserFactory, + final HttpMessageWriterFactory responseWriterFactory) { + this(cconfig, null, null, requestParserFactory, responseWriterFactory); + } + + public DefaultBHttpServerConnectionFactory(final ConnectionConfig cconfig) { + this(cconfig, null, null, null, null); + } + + public DefaultBHttpServerConnectionFactory() { + this(null, null, null, null, null); + } + + public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException { + final DefaultBHttpServerConnection conn = new DefaultBHttpServerConnection( + this.cconfig.getBufferSize(), + this.cconfig.getFragmentSizeHint(), + ConnSupport.createDecoder(this.cconfig), + ConnSupport.createEncoder(this.cconfig), + this.cconfig.getMessageConstraints(), + this.incomingContentStrategy, + this.outgoingContentStrategy, + this.requestParserFactory, + this.responseWriterFactory); + conn.bind(socket); + return conn; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultConnectionReuseStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultConnectionReuseStrategy.java new file mode 100644 index 000000000..b58127e5f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultConnectionReuseStrategy.java @@ -0,0 +1,189 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.TokenIterator; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.message.BasicTokenIterator; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of a strategy deciding about connection re-use. + * The default implementation first checks some basics, for example + * whether the connection is still open or whether the end of the + * request entity can be determined without closing the connection. + * If these checks pass, the tokens in the Connection header will + * be examined. In the absence of a Connection header, the + * non-standard but commonly used Proxy-Connection header takes + * it's role. A token close indicates that the connection cannot + * be reused. If there is no such token, a token keep-alive + * indicates that the connection should be re-used. If neither token is found, + * or if there are no Connection headers, the default policy for + * the HTTP version is applied. Since HTTP/1.1, connections are + * re-used by default. Up until HTTP/1.0, connections are not + * re-used by default. + * + * @since 4.0 + */ +@Immutable +public class DefaultConnectionReuseStrategy implements ConnectionReuseStrategy { + + public static final DefaultConnectionReuseStrategy INSTANCE = new DefaultConnectionReuseStrategy(); + + public DefaultConnectionReuseStrategy() { + super(); + } + + // see interface ConnectionReuseStrategy + public boolean keepAlive(final HttpResponse response, + final HttpContext context) { + Args.notNull(response, "HTTP response"); + Args.notNull(context, "HTTP context"); + + // Check for a self-terminating entity. If the end of the entity will + // be indicated by closing the connection, there is no keep-alive. + final ProtocolVersion ver = response.getStatusLine().getProtocolVersion(); + final Header teh = response.getFirstHeader(HTTP.TRANSFER_ENCODING); + if (teh != null) { + if (!HTTP.CHUNK_CODING.equalsIgnoreCase(teh.getValue())) { + return false; + } + } else { + if (canResponseHaveBody(response)) { + final Header[] clhs = response.getHeaders(HTTP.CONTENT_LEN); + // Do not reuse if not properly content-length delimited + if (clhs.length == 1) { + final Header clh = clhs[0]; + try { + final int contentLen = Integer.parseInt(clh.getValue()); + if (contentLen < 0) { + return false; + } + } catch (final NumberFormatException ex) { + return false; + } + } else { + return false; + } + } + } + + // Check for the "Connection" header. If that is absent, check for + // the "Proxy-Connection" header. The latter is an unspecified and + // broken but unfortunately common extension of HTTP. + HeaderIterator hit = response.headerIterator(HTTP.CONN_DIRECTIVE); + if (!hit.hasNext()) { + hit = response.headerIterator("Proxy-Connection"); + } + + // Experimental usage of the "Connection" header in HTTP/1.0 is + // documented in RFC 2068, section 19.7.1. A token "keep-alive" is + // used to indicate that the connection should be persistent. + // Note that the final specification of HTTP/1.1 in RFC 2616 does not + // include this information. Neither is the "Connection" header + // mentioned in RFC 1945, which informally describes HTTP/1.0. + // + // RFC 2616 specifies "close" as the only connection token with a + // specific meaning: it disables persistent connections. + // + // The "Proxy-Connection" header is not formally specified anywhere, + // but is commonly used to carry one token, "close" or "keep-alive". + // The "Connection" header, on the other hand, is defined as a + // sequence of tokens, where each token is a header name, and the + // token "close" has the above-mentioned additional meaning. + // + // To get through this mess, we treat the "Proxy-Connection" header + // in exactly the same way as the "Connection" header, but only if + // the latter is missing. We scan the sequence of tokens for both + // "close" and "keep-alive". As "close" is specified by RFC 2068, + // it takes precedence and indicates a non-persistent connection. + // If there is no "close" but a "keep-alive", we take the hint. + + if (hit.hasNext()) { + try { + final TokenIterator ti = createTokenIterator(hit); + boolean keepalive = false; + while (ti.hasNext()) { + final String token = ti.nextToken(); + if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) { + return false; + } else if (HTTP.CONN_KEEP_ALIVE.equalsIgnoreCase(token)) { + // continue the loop, there may be a "close" afterwards + keepalive = true; + } + } + if (keepalive) + { + return true; + // neither "close" nor "keep-alive", use default policy + } + + } catch (final ParseException px) { + // invalid connection header means no persistent connection + // we don't have logging in HttpCore, so the exception is lost + return false; + } + } + + // default since HTTP/1.1 is persistent, before it was non-persistent + return !ver.lessEquals(HttpVersion.HTTP_1_0); + } + + + /** + * Creates a token iterator from a header iterator. + * This method can be overridden to replace the implementation of + * the token iterator. + * + * @param hit the header iterator + * + * @return the token iterator + */ + protected TokenIterator createTokenIterator(final HeaderIterator hit) { + return new BasicTokenIterator(hit); + } + + private boolean canResponseHaveBody(final HttpResponse response) { + final int status = response.getStatusLine().getStatusCode(); + return status >= HttpStatus.SC_OK + && status != HttpStatus.SC_NO_CONTENT + && status != HttpStatus.SC_NOT_MODIFIED + && status != HttpStatus.SC_RESET_CONTENT; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpClientConnection.java new file mode 100644 index 000000000..adb597863 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpClientConnection.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of a client-side HTTP connection. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultBHttpClientConnection} + */ +@NotThreadSafe +@Deprecated +public class DefaultHttpClientConnection extends SocketHttpClientConnection { + + public DefaultHttpClientConnection() { + super(); + } + + @Override + public void bind( + final Socket socket, + final HttpParams params) throws IOException { + Args.notNull(socket, "Socket"); + Args.notNull(params, "HTTP parameters"); + assertNotOpen(); + socket.setTcpNoDelay(params.getBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)); + socket.setSoTimeout(params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0)); + socket.setKeepAlive(params.getBooleanParameter(CoreConnectionPNames.SO_KEEPALIVE, false)); + final int linger = params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1); + if (linger >= 0) { + socket.setSoLinger(linger > 0, linger); + } + super.bind(socket, params); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpRequestFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpRequestFactory.java new file mode 100644 index 000000000..0491e94bd --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpRequestFactory.java @@ -0,0 +1,109 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestFactory; +import ch.boye.httpclientandroidlib.MethodNotSupportedException; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.message.BasicHttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.message.BasicHttpRequest; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default factory for creating {@link HttpRequest} objects. + * + * @since 4.0 + */ +@Immutable +public class DefaultHttpRequestFactory implements HttpRequestFactory { + + public static final DefaultHttpRequestFactory INSTANCE = new DefaultHttpRequestFactory(); + + private static final String[] RFC2616_COMMON_METHODS = { + "GET" + }; + + private static final String[] RFC2616_ENTITY_ENC_METHODS = { + "POST", + "PUT" + }; + + private static final String[] RFC2616_SPECIAL_METHODS = { + "HEAD", + "OPTIONS", + "DELETE", + "TRACE", + "CONNECT" + }; + + + public DefaultHttpRequestFactory() { + super(); + } + + private static boolean isOneOf(final String[] methods, final String method) { + for (final String method2 : methods) { + if (method2.equalsIgnoreCase(method)) { + return true; + } + } + return false; + } + + public HttpRequest newHttpRequest(final RequestLine requestline) + throws MethodNotSupportedException { + Args.notNull(requestline, "Request line"); + final String method = requestline.getMethod(); + if (isOneOf(RFC2616_COMMON_METHODS, method)) { + return new BasicHttpRequest(requestline); + } else if (isOneOf(RFC2616_ENTITY_ENC_METHODS, method)) { + return new BasicHttpEntityEnclosingRequest(requestline); + } else if (isOneOf(RFC2616_SPECIAL_METHODS, method)) { + return new BasicHttpRequest(requestline); + } else { + throw new MethodNotSupportedException(method + " method not supported"); + } + } + + public HttpRequest newHttpRequest(final String method, final String uri) + throws MethodNotSupportedException { + if (isOneOf(RFC2616_COMMON_METHODS, method)) { + return new BasicHttpRequest(method, uri); + } else if (isOneOf(RFC2616_ENTITY_ENC_METHODS, method)) { + return new BasicHttpEntityEnclosingRequest(method, uri); + } else if (isOneOf(RFC2616_SPECIAL_METHODS, method)) { + return new BasicHttpRequest(method, uri); + } else { + throw new MethodNotSupportedException(method + + " method not supported"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpResponseFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpResponseFactory.java new file mode 100644 index 000000000..21511b9ff --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpResponseFactory.java @@ -0,0 +1,109 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.ReasonPhraseCatalog; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.message.BasicHttpResponse; +import ch.boye.httpclientandroidlib.message.BasicStatusLine; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default factory for creating {@link HttpResponse} objects. + * + * @since 4.0 + */ +@Immutable +public class DefaultHttpResponseFactory implements HttpResponseFactory { + + public static final DefaultHttpResponseFactory INSTANCE = new DefaultHttpResponseFactory(); + + /** The catalog for looking up reason phrases. */ + protected final ReasonPhraseCatalog reasonCatalog; + + + /** + * Creates a new response factory with the given catalog. + * + * @param catalog the catalog of reason phrases + */ + public DefaultHttpResponseFactory(final ReasonPhraseCatalog catalog) { + this.reasonCatalog = Args.notNull(catalog, "Reason phrase catalog"); + } + + /** + * Creates a new response factory with the default catalog. + * The default catalog is {@link EnglishReasonPhraseCatalog}. + */ + public DefaultHttpResponseFactory() { + this(EnglishReasonPhraseCatalog.INSTANCE); + } + + + // non-javadoc, see interface HttpResponseFactory + public HttpResponse newHttpResponse( + final ProtocolVersion ver, + final int status, + final HttpContext context) { + Args.notNull(ver, "HTTP version"); + final Locale loc = determineLocale(context); + final String reason = this.reasonCatalog.getReason(status, loc); + final StatusLine statusline = new BasicStatusLine(ver, status, reason); + return new BasicHttpResponse(statusline, this.reasonCatalog, loc); + } + + + // non-javadoc, see interface HttpResponseFactory + public HttpResponse newHttpResponse( + final StatusLine statusline, + final HttpContext context) { + Args.notNull(statusline, "Status line"); + return new BasicHttpResponse(statusline, this.reasonCatalog, determineLocale(context)); + } + + /** + * Determines the locale of the response. + * The implementation in this class always returns the default locale. + * + * @param context the context from which to determine the locale, or + * null to use the default locale + * + * @return the locale for the response, never null + */ + protected Locale determineLocale(final HttpContext context) { + return Locale.getDefault(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpServerConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpServerConnection.java new file mode 100644 index 000000000..8162e6107 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/DefaultHttpServerConnection.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of a server-side HTTP connection. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultBHttpServerConnection} + */ +@NotThreadSafe +@Deprecated +public class DefaultHttpServerConnection extends SocketHttpServerConnection { + + public DefaultHttpServerConnection() { + super(); + } + + @Override + public void bind(final Socket socket, final HttpParams params) throws IOException { + Args.notNull(socket, "Socket"); + Args.notNull(params, "HTTP parameters"); + assertNotOpen(); + socket.setTcpNoDelay(params.getBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)); + socket.setSoTimeout(params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0)); + socket.setKeepAlive(params.getBooleanParameter(CoreConnectionPNames.SO_KEEPALIVE, false)); + final int linger = params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1); + if (linger >= 0) { + socket.setSoLinger(linger > 0, linger); + } + if (linger >= 0) { + socket.setSoLinger(linger > 0, linger); + } + super.bind(socket, params); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/EnglishReasonPhraseCatalog.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/EnglishReasonPhraseCatalog.java new file mode 100644 index 000000000..51e435c0a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/EnglishReasonPhraseCatalog.java @@ -0,0 +1,224 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.ReasonPhraseCatalog; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * English reason phrases for HTTP status codes. + * All status codes defined in RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and + * RFC2518 (WebDAV) are supported. + * + * @since 4.0 + */ +@Immutable +public class EnglishReasonPhraseCatalog implements ReasonPhraseCatalog { + + // static array with english reason phrases defined below + + /** + * The default instance of this catalog. + * This catalog is thread safe, so there typically + * is no need to create other instances. + */ + public final static EnglishReasonPhraseCatalog INSTANCE = + new EnglishReasonPhraseCatalog(); + + + /** + * Restricted default constructor, for derived classes. + * If you need an instance of this class, use {@link #INSTANCE INSTANCE}. + */ + protected EnglishReasonPhraseCatalog() { + // no body + } + + + /** + * Obtains the reason phrase for a status code. + * + * @param status the status code, in the range 100-599 + * @param loc ignored + * + * @return the reason phrase, or null + */ + public String getReason(final int status, final Locale loc) { + Args.check(status >= 100 && status < 600, "Unknown category for status code " + status); + final int category = status / 100; + final int subcode = status - 100*category; + + String reason = null; + if (REASON_PHRASES[category].length > subcode) { + reason = REASON_PHRASES[category][subcode]; + } + + return reason; + } + + + /** Reason phrases lookup table. */ + private static final String[][] REASON_PHRASES = new String[][]{ + null, + new String[3], // 1xx + new String[8], // 2xx + new String[8], // 3xx + new String[25], // 4xx + new String[8] // 5xx + }; + + + + /** + * Stores the given reason phrase, by status code. + * Helper method to initialize the static lookup table. + * + * @param status the status code for which to define the phrase + * @param reason the reason phrase for this status code + */ + private static void setReason(final int status, final String reason) { + final int category = status / 100; + final int subcode = status - 100*category; + REASON_PHRASES[category][subcode] = reason; + } + + + // ----------------------------------------------------- Static Initializer + + /** Set up status code to "reason phrase" map. */ + static { + // HTTP 1.0 Server status codes -- see RFC 1945 + setReason(HttpStatus.SC_OK, + "OK"); + setReason(HttpStatus.SC_CREATED, + "Created"); + setReason(HttpStatus.SC_ACCEPTED, + "Accepted"); + setReason(HttpStatus.SC_NO_CONTENT, + "No Content"); + setReason(HttpStatus.SC_MOVED_PERMANENTLY, + "Moved Permanently"); + setReason(HttpStatus.SC_MOVED_TEMPORARILY, + "Moved Temporarily"); + setReason(HttpStatus.SC_NOT_MODIFIED, + "Not Modified"); + setReason(HttpStatus.SC_BAD_REQUEST, + "Bad Request"); + setReason(HttpStatus.SC_UNAUTHORIZED, + "Unauthorized"); + setReason(HttpStatus.SC_FORBIDDEN, + "Forbidden"); + setReason(HttpStatus.SC_NOT_FOUND, + "Not Found"); + setReason(HttpStatus.SC_INTERNAL_SERVER_ERROR, + "Internal Server Error"); + setReason(HttpStatus.SC_NOT_IMPLEMENTED, + "Not Implemented"); + setReason(HttpStatus.SC_BAD_GATEWAY, + "Bad Gateway"); + setReason(HttpStatus.SC_SERVICE_UNAVAILABLE, + "Service Unavailable"); + + // HTTP 1.1 Server status codes -- see RFC 2048 + setReason(HttpStatus.SC_CONTINUE, + "Continue"); + setReason(HttpStatus.SC_TEMPORARY_REDIRECT, + "Temporary Redirect"); + setReason(HttpStatus.SC_METHOD_NOT_ALLOWED, + "Method Not Allowed"); + setReason(HttpStatus.SC_CONFLICT, + "Conflict"); + setReason(HttpStatus.SC_PRECONDITION_FAILED, + "Precondition Failed"); + setReason(HttpStatus.SC_REQUEST_TOO_LONG, + "Request Too Long"); + setReason(HttpStatus.SC_REQUEST_URI_TOO_LONG, + "Request-URI Too Long"); + setReason(HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, + "Unsupported Media Type"); + setReason(HttpStatus.SC_MULTIPLE_CHOICES, + "Multiple Choices"); + setReason(HttpStatus.SC_SEE_OTHER, + "See Other"); + setReason(HttpStatus.SC_USE_PROXY, + "Use Proxy"); + setReason(HttpStatus.SC_PAYMENT_REQUIRED, + "Payment Required"); + setReason(HttpStatus.SC_NOT_ACCEPTABLE, + "Not Acceptable"); + setReason(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, + "Proxy Authentication Required"); + setReason(HttpStatus.SC_REQUEST_TIMEOUT, + "Request Timeout"); + + setReason(HttpStatus.SC_SWITCHING_PROTOCOLS, + "Switching Protocols"); + setReason(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, + "Non Authoritative Information"); + setReason(HttpStatus.SC_RESET_CONTENT, + "Reset Content"); + setReason(HttpStatus.SC_PARTIAL_CONTENT, + "Partial Content"); + setReason(HttpStatus.SC_GATEWAY_TIMEOUT, + "Gateway Timeout"); + setReason(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED, + "Http Version Not Supported"); + setReason(HttpStatus.SC_GONE, + "Gone"); + setReason(HttpStatus.SC_LENGTH_REQUIRED, + "Length Required"); + setReason(HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE, + "Requested Range Not Satisfiable"); + setReason(HttpStatus.SC_EXPECTATION_FAILED, + "Expectation Failed"); + + // WebDAV Server-specific status codes + setReason(HttpStatus.SC_PROCESSING, + "Processing"); + setReason(HttpStatus.SC_MULTI_STATUS, + "Multi-Status"); + setReason(HttpStatus.SC_UNPROCESSABLE_ENTITY, + "Unprocessable Entity"); + setReason(HttpStatus.SC_INSUFFICIENT_SPACE_ON_RESOURCE, + "Insufficient Space On Resource"); + setReason(HttpStatus.SC_METHOD_FAILURE, + "Method Failure"); + setReason(HttpStatus.SC_LOCKED, + "Locked"); + setReason(HttpStatus.SC_INSUFFICIENT_STORAGE, + "Insufficient Storage"); + setReason(HttpStatus.SC_FAILED_DEPENDENCY, + "Failed Dependency"); + } + + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/HttpConnectionMetricsImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/HttpConnectionMetricsImpl.java new file mode 100644 index 000000000..bfc415082 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/HttpConnectionMetricsImpl.java @@ -0,0 +1,148 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.util.HashMap; +import java.util.Map; + +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; + +/** + * Default implementation of the {@link HttpConnectionMetrics} interface. + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpConnectionMetricsImpl implements HttpConnectionMetrics { + + public static final String REQUEST_COUNT = "http.request-count"; + public static final String RESPONSE_COUNT = "http.response-count"; + public static final String SENT_BYTES_COUNT = "http.sent-bytes-count"; + public static final String RECEIVED_BYTES_COUNT = "http.received-bytes-count"; + + private final HttpTransportMetrics inTransportMetric; + private final HttpTransportMetrics outTransportMetric; + private long requestCount = 0; + private long responseCount = 0; + + /** + * The cache map for all metrics values. + */ + private Map metricsCache; + + public HttpConnectionMetricsImpl( + final HttpTransportMetrics inTransportMetric, + final HttpTransportMetrics outTransportMetric) { + super(); + this.inTransportMetric = inTransportMetric; + this.outTransportMetric = outTransportMetric; + } + + /* ------------------ Public interface method -------------------------- */ + + public long getReceivedBytesCount() { + if (this.inTransportMetric != null) { + return this.inTransportMetric.getBytesTransferred(); + } else { + return -1; + } + } + + public long getSentBytesCount() { + if (this.outTransportMetric != null) { + return this.outTransportMetric.getBytesTransferred(); + } else { + return -1; + } + } + + public long getRequestCount() { + return this.requestCount; + } + + public void incrementRequestCount() { + this.requestCount++; + } + + public long getResponseCount() { + return this.responseCount; + } + + public void incrementResponseCount() { + this.responseCount++; + } + + public Object getMetric(final String metricName) { + Object value = null; + if (this.metricsCache != null) { + value = this.metricsCache.get(metricName); + } + if (value == null) { + if (REQUEST_COUNT.equals(metricName)) { + value = Long.valueOf(requestCount); + } else if (RESPONSE_COUNT.equals(metricName)) { + value = Long.valueOf(responseCount); + } else if (RECEIVED_BYTES_COUNT.equals(metricName)) { + if (this.inTransportMetric != null) { + return Long.valueOf(this.inTransportMetric.getBytesTransferred()); + } else { + return null; + } + } else if (SENT_BYTES_COUNT.equals(metricName)) { + if (this.outTransportMetric != null) { + return Long.valueOf(this.outTransportMetric.getBytesTransferred()); + } else { + return null; + } + } + } + return value; + } + + public void setMetric(final String metricName, final Object obj) { + if (this.metricsCache == null) { + this.metricsCache = new HashMap(); + } + this.metricsCache.put(metricName, obj); + } + + public void reset() { + if (this.outTransportMetric != null) { + this.outTransportMetric.reset(); + } + if (this.inTransportMetric != null) { + this.inTransportMetric.reset(); + } + this.requestCount = 0; + this.responseCount = 0; + this.metricsCache = null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/NoConnectionReuseStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/NoConnectionReuseStrategy.java new file mode 100644 index 000000000..fdf312844 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/NoConnectionReuseStrategy.java @@ -0,0 +1,53 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * A strategy that never re-uses a connection. + * + * @since 4.0 + */ +@Immutable +public class NoConnectionReuseStrategy implements ConnectionReuseStrategy { + + public static final NoConnectionReuseStrategy INSTANCE = new NoConnectionReuseStrategy(); + + public NoConnectionReuseStrategy() { + super(); + } + + public boolean keepAlive(final HttpResponse response, final HttpContext context) { + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpClientConnection.java new file mode 100644 index 000000000..72f2f7e87 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpClientConnection.java @@ -0,0 +1,283 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; + +import ch.boye.httpclientandroidlib.HttpInetConnection; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.impl.io.SocketInputBuffer; +import ch.boye.httpclientandroidlib.impl.io.SocketOutputBuffer; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Implementation of a client-side HTTP connection that can be bound to an + * arbitrary {@link Socket} for receiving data from and transmitting data to + * a remote server. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultBHttpClientConnection} + */ +@NotThreadSafe +@Deprecated +public class SocketHttpClientConnection + extends AbstractHttpClientConnection implements HttpInetConnection { + + private volatile boolean open; + private volatile Socket socket = null; + + public SocketHttpClientConnection() { + super(); + } + + protected void assertNotOpen() { + Asserts.check(!this.open, "Connection is already open"); + } + + @Override + protected void assertOpen() { + Asserts.check(this.open, "Connection is not open"); + } + + /** + * Creates an instance of {@link SocketInputBuffer} to be used for + * receiving data from the given {@link Socket}. + *

    + * This method can be overridden in a super class in order to provide + * a custom implementation of {@link SessionInputBuffer} interface. + * + * @see SocketInputBuffer#SocketInputBuffer(Socket, int, HttpParams) + * + * @param socket the socket. + * @param buffersize the buffer size. + * @param params HTTP parameters. + * @return session input buffer. + * @throws IOException in case of an I/O error. + */ + protected SessionInputBuffer createSessionInputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + return new SocketInputBuffer(socket, buffersize, params); + } + + /** + * Creates an instance of {@link SessionOutputBuffer} to be used for + * sending data to the given {@link Socket}. + *

    + * This method can be overridden in a super class in order to provide + * a custom implementation of {@link SocketOutputBuffer} interface. + * + * @see SocketOutputBuffer#SocketOutputBuffer(Socket, int, HttpParams) + * + * @param socket the socket. + * @param buffersize the buffer size. + * @param params HTTP parameters. + * @return session output buffer. + * @throws IOException in case of an I/O error. + */ + protected SessionOutputBuffer createSessionOutputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + return new SocketOutputBuffer(socket, buffersize, params); + } + + /** + * Binds this connection to the given {@link Socket}. This socket will be + * used by the connection to send and receive data. + *

    + * This method will invoke {@link #createSessionInputBuffer(Socket, int, HttpParams)} + * and {@link #createSessionOutputBuffer(Socket, int, HttpParams)} methods + * to create session input / output buffers bound to this socket and then + * will invoke {@link #init(SessionInputBuffer, SessionOutputBuffer, HttpParams)} + * method to pass references to those buffers to the underlying HTTP message + * parser and formatter. + *

    + * After this method's execution the connection status will be reported + * as open and the {@link #isOpen()} will return true. + * + * @param socket the socket. + * @param params HTTP parameters. + * @throws IOException in case of an I/O error. + */ + protected void bind( + final Socket socket, + final HttpParams params) throws IOException { + Args.notNull(socket, "Socket"); + Args.notNull(params, "HTTP parameters"); + this.socket = socket; + + final int buffersize = params.getIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1); + init( + createSessionInputBuffer(socket, buffersize, params), + createSessionOutputBuffer(socket, buffersize, params), + params); + + this.open = true; + } + + public boolean isOpen() { + return this.open; + } + + protected Socket getSocket() { + return this.socket; + } + + public InetAddress getLocalAddress() { + if (this.socket != null) { + return this.socket.getLocalAddress(); + } else { + return null; + } + } + + public int getLocalPort() { + if (this.socket != null) { + return this.socket.getLocalPort(); + } else { + return -1; + } + } + + public InetAddress getRemoteAddress() { + if (this.socket != null) { + return this.socket.getInetAddress(); + } else { + return null; + } + } + + public int getRemotePort() { + if (this.socket != null) { + return this.socket.getPort(); + } else { + return -1; + } + } + + public void setSocketTimeout(final int timeout) { + assertOpen(); + if (this.socket != null) { + try { + this.socket.setSoTimeout(timeout); + } catch (final SocketException ignore) { + // It is not quite clear from the Sun's documentation if there are any + // other legitimate cases for a socket exception to be thrown when setting + // SO_TIMEOUT besides the socket being already closed + } + } + } + + public int getSocketTimeout() { + if (this.socket != null) { + try { + return this.socket.getSoTimeout(); + } catch (final SocketException ignore) { + return -1; + } + } else { + return -1; + } + } + + public void shutdown() throws IOException { + this.open = false; + final Socket tmpsocket = this.socket; + if (tmpsocket != null) { + tmpsocket.close(); + } + } + + public void close() throws IOException { + if (!this.open) { + return; + } + this.open = false; + final Socket sock = this.socket; + try { + doFlush(); + try { + try { + sock.shutdownOutput(); + } catch (final IOException ignore) { + } + try { + sock.shutdownInput(); + } catch (final IOException ignore) { + } + } catch (final UnsupportedOperationException ignore) { + // if one isn't supported, the other one isn't either + } + } finally { + sock.close(); + } + } + + private static void formatAddress(final StringBuilder buffer, final SocketAddress socketAddress) { + if (socketAddress instanceof InetSocketAddress) { + final InetSocketAddress addr = ((InetSocketAddress) socketAddress); + buffer.append(addr.getAddress() != null ? addr.getAddress().getHostAddress() : + addr.getAddress()) + .append(':') + .append(addr.getPort()); + } else { + buffer.append(socketAddress); + } + } + + @Override + public String toString() { + if (this.socket != null) { + final StringBuilder buffer = new StringBuilder(); + final SocketAddress remoteAddress = this.socket.getRemoteSocketAddress(); + final SocketAddress localAddress = this.socket.getLocalSocketAddress(); + if (remoteAddress != null && localAddress != null) { + formatAddress(buffer, localAddress); + buffer.append("<->"); + formatAddress(buffer, remoteAddress); + } + return buffer.toString(); + } else { + return super.toString(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpServerConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpServerConnection.java new file mode 100644 index 000000000..0a74b94a3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/SocketHttpServerConnection.java @@ -0,0 +1,271 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; + +import ch.boye.httpclientandroidlib.HttpInetConnection; +import ch.boye.httpclientandroidlib.impl.io.SocketInputBuffer; +import ch.boye.httpclientandroidlib.impl.io.SocketOutputBuffer; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +@Deprecated +public class SocketHttpServerConnection extends + AbstractHttpServerConnection implements HttpInetConnection { + + private volatile boolean open; + private volatile Socket socket = null; + + public SocketHttpServerConnection() { + super(); + } + + protected void assertNotOpen() { + Asserts.check(!this.open, "Connection is already open"); + } + + @Override + protected void assertOpen() { + Asserts.check(this.open, "Connection is not open"); + } + + /** + * Creates an instance of {@link SocketInputBuffer} to be used for + * receiving data from the given {@link Socket}. + *

    + * This method can be overridden in a super class in order to provide + * a custom implementation of {@link SessionInputBuffer} interface. + * + * @see SocketInputBuffer#SocketInputBuffer(Socket, int, HttpParams) + * + * @param socket the socket. + * @param buffersize the buffer size. + * @param params HTTP parameters. + * @return session input buffer. + * @throws IOException in case of an I/O error. + */ + protected SessionInputBuffer createSessionInputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + return new SocketInputBuffer(socket, buffersize, params); + } + + /** + * Creates an instance of {@link SessionOutputBuffer} to be used for + * sending data to the given {@link Socket}. + *

    + * This method can be overridden in a super class in order to provide + * a custom implementation of {@link SocketOutputBuffer} interface. + * + * @see SocketOutputBuffer#SocketOutputBuffer(Socket, int, HttpParams) + * + * @param socket the socket. + * @param buffersize the buffer size. + * @param params HTTP parameters. + * @return session output buffer. + * @throws IOException in case of an I/O error. + */ + protected SessionOutputBuffer createSessionOutputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + return new SocketOutputBuffer(socket, buffersize, params); + } + + /** + * Binds this connection to the given {@link Socket}. This socket will be + * used by the connection to send and receive data. + *

    + * This method will invoke {@link #createSessionInputBuffer(Socket, int, HttpParams)} + * and {@link #createSessionOutputBuffer(Socket, int, HttpParams)} methods + * to create session input / output buffers bound to this socket and then + * will invoke {@link #init(SessionInputBuffer, SessionOutputBuffer, HttpParams)} + * method to pass references to those buffers to the underlying HTTP message + * parser and formatter. + *

    + * After this method's execution the connection status will be reported + * as open and the {@link #isOpen()} will return true. + * + * @param socket the socket. + * @param params HTTP parameters. + * @throws IOException in case of an I/O error. + */ + protected void bind(final Socket socket, final HttpParams params) throws IOException { + Args.notNull(socket, "Socket"); + Args.notNull(params, "HTTP parameters"); + this.socket = socket; + + final int buffersize = params.getIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1); + init( + createSessionInputBuffer(socket, buffersize, params), + createSessionOutputBuffer(socket, buffersize, params), + params); + + this.open = true; + } + + protected Socket getSocket() { + return this.socket; + } + + public boolean isOpen() { + return this.open; + } + + public InetAddress getLocalAddress() { + if (this.socket != null) { + return this.socket.getLocalAddress(); + } else { + return null; + } + } + + public int getLocalPort() { + if (this.socket != null) { + return this.socket.getLocalPort(); + } else { + return -1; + } + } + + public InetAddress getRemoteAddress() { + if (this.socket != null) { + return this.socket.getInetAddress(); + } else { + return null; + } + } + + public int getRemotePort() { + if (this.socket != null) { + return this.socket.getPort(); + } else { + return -1; + } + } + + public void setSocketTimeout(final int timeout) { + assertOpen(); + if (this.socket != null) { + try { + this.socket.setSoTimeout(timeout); + } catch (final SocketException ignore) { + // It is not quite clear from the Sun's documentation if there are any + // other legitimate cases for a socket exception to be thrown when setting + // SO_TIMEOUT besides the socket being already closed + } + } + } + + public int getSocketTimeout() { + if (this.socket != null) { + try { + return this.socket.getSoTimeout(); + } catch (final SocketException ignore) { + return -1; + } + } else { + return -1; + } + } + + public void shutdown() throws IOException { + this.open = false; + final Socket tmpsocket = this.socket; + if (tmpsocket != null) { + tmpsocket.close(); + } + } + + public void close() throws IOException { + if (!this.open) { + return; + } + this.open = false; + this.open = false; + final Socket sock = this.socket; + try { + doFlush(); + try { + try { + sock.shutdownOutput(); + } catch (final IOException ignore) { + } + try { + sock.shutdownInput(); + } catch (final IOException ignore) { + } + } catch (final UnsupportedOperationException ignore) { + // if one isn't supported, the other one isn't either + } + } finally { + sock.close(); + } + } + + private static void formatAddress(final StringBuilder buffer, final SocketAddress socketAddress) { + if (socketAddress instanceof InetSocketAddress) { + final InetSocketAddress addr = ((InetSocketAddress) socketAddress); + buffer.append(addr.getAddress() != null ? addr.getAddress().getHostAddress() : + addr.getAddress()) + .append(':') + .append(addr.getPort()); + } else { + buffer.append(socketAddress); + } + } + + @Override + public String toString() { + if (this.socket != null) { + final StringBuilder buffer = new StringBuilder(); + final SocketAddress remoteAddress = this.socket.getRemoteSocketAddress(); + final SocketAddress localAddress = this.socket.getLocalSocketAddress(); + if (remoteAddress != null && localAddress != null) { + formatAddress(buffer, localAddress); + buffer.append("<->"); + formatAddress(buffer, remoteAddress); + } + return buffer.toString(); + } else { + return super.toString(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/AuthSchemeBase.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/AuthSchemeBase.java new file mode 100644 index 000000000..e1b98db9e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/AuthSchemeBase.java @@ -0,0 +1,169 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract authentication scheme class that serves as a basis + * for all authentication schemes supported by HttpClient. This class + * defines the generic way of parsing an authentication challenge. It + * does not make any assumptions regarding the format of the challenge + * nor does it impose any specific way of responding to that challenge. + * + * + * @since 4.0 + */ +@NotThreadSafe +public abstract class AuthSchemeBase implements ContextAwareAuthScheme { + + private ChallengeState challengeState; + + /** + * Creates an instance of AuthSchemeBase with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public AuthSchemeBase(final ChallengeState challengeState) { + super(); + this.challengeState = challengeState; + } + + public AuthSchemeBase() { + super(); + } + + /** + * Processes the given challenge token. Some authentication schemes + * may involve multiple challenge-response exchanges. Such schemes must be able + * to maintain the state information when dealing with sequential challenges + * + * @param header the challenge header + * + * @throws MalformedChallengeException is thrown if the authentication challenge + * is malformed + */ + public void processChallenge(final Header header) throws MalformedChallengeException { + Args.notNull(header, "Header"); + final String authheader = header.getName(); + if (authheader.equalsIgnoreCase(AUTH.WWW_AUTH)) { + this.challengeState = ChallengeState.TARGET; + } else if (authheader.equalsIgnoreCase(AUTH.PROXY_AUTH)) { + this.challengeState = ChallengeState.PROXY; + } else { + throw new MalformedChallengeException("Unexpected header name: " + authheader); + } + + final CharArrayBuffer buffer; + int pos; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + pos = ((FormattedHeader) header).getValuePos(); + } else { + final String s = header.getValue(); + if (s == null) { + throw new MalformedChallengeException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + pos = 0; + } + while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int beginIndex = pos; + while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int endIndex = pos; + final String s = buffer.substring(beginIndex, endIndex); + if (!s.equalsIgnoreCase(getSchemeName())) { + throw new MalformedChallengeException("Invalid scheme identifier: " + s); + } + + parseChallenge(buffer, pos, buffer.length()); + } + + + @SuppressWarnings("deprecation") + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + return authenticate(credentials, request); + } + + protected abstract void parseChallenge( + CharArrayBuffer buffer, int beginIndex, int endIndex) throws MalformedChallengeException; + + /** + * Returns true if authenticating against a proxy, false + * otherwise. + */ + public boolean isProxy() { + return this.challengeState != null && this.challengeState == ChallengeState.PROXY; + } + + /** + * Returns {@link ChallengeState} value or null if unchallenged. + * + * @since 4.2 + */ + public ChallengeState getChallengeState() { + return this.challengeState; + } + + @Override + public String toString() { + final String name = getSchemeName(); + if (name != null) { + return name.toUpperCase(Locale.ENGLISH); + } else { + return super.toString(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicScheme.java new file mode 100644 index 000000000..c58b5e582 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicScheme.java @@ -0,0 +1,219 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; + +import org.mozilla.apache.commons.codec.binary.Base64; +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; +import ch.boye.httpclientandroidlib.util.EncodingUtils; + +/** + * Basic authentication scheme as defined in RFC 2617. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicScheme extends RFC2617Scheme { + +/* Base64 instance removed by HttpClient for Android script. */ + /** Whether the basic authentication process is complete */ + private boolean complete; + + /** + * @since 4.3 + */ + public BasicScheme(final Charset credentialsCharset) { + super(credentialsCharset); +/* Base64 instance removed by HttpClient for Android script. */ + this.complete = false; + } + + /** + * Creates an instance of BasicScheme with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public BasicScheme(final ChallengeState challengeState) { + super(challengeState); +/* Base64 instance removed by HttpClient for Android script. */ + } + + public BasicScheme() { + this(Consts.ASCII); + } + + /** + * Returns textual designation of the basic authentication scheme. + * + * @return basic + */ + public String getSchemeName() { + return "basic"; + } + + /** + * Processes the Basic challenge. + * + * @param header the challenge header + * + * @throws MalformedChallengeException is thrown if the authentication challenge + * is malformed + */ + @Override + public void processChallenge( + final Header header) throws MalformedChallengeException { + super.processChallenge(header); + this.complete = true; + } + + /** + * Tests if the Basic authentication process has been completed. + * + * @return true if Basic authorization has been processed, + * false otherwise. + */ + public boolean isComplete() { + return this.complete; + } + + /** + * Returns false. Basic authentication scheme is request based. + * + * @return false. + */ + public boolean isConnectionBased() { + return false; + } + + /** + * @deprecated (4.2) Use {@link ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme#authenticate( + * Credentials, HttpRequest, ch.boye.httpclientandroidlib.protocol.HttpContext)} + */ + @Deprecated + public Header authenticate( + final Credentials credentials, final HttpRequest request) throws AuthenticationException { + return authenticate(credentials, request, new BasicHttpContext()); + } + + /** + * Produces basic authorization header for the given set of {@link Credentials}. + * + * @param credentials The set of credentials to be used for authentication + * @param request The request being authenticated + * @throws ch.boye.httpclientandroidlib.auth.InvalidCredentialsException if authentication + * credentials are not valid or not applicable for this authentication scheme + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * + * @return a basic authorization string + */ + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + + Args.notNull(credentials, "Credentials"); + Args.notNull(request, "HTTP request"); + final StringBuilder tmp = new StringBuilder(); + tmp.append(credentials.getUserPrincipal().getName()); + tmp.append(":"); + tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword()); + + final byte[] base64password = Base64.encodeBase64( + EncodingUtils.getBytes(tmp.toString(), getCredentialsCharset(request))); + + final CharArrayBuffer buffer = new CharArrayBuffer(32); + if (isProxy()) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": Basic "); + buffer.append(base64password, 0, base64password.length); + + return new BufferedHeader(buffer); + } + + /** + * Returns a basic Authorization header value for the given + * {@link Credentials} and charset. + * + * @param credentials The credentials to encode. + * @param charset The charset to use for encoding the credentials + * + * @return a basic authorization header + * + * @deprecated (4.3) use {@link #authenticate(Credentials, HttpRequest, HttpContext)}. + */ + @Deprecated + public static Header authenticate( + final Credentials credentials, + final String charset, + final boolean proxy) { + Args.notNull(credentials, "Credentials"); + Args.notNull(charset, "charset"); + + final StringBuilder tmp = new StringBuilder(); + tmp.append(credentials.getUserPrincipal().getName()); + tmp.append(":"); + tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword()); + + final byte[] base64password = Base64.encodeBase64( + EncodingUtils.getBytes(tmp.toString(), charset)); + + final CharArrayBuffer buffer = new CharArrayBuffer(32); + if (proxy) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": Basic "); + buffer.append(base64password, 0, base64password.length); + + return new BufferedHeader(buffer); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicSchemeFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicSchemeFactory.java new file mode 100644 index 000000000..c4eb8c6be --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/BasicSchemeFactory.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeFactory; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link AuthSchemeProvider} implementation that creates and initializes + * {@link BasicScheme} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class BasicSchemeFactory implements AuthSchemeFactory, AuthSchemeProvider { + + private final Charset charset; + + /** + * @since 4.3 + */ + public BasicSchemeFactory(final Charset charset) { + super(); + this.charset = charset; + } + + public BasicSchemeFactory() { + this(null); + } + + public AuthScheme newInstance(final HttpParams params) { + return new BasicScheme(); + } + + public AuthScheme create(final HttpContext context) { + return new BasicScheme(this.charset); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestScheme.java new file mode 100644 index 000000000..542f6236b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestScheme.java @@ -0,0 +1,489 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Formatter; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.StringTokenizer; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueFormatter; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; +import ch.boye.httpclientandroidlib.util.EncodingUtils; + +/** + * Digest authentication scheme as defined in RFC 2617. + * Both MD5 (default) and MD5-sess are supported. + * Currently only qop=auth or no qop is supported. qop=auth-int + * is unsupported. If auth and auth-int are provided, auth is + * used. + *

    + * Since the digest username is included as clear text in the generated + * Authentication header, the charset of the username must be compatible + * with the HTTP element charset used by the connection. + * + * @since 4.0 + */ +@NotThreadSafe +public class DigestScheme extends RFC2617Scheme { + + /** + * Hexa values used when creating 32 character long digest in HTTP DigestScheme + * in case of authentication. + * + * @see #encode(byte[]) + */ + private static final char[] HEXADECIMAL = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', + 'e', 'f' + }; + + /** Whether the digest authentication process is complete */ + private boolean complete; + + private static final int QOP_UNKNOWN = -1; + private static final int QOP_MISSING = 0; + private static final int QOP_AUTH_INT = 1; + private static final int QOP_AUTH = 2; + + private String lastNonce; + private long nounceCount; + private String cnonce; + private String a1; + private String a2; + + /** + * @since 4.3 + */ + public DigestScheme(final Charset credentialsCharset) { + super(credentialsCharset); + this.complete = false; + } + + /** + * Creates an instance of DigestScheme with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public DigestScheme(final ChallengeState challengeState) { + super(challengeState); + } + + public DigestScheme() { + this(Consts.ASCII); + } + + /** + * Processes the Digest challenge. + * + * @param header the challenge header + * + * @throws MalformedChallengeException is thrown if the authentication challenge + * is malformed + */ + @Override + public void processChallenge( + final Header header) throws MalformedChallengeException { + super.processChallenge(header); + this.complete = true; + } + + /** + * Tests if the Digest authentication process has been completed. + * + * @return true if Digest authorization has been processed, + * false otherwise. + */ + public boolean isComplete() { + final String s = getParameter("stale"); + if ("true".equalsIgnoreCase(s)) { + return false; + } else { + return this.complete; + } + } + + /** + * Returns textual designation of the digest authentication scheme. + * + * @return digest + */ + public String getSchemeName() { + return "digest"; + } + + /** + * Returns false. Digest authentication scheme is request based. + * + * @return false. + */ + public boolean isConnectionBased() { + return false; + } + + public void overrideParamter(final String name, final String value) { + getParameters().put(name, value); + } + + /** + * @deprecated (4.2) Use {@link ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme#authenticate( + * Credentials, HttpRequest, ch.boye.httpclientandroidlib.protocol.HttpContext)} + */ + @Deprecated + public Header authenticate( + final Credentials credentials, final HttpRequest request) throws AuthenticationException { + return authenticate(credentials, request, new BasicHttpContext()); + } + + /** + * Produces a digest authorization string for the given set of + * {@link Credentials}, method name and URI. + * + * @param credentials A set of credentials to be used for athentication + * @param request The request being authenticated + * + * @throws ch.boye.httpclientandroidlib.auth.InvalidCredentialsException if authentication credentials + * are not valid or not applicable for this authentication scheme + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * + * @return a digest authorization string + */ + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + + Args.notNull(credentials, "Credentials"); + Args.notNull(request, "HTTP request"); + if (getParameter("realm") == null) { + throw new AuthenticationException("missing realm in challenge"); + } + if (getParameter("nonce") == null) { + throw new AuthenticationException("missing nonce in challenge"); + } + // Add method name and request-URI to the parameter map + getParameters().put("methodname", request.getRequestLine().getMethod()); + getParameters().put("uri", request.getRequestLine().getUri()); + final String charset = getParameter("charset"); + if (charset == null) { + getParameters().put("charset", getCredentialsCharset(request)); + } + return createDigestHeader(credentials, request); + } + + private static MessageDigest createMessageDigest( + final String digAlg) throws UnsupportedDigestAlgorithmException { + try { + return MessageDigest.getInstance(digAlg); + } catch (final Exception e) { + throw new UnsupportedDigestAlgorithmException( + "Unsupported algorithm in HTTP Digest authentication: " + + digAlg); + } + } + + /** + * Creates digest-response header as defined in RFC2617. + * + * @param credentials User credentials + * + * @return The digest-response as String. + */ + private Header createDigestHeader( + final Credentials credentials, + final HttpRequest request) throws AuthenticationException { + final String uri = getParameter("uri"); + final String realm = getParameter("realm"); + final String nonce = getParameter("nonce"); + final String opaque = getParameter("opaque"); + final String method = getParameter("methodname"); + String algorithm = getParameter("algorithm"); + // If an algorithm is not specified, default to MD5. + if (algorithm == null) { + algorithm = "MD5"; + } + + final Set qopset = new HashSet(8); + int qop = QOP_UNKNOWN; + final String qoplist = getParameter("qop"); + if (qoplist != null) { + final StringTokenizer tok = new StringTokenizer(qoplist, ","); + while (tok.hasMoreTokens()) { + final String variant = tok.nextToken().trim(); + qopset.add(variant.toLowerCase(Locale.ENGLISH)); + } + if (request instanceof HttpEntityEnclosingRequest && qopset.contains("auth-int")) { + qop = QOP_AUTH_INT; + } else if (qopset.contains("auth")) { + qop = QOP_AUTH; + } + } else { + qop = QOP_MISSING; + } + + if (qop == QOP_UNKNOWN) { + throw new AuthenticationException("None of the qop methods is supported: " + qoplist); + } + + String charset = getParameter("charset"); + if (charset == null) { + charset = "ISO-8859-1"; + } + + String digAlg = algorithm; + if (digAlg.equalsIgnoreCase("MD5-sess")) { + digAlg = "MD5"; + } + + final MessageDigest digester; + try { + digester = createMessageDigest(digAlg); + } catch (final UnsupportedDigestAlgorithmException ex) { + throw new AuthenticationException("Unsuppported digest algorithm: " + digAlg); + } + + final String uname = credentials.getUserPrincipal().getName(); + final String pwd = credentials.getPassword(); + + if (nonce.equals(this.lastNonce)) { + nounceCount++; + } else { + nounceCount = 1; + cnonce = null; + lastNonce = nonce; + } + final StringBuilder sb = new StringBuilder(256); + final Formatter formatter = new Formatter(sb, Locale.US); + formatter.format("%08x", nounceCount); + formatter.close(); + final String nc = sb.toString(); + + if (cnonce == null) { + cnonce = createCnonce(); + } + + a1 = null; + a2 = null; + // 3.2.2.2: Calculating digest + if (algorithm.equalsIgnoreCase("MD5-sess")) { + // H( unq(username-value) ":" unq(realm-value) ":" passwd ) + // ":" unq(nonce-value) + // ":" unq(cnonce-value) + + // calculated one per session + sb.setLength(0); + sb.append(uname).append(':').append(realm).append(':').append(pwd); + final String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset))); + sb.setLength(0); + sb.append(checksum).append(':').append(nonce).append(':').append(cnonce); + a1 = sb.toString(); + } else { + // unq(username-value) ":" unq(realm-value) ":" passwd + sb.setLength(0); + sb.append(uname).append(':').append(realm).append(':').append(pwd); + a1 = sb.toString(); + } + + final String hasha1 = encode(digester.digest(EncodingUtils.getBytes(a1, charset))); + + if (qop == QOP_AUTH) { + // Method ":" digest-uri-value + a2 = method + ':' + uri; + } else if (qop == QOP_AUTH_INT) { + // Method ":" digest-uri-value ":" H(entity-body) + HttpEntity entity = null; + if (request instanceof HttpEntityEnclosingRequest) { + entity = ((HttpEntityEnclosingRequest) request).getEntity(); + } + if (entity != null && !entity.isRepeatable()) { + // If the entity is not repeatable, try falling back onto QOP_AUTH + if (qopset.contains("auth")) { + qop = QOP_AUTH; + a2 = method + ':' + uri; + } else { + throw new AuthenticationException("Qop auth-int cannot be used with " + + "a non-repeatable entity"); + } + } else { + final HttpEntityDigester entityDigester = new HttpEntityDigester(digester); + try { + if (entity != null) { + entity.writeTo(entityDigester); + } + entityDigester.close(); + } catch (final IOException ex) { + throw new AuthenticationException("I/O error reading entity content", ex); + } + a2 = method + ':' + uri + ':' + encode(entityDigester.getDigest()); + } + } else { + a2 = method + ':' + uri; + } + + final String hasha2 = encode(digester.digest(EncodingUtils.getBytes(a2, charset))); + + // 3.2.2.1 + + final String digestValue; + if (qop == QOP_MISSING) { + sb.setLength(0); + sb.append(hasha1).append(':').append(nonce).append(':').append(hasha2); + digestValue = sb.toString(); + } else { + sb.setLength(0); + sb.append(hasha1).append(':').append(nonce).append(':').append(nc).append(':') + .append(cnonce).append(':').append(qop == QOP_AUTH_INT ? "auth-int" : "auth") + .append(':').append(hasha2); + digestValue = sb.toString(); + } + + final String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue))); + + final CharArrayBuffer buffer = new CharArrayBuffer(128); + if (isProxy()) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": Digest "); + + final List params = new ArrayList(20); + params.add(new BasicNameValuePair("username", uname)); + params.add(new BasicNameValuePair("realm", realm)); + params.add(new BasicNameValuePair("nonce", nonce)); + params.add(new BasicNameValuePair("uri", uri)); + params.add(new BasicNameValuePair("response", digest)); + + if (qop != QOP_MISSING) { + params.add(new BasicNameValuePair("qop", qop == QOP_AUTH_INT ? "auth-int" : "auth")); + params.add(new BasicNameValuePair("nc", nc)); + params.add(new BasicNameValuePair("cnonce", cnonce)); + } + // algorithm cannot be null here + params.add(new BasicNameValuePair("algorithm", algorithm)); + if (opaque != null) { + params.add(new BasicNameValuePair("opaque", opaque)); + } + + for (int i = 0; i < params.size(); i++) { + final BasicNameValuePair param = params.get(i); + if (i > 0) { + buffer.append(", "); + } + final String name = param.getName(); + final boolean noQuotes = ("nc".equals(name) || "qop".equals(name) + || "algorithm".equals(name)); + BasicHeaderValueFormatter.INSTANCE.formatNameValuePair(buffer, param, !noQuotes); + } + return new BufferedHeader(buffer); + } + + String getCnonce() { + return cnonce; + } + + String getA1() { + return a1; + } + + String getA2() { + return a2; + } + + /** + * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long + * String according to RFC 2617. + * + * @param binaryData array containing the digest + * @return encoded MD5, or null if encoding failed + */ + static String encode(final byte[] binaryData) { + final int n = binaryData.length; + final char[] buffer = new char[n * 2]; + for (int i = 0; i < n; i++) { + final int low = (binaryData[i] & 0x0f); + final int high = ((binaryData[i] & 0xf0) >> 4); + buffer[i * 2] = HEXADECIMAL[high]; + buffer[(i * 2) + 1] = HEXADECIMAL[low]; + } + + return new String(buffer); + } + + + /** + * Creates a random cnonce value based on the current time. + * + * @return The cnonce value as String. + */ + public static String createCnonce() { + final SecureRandom rnd = new SecureRandom(); + final byte[] tmp = new byte[8]; + rnd.nextBytes(tmp); + return encode(tmp); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("DIGEST [complete=").append(complete) + .append(", nonce=").append(lastNonce) + .append(", nc=").append(nounceCount) + .append("]"); + return builder.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestSchemeFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestSchemeFactory.java new file mode 100644 index 000000000..0582bed49 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/DigestSchemeFactory.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeFactory; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link AuthSchemeProvider} implementation that creates and initializes + * {@link DigestScheme} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class DigestSchemeFactory implements AuthSchemeFactory, AuthSchemeProvider { + + private final Charset charset; + + /** + * @since 4.3 + */ + public DigestSchemeFactory(final Charset charset) { + super(); + this.charset = charset; + } + + public DigestSchemeFactory() { + this(null); + } + + public AuthScheme newInstance(final HttpParams params) { + return new DigestScheme(); + } + + public AuthScheme create(final HttpContext context) { + return new DigestScheme(this.charset); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpAuthenticator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpAuthenticator.java new file mode 100644 index 000000000..28b8b74d0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpAuthenticator.java @@ -0,0 +1,245 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; +import java.util.Locale; +import java.util.Map; +import java.util.Queue; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.auth.AuthOption; +import ch.boye.httpclientandroidlib.auth.AuthProtocolState; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * @since 4.3 + */ +public class HttpAuthenticator { + + public HttpClientAndroidLog log; + + public HttpAuthenticator(final HttpClientAndroidLog log) { + super(); + this.log = log != null ? log : new HttpClientAndroidLog(getClass()); + } + + public HttpAuthenticator() { + this(null); + } + + public boolean isAuthenticationRequested( + final HttpHost host, + final HttpResponse response, + final AuthenticationStrategy authStrategy, + final AuthState authState, + final HttpContext context) { + if (authStrategy.isAuthenticationRequested(host, response, context)) { + this.log.debug("Authentication required"); + if (authState.getState() == AuthProtocolState.SUCCESS) { + authStrategy.authFailed(host, authState.getAuthScheme(), context); + } + return true; + } else { + switch (authState.getState()) { + case CHALLENGED: + case HANDSHAKE: + this.log.debug("Authentication succeeded"); + authState.setState(AuthProtocolState.SUCCESS); + authStrategy.authSucceeded(host, authState.getAuthScheme(), context); + break; + case SUCCESS: + break; + default: + authState.setState(AuthProtocolState.UNCHALLENGED); + } + return false; + } + } + + public boolean handleAuthChallenge( + final HttpHost host, + final HttpResponse response, + final AuthenticationStrategy authStrategy, + final AuthState authState, + final HttpContext context) { + try { + if (this.log.isDebugEnabled()) { + this.log.debug(host.toHostString() + " requested authentication"); + } + final Map challenges = authStrategy.getChallenges(host, response, context); + if (challenges.isEmpty()) { + this.log.debug("Response contains no authentication challenges"); + return false; + } + + final AuthScheme authScheme = authState.getAuthScheme(); + switch (authState.getState()) { + case FAILURE: + return false; + case SUCCESS: + authState.reset(); + break; + case CHALLENGED: + case HANDSHAKE: + if (authScheme == null) { + this.log.debug("Auth scheme is null"); + authStrategy.authFailed(host, null, context); + authState.reset(); + authState.setState(AuthProtocolState.FAILURE); + return false; + } + case UNCHALLENGED: + if (authScheme != null) { + final String id = authScheme.getSchemeName(); + final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH)); + if (challenge != null) { + this.log.debug("Authorization challenge processed"); + authScheme.processChallenge(challenge); + if (authScheme.isComplete()) { + this.log.debug("Authentication failed"); + authStrategy.authFailed(host, authState.getAuthScheme(), context); + authState.reset(); + authState.setState(AuthProtocolState.FAILURE); + return false; + } else { + authState.setState(AuthProtocolState.HANDSHAKE); + return true; + } + } else { + authState.reset(); + // Retry authentication with a different scheme + } + } + } + final Queue authOptions = authStrategy.select(challenges, host, response, context); + if (authOptions != null && !authOptions.isEmpty()) { + if (this.log.isDebugEnabled()) { + this.log.debug("Selected authentication options: " + authOptions); + } + authState.setState(AuthProtocolState.CHALLENGED); + authState.update(authOptions); + return true; + } else { + return false; + } + } catch (final MalformedChallengeException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn("Malformed challenge: " + ex.getMessage()); + } + authState.reset(); + return false; + } + } + + public void generateAuthResponse( + final HttpRequest request, + final AuthState authState, + final HttpContext context) throws HttpException, IOException { + AuthScheme authScheme = authState.getAuthScheme(); + Credentials creds = authState.getCredentials(); + switch (authState.getState()) { + case FAILURE: + return; + case SUCCESS: + ensureAuthScheme(authScheme); + if (authScheme.isConnectionBased()) { + return; + } + break; + case CHALLENGED: + final Queue authOptions = authState.getAuthOptions(); + if (authOptions != null) { + while (!authOptions.isEmpty()) { + final AuthOption authOption = authOptions.remove(); + authScheme = authOption.getAuthScheme(); + creds = authOption.getCredentials(); + authState.update(authScheme, creds); + if (this.log.isDebugEnabled()) { + this.log.debug("Generating response to an authentication challenge using " + + authScheme.getSchemeName() + " scheme"); + } + try { + final Header header = doAuth(authScheme, creds, request, context); + request.addHeader(header); + break; + } catch (final AuthenticationException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn(authScheme + " authentication error: " + ex.getMessage()); + } + } + } + return; + } else { + ensureAuthScheme(authScheme); + } + } + if (authScheme != null) { + try { + final Header header = doAuth(authScheme, creds, request, context); + request.addHeader(header); + } catch (final AuthenticationException ex) { + if (this.log.isErrorEnabled()) { + this.log.error(authScheme + " authentication error: " + ex.getMessage()); + } + } + } + } + + private void ensureAuthScheme(final AuthScheme authScheme) { + Asserts.notNull(authScheme, "Auth scheme"); + } + + @SuppressWarnings("deprecation") + private Header doAuth( + final AuthScheme authScheme, + final Credentials creds, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + if (authScheme instanceof ContextAwareAuthScheme) { + return ((ContextAwareAuthScheme) authScheme).authenticate(creds, request, context); + } else { + return authScheme.authenticate(creds, request); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpEntityDigester.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpEntityDigester.java new file mode 100644 index 000000000..053951587 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/HttpEntityDigester.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.MessageDigest; + +class HttpEntityDigester extends OutputStream { + + private final MessageDigest digester; + private boolean closed; + private byte[] digest; + + HttpEntityDigester(final MessageDigest digester) { + super(); + this.digester = digester; + this.digester.reset(); + } + + @Override + public void write(final int b) throws IOException { + if (this.closed) { + throw new IOException("Stream has been already closed"); + } + this.digester.update((byte) b); + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + if (this.closed) { + throw new IOException("Stream has been already closed"); + } + this.digester.update(b, off, len); + } + + @Override + public void close() throws IOException { + if (this.closed) { + return; + } + this.closed = true; + this.digest = this.digester.digest(); + super.close(); + } + + public byte[] getDigest() { + return this.digest; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngine.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngine.java new file mode 100644 index 000000000..ce2457000 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngine.java @@ -0,0 +1,70 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +/** + * Abstract NTLM authentication engine. The engine can be used to + * generate Type1 messages and Type3 messages in response to a + * Type2 challenge. + * + * @since 4.0 + */ +public interface NTLMEngine { + + /** + * Generates a Type1 message given the domain and workstation. + * + * @param domain Optional Windows domain name. Can be null. + * @param workstation Optional Windows workstation name. Can be + * null. + * @return Type1 message + * @throws NTLMEngineException + */ + String generateType1Msg( + String domain, + String workstation) throws NTLMEngineException; + + /** + * Generates a Type3 message given the user credentials and the + * authentication challenge. + * + * @param username Windows user name + * @param password Password + * @param domain Windows domain name + * @param workstation Windows workstation name + * @param challenge Type2 challenge. + * @return Type3 response. + * @throws NTLMEngineException + */ + String generateType3Msg( + String username, + String password, + String domain, + String workstation, + String challenge) throws NTLMEngineException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineException.java new file mode 100644 index 000000000..88ef96912 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineException.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; + +/** + * Signals NTLM protocol failure. + * + * + * @since 4.0 + */ +@Immutable +public class NTLMEngineException extends AuthenticationException { + + private static final long serialVersionUID = 6027981323731768824L; + + public NTLMEngineException() { + super(); + } + + /** + * Creates a new NTLMEngineException with the specified message. + * + * @param message the exception detail message + */ + public NTLMEngineException(final String message) { + super(message); + } + + /** + * Creates a new NTLMEngineException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public NTLMEngineException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java new file mode 100644 index 000000000..195148f6b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMEngineImpl.java @@ -0,0 +1,1672 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.UnsupportedEncodingException; +import java.security.Key; +import java.security.MessageDigest; +import java.util.Arrays; +import java.util.Locale; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.mozilla.apache.commons.codec.binary.Base64; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.EncodingUtils; + +/** + * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM + * authentication protocol. + * + * @since 4.1 + */ +@NotThreadSafe +final class NTLMEngineImpl implements NTLMEngine { + + // Flags we use; descriptions according to: + // http://davenport.sourceforge.net/ntlm.html + // and + // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx + protected static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001; // Unicode string encoding requested + protected static final int FLAG_REQUEST_TARGET = 0x00000004; // Requests target field + protected static final int FLAG_REQUEST_SIGN = 0x00000010; // Requests all messages have a signature attached, in NEGOTIATE message. + protected static final int FLAG_REQUEST_SEAL = 0x00000020; // Request key exchange for message confidentiality in NEGOTIATE message. MUST be used in conjunction with 56BIT. + protected static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080; // Request Lan Manager key instead of user session key + protected static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security. MUST be set in NEGOTIATE and CHALLENGE both + protected static final int FLAG_DOMAIN_PRESENT = 0x00001000; // Domain is present in message + protected static final int FLAG_WORKSTATION_PRESENT = 0x00002000; // Workstation is present in message + protected static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000; // Requests a signature block on all messages. Overridden by REQUEST_SIGN and REQUEST_SEAL. + protected static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security + protected static final int FLAG_REQUEST_VERSION = 0x02000000; // Request protocol version + protected static final int FLAG_TARGETINFO_PRESENT = 0x00800000; // From server in challenge message, indicating targetinfo is present + protected static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange + protected static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000; // Request explicit key exchange + protected static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000; // Must be used in conjunction with SEAL + + + /** Secure random generator */ + private static final java.security.SecureRandom RND_GEN; + static { + java.security.SecureRandom rnd = null; + try { + rnd = java.security.SecureRandom.getInstance("SHA1PRNG"); + } catch (final Exception ignore) { + } + RND_GEN = rnd; + } + + /** Character encoding */ + static final String DEFAULT_CHARSET = "ASCII"; + + /** The character set to use for encoding the credentials */ + private String credentialCharset = DEFAULT_CHARSET; + + /** The signature string as bytes in the default encoding */ + private static final byte[] SIGNATURE; + + static { + final byte[] bytesWithoutNull = EncodingUtils.getBytes("NTLMSSP", "ASCII"); + SIGNATURE = new byte[bytesWithoutNull.length + 1]; + System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length); + SIGNATURE[bytesWithoutNull.length] = (byte) 0x00; + } + + /** + * Returns the response for the given message. + * + * @param message + * the message that was received from the server. + * @param username + * the username to authenticate with. + * @param password + * the password to authenticate with. + * @param host + * The host. + * @param domain + * the NT domain to authenticate in. + * @return The response. + * @throws ch.boye.httpclientandroidlib.HttpException + * If the messages cannot be retrieved. + */ + final String getResponseFor(final String message, final String username, final String password, + final String host, final String domain) throws NTLMEngineException { + + final String response; + if (message == null || message.trim().equals("")) { + response = getType1Message(host, domain); + } else { + final Type2Message t2m = new Type2Message(message); + response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m + .getFlags(), t2m.getTarget(), t2m.getTargetInfo()); + } + return response; + } + + /** + * Creates the first message (type 1 message) in the NTLM authentication + * sequence. This message includes the user name, domain and host for the + * authentication session. + * + * @param host + * the computer name of the host requesting authentication. + * @param domain + * The domain to authenticate with. + * @return String the message to add to the HTTP request header. + */ + String getType1Message(final String host, final String domain) throws NTLMEngineException { + return new Type1Message(domain, host).getResponse(); + } + + /** + * Creates the type 3 message using the given server nonce. The type 3 + * message includes all the information for authentication, host, domain, + * username and the result of encrypting the nonce sent by the server using + * the user's password as the key. + * + * @param user + * The user name. This should not include the domain name. + * @param password + * The password. + * @param host + * The host that is originating the authentication request. + * @param domain + * The domain to authenticate within. + * @param nonce + * the 8 byte array the server sent. + * @return The type 3 message. + * @throws NTLMEngineException + * If {@link #RC4(byte[],byte[])} fails. + */ + String getType3Message(final String user, final String password, final String host, final String domain, + final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation) + throws NTLMEngineException { + return new Type3Message(domain, host, user, password, nonce, type2Flags, target, + targetInformation).getResponse(); + } + + /** + * @return Returns the credentialCharset. + */ + String getCredentialCharset() { + return credentialCharset; + } + + /** + * @param credentialCharset + * The credentialCharset to set. + */ + void setCredentialCharset(final String credentialCharset) { + this.credentialCharset = credentialCharset; + } + + /** Strip dot suffix from a name */ + private static String stripDotSuffix(final String value) { + if (value == null) { + return null; + } + final int index = value.indexOf("."); + if (index != -1) { + return value.substring(0, index); + } + return value; + } + + /** Convert host to standard form */ + private static String convertHost(final String host) { + return stripDotSuffix(host); + } + + /** Convert domain to standard form */ + private static String convertDomain(final String domain) { + return stripDotSuffix(domain); + } + + private static int readULong(final byte[] src, final int index) throws NTLMEngineException { + if (src.length < index + 4) { + throw new NTLMEngineException("NTLM authentication - buffer too small for DWORD"); + } + return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8) + | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24); + } + + private static int readUShort(final byte[] src, final int index) throws NTLMEngineException { + if (src.length < index + 2) { + throw new NTLMEngineException("NTLM authentication - buffer too small for WORD"); + } + return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8); + } + + private static byte[] readSecurityBuffer(final byte[] src, final int index) throws NTLMEngineException { + final int length = readUShort(src, index); + final int offset = readULong(src, index + 4); + if (src.length < offset + length) { + throw new NTLMEngineException( + "NTLM authentication - buffer too small for data item"); + } + final byte[] buffer = new byte[length]; + System.arraycopy(src, offset, buffer, 0, length); + return buffer; + } + + /** Calculate a challenge block */ + private static byte[] makeRandomChallenge() throws NTLMEngineException { + if (RND_GEN == null) { + throw new NTLMEngineException("Random generator not available"); + } + final byte[] rval = new byte[8]; + synchronized (RND_GEN) { + RND_GEN.nextBytes(rval); + } + return rval; + } + + /** Calculate a 16-byte secondary key */ + private static byte[] makeSecondaryKey() throws NTLMEngineException { + if (RND_GEN == null) { + throw new NTLMEngineException("Random generator not available"); + } + final byte[] rval = new byte[16]; + synchronized (RND_GEN) { + RND_GEN.nextBytes(rval); + } + return rval; + } + + protected static class CipherGen { + + protected final String domain; + protected final String user; + protected final String password; + protected final byte[] challenge; + protected final String target; + protected final byte[] targetInformation; + + // Information we can generate but may be passed in (for testing) + protected byte[] clientChallenge; + protected byte[] clientChallenge2; + protected byte[] secondaryKey; + protected byte[] timestamp; + + // Stuff we always generate + protected byte[] lmHash = null; + protected byte[] lmResponse = null; + protected byte[] ntlmHash = null; + protected byte[] ntlmResponse = null; + protected byte[] ntlmv2Hash = null; + protected byte[] lmv2Hash = null; + protected byte[] lmv2Response = null; + protected byte[] ntlmv2Blob = null; + protected byte[] ntlmv2Response = null; + protected byte[] ntlm2SessionResponse = null; + protected byte[] lm2SessionResponse = null; + protected byte[] lmUserSessionKey = null; + protected byte[] ntlmUserSessionKey = null; + protected byte[] ntlmv2UserSessionKey = null; + protected byte[] ntlm2SessionResponseUserSessionKey = null; + protected byte[] lanManagerSessionKey = null; + + public CipherGen(final String domain, final String user, final String password, + final byte[] challenge, final String target, final byte[] targetInformation, + final byte[] clientChallenge, final byte[] clientChallenge2, + final byte[] secondaryKey, final byte[] timestamp) { + this.domain = domain; + this.target = target; + this.user = user; + this.password = password; + this.challenge = challenge; + this.targetInformation = targetInformation; + this.clientChallenge = clientChallenge; + this.clientChallenge2 = clientChallenge2; + this.secondaryKey = secondaryKey; + this.timestamp = timestamp; + } + + public CipherGen(final String domain, final String user, final String password, + final byte[] challenge, final String target, final byte[] targetInformation) { + this(domain, user, password, challenge, target, targetInformation, null, null, null, null); + } + + /** Calculate and return client challenge */ + public byte[] getClientChallenge() + throws NTLMEngineException { + if (clientChallenge == null) { + clientChallenge = makeRandomChallenge(); + } + return clientChallenge; + } + + /** Calculate and return second client challenge */ + public byte[] getClientChallenge2() + throws NTLMEngineException { + if (clientChallenge2 == null) { + clientChallenge2 = makeRandomChallenge(); + } + return clientChallenge2; + } + + /** Calculate and return random secondary key */ + public byte[] getSecondaryKey() + throws NTLMEngineException { + if (secondaryKey == null) { + secondaryKey = makeSecondaryKey(); + } + return secondaryKey; + } + + /** Calculate and return the LMHash */ + public byte[] getLMHash() + throws NTLMEngineException { + if (lmHash == null) { + lmHash = lmHash(password); + } + return lmHash; + } + + /** Calculate and return the LMResponse */ + public byte[] getLMResponse() + throws NTLMEngineException { + if (lmResponse == null) { + lmResponse = lmResponse(getLMHash(),challenge); + } + return lmResponse; + } + + /** Calculate and return the NTLMHash */ + public byte[] getNTLMHash() + throws NTLMEngineException { + if (ntlmHash == null) { + ntlmHash = ntlmHash(password); + } + return ntlmHash; + } + + /** Calculate and return the NTLMResponse */ + public byte[] getNTLMResponse() + throws NTLMEngineException { + if (ntlmResponse == null) { + ntlmResponse = lmResponse(getNTLMHash(),challenge); + } + return ntlmResponse; + } + + /** Calculate the LMv2 hash */ + public byte[] getLMv2Hash() + throws NTLMEngineException { + if (lmv2Hash == null) { + lmv2Hash = lmv2Hash(domain, user, getNTLMHash()); + } + return lmv2Hash; + } + + /** Calculate the NTLMv2 hash */ + public byte[] getNTLMv2Hash() + throws NTLMEngineException { + if (ntlmv2Hash == null) { + ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash()); + } + return ntlmv2Hash; + } + + /** Calculate a timestamp */ + public byte[] getTimestamp() { + if (timestamp == null) { + long time = System.currentTimeMillis(); + time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch. + time *= 10000; // tenths of a microsecond. + // convert to little-endian byte array. + timestamp = new byte[8]; + for (int i = 0; i < 8; i++) { + timestamp[i] = (byte) time; + time >>>= 8; + } + } + return timestamp; + } + + /** Calculate the NTLMv2Blob */ + public byte[] getNTLMv2Blob() + throws NTLMEngineException { + if (ntlmv2Blob == null) { + ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp()); + } + return ntlmv2Blob; + } + + /** Calculate the NTLMv2Response */ + public byte[] getNTLMv2Response() + throws NTLMEngineException { + if (ntlmv2Response == null) { + ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob()); + } + return ntlmv2Response; + } + + /** Calculate the LMv2Response */ + public byte[] getLMv2Response() + throws NTLMEngineException { + if (lmv2Response == null) { + lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge()); + } + return lmv2Response; + } + + /** Get NTLM2SessionResponse */ + public byte[] getNTLM2SessionResponse() + throws NTLMEngineException { + if (ntlm2SessionResponse == null) { + ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge()); + } + return ntlm2SessionResponse; + } + + /** Calculate and return LM2 session response */ + public byte[] getLM2SessionResponse() + throws NTLMEngineException { + if (lm2SessionResponse == null) { + final byte[] clChallenge = getClientChallenge(); + lm2SessionResponse = new byte[24]; + System.arraycopy(clChallenge, 0, lm2SessionResponse, 0, clChallenge.length); + Arrays.fill(lm2SessionResponse, clChallenge.length, lm2SessionResponse.length, (byte) 0x00); + } + return lm2SessionResponse; + } + + /** Get LMUserSessionKey */ + public byte[] getLMUserSessionKey() + throws NTLMEngineException { + if (lmUserSessionKey == null) { + lmUserSessionKey = new byte[16]; + System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8); + Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00); + } + return lmUserSessionKey; + } + + /** Get NTLMUserSessionKey */ + public byte[] getNTLMUserSessionKey() + throws NTLMEngineException { + if (ntlmUserSessionKey == null) { + final MD4 md4 = new MD4(); + md4.update(getNTLMHash()); + ntlmUserSessionKey = md4.getOutput(); + } + return ntlmUserSessionKey; + } + + /** GetNTLMv2UserSessionKey */ + public byte[] getNTLMv2UserSessionKey() + throws NTLMEngineException { + if (ntlmv2UserSessionKey == null) { + final byte[] ntlmv2hash = getNTLMv2Hash(); + final byte[] truncatedResponse = new byte[16]; + System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16); + ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash); + } + return ntlmv2UserSessionKey; + } + + /** Get NTLM2SessionResponseUserSessionKey */ + public byte[] getNTLM2SessionResponseUserSessionKey() + throws NTLMEngineException { + if (ntlm2SessionResponseUserSessionKey == null) { + final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse(); + final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length]; + System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length); + System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length); + ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,getNTLMUserSessionKey()); + } + return ntlm2SessionResponseUserSessionKey; + } + + /** Get LAN Manager session key */ + public byte[] getLanManagerSessionKey() + throws NTLMEngineException { + if (lanManagerSessionKey == null) { + try { + final byte[] keyBytes = new byte[14]; + System.arraycopy(getLMHash(), 0, keyBytes, 0, 8); + Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd); + final Key lowKey = createDESKey(keyBytes, 0); + final Key highKey = createDESKey(keyBytes, 7); + final byte[] truncatedResponse = new byte[8]; + System.arraycopy(getLMResponse(), 0, truncatedResponse, 0, truncatedResponse.length); + Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, lowKey); + final byte[] lowPart = des.doFinal(truncatedResponse); + des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, highKey); + final byte[] highPart = des.doFinal(truncatedResponse); + lanManagerSessionKey = new byte[16]; + System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length); + System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length); + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + return lanManagerSessionKey; + } + } + + /** Calculates HMAC-MD5 */ + static byte[] hmacMD5(final byte[] value, final byte[] key) + throws NTLMEngineException { + final HMACMD5 hmacMD5 = new HMACMD5(key); + hmacMD5.update(value); + return hmacMD5.getOutput(); + } + + /** Calculates RC4 */ + static byte[] RC4(final byte[] value, final byte[] key) + throws NTLMEngineException { + try { + final Cipher rc4 = Cipher.getInstance("RC4"); + rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4")); + return rc4.doFinal(value); + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Calculates the NTLM2 Session Response for the given challenge, using the + * specified password and client challenge. + * + * @return The NTLM2 Session Response. This is placed in the NTLM response + * field of the Type 3 message; the LM response field contains the + * client challenge, null-padded to 24 bytes. + */ + static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge, + final byte[] clientChallenge) throws NTLMEngineException { + try { + // Look up MD5 algorithm (was necessary on jdk 1.4.2) + // This used to be needed, but java 1.5.0_07 includes the MD5 + // algorithm (finally) + // Class x = Class.forName("gnu.crypto.hash.MD5"); + // Method updateMethod = x.getMethod("update",new + // Class[]{byte[].class}); + // Method digestMethod = x.getMethod("digest",new Class[0]); + // Object mdInstance = x.newInstance(); + // updateMethod.invoke(mdInstance,new Object[]{challenge}); + // updateMethod.invoke(mdInstance,new Object[]{clientChallenge}); + // byte[] digest = (byte[])digestMethod.invoke(mdInstance,new + // Object[0]); + + final MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(challenge); + md5.update(clientChallenge); + final byte[] digest = md5.digest(); + + final byte[] sessionHash = new byte[8]; + System.arraycopy(digest, 0, sessionHash, 0, 8); + return lmResponse(ntlmHash, sessionHash); + } catch (final Exception e) { + if (e instanceof NTLMEngineException) { + throw (NTLMEngineException) e; + } + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Creates the LM Hash of the user's password. + * + * @param password + * The password. + * + * @return The LM Hash of the given password, used in the calculation of the + * LM Response. + */ + private static byte[] lmHash(final String password) throws NTLMEngineException { + try { + final byte[] oemPassword = password.toUpperCase(Locale.ENGLISH).getBytes("US-ASCII"); + final int length = Math.min(oemPassword.length, 14); + final byte[] keyBytes = new byte[14]; + System.arraycopy(oemPassword, 0, keyBytes, 0, length); + final Key lowKey = createDESKey(keyBytes, 0); + final Key highKey = createDESKey(keyBytes, 7); + final byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII"); + final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, lowKey); + final byte[] lowHash = des.doFinal(magicConstant); + des.init(Cipher.ENCRYPT_MODE, highKey); + final byte[] highHash = des.doFinal(magicConstant); + final byte[] lmHash = new byte[16]; + System.arraycopy(lowHash, 0, lmHash, 0, 8); + System.arraycopy(highHash, 0, lmHash, 8, 8); + return lmHash; + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Creates the NTLM Hash of the user's password. + * + * @param password + * The password. + * + * @return The NTLM Hash of the given password, used in the calculation of + * the NTLM Response and the NTLMv2 and LMv2 Hashes. + */ + private static byte[] ntlmHash(final String password) throws NTLMEngineException { + try { + final byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked"); + final MD4 md4 = new MD4(); + md4.update(unicodePassword); + return md4.getOutput(); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); + } + } + + /** + * Creates the LMv2 Hash of the user's password. + * + * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2 + * Responses. + */ + private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash) + throws NTLMEngineException { + try { + final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); + // Upper case username, upper case domain! + hmacMD5.update(user.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); + if (domain != null) { + hmacMD5.update(domain.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); + } + return hmacMD5.getOutput(); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e); + } + } + + /** + * Creates the NTLMv2 Hash of the user's password. + * + * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2 + * Responses. + */ + private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash) + throws NTLMEngineException { + try { + final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); + // Upper case username, mixed case target!! + hmacMD5.update(user.toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked")); + if (domain != null) { + hmacMD5.update(domain.getBytes("UnicodeLittleUnmarked")); + } + return hmacMD5.getOutput(); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported! " + e.getMessage(), e); + } + } + + /** + * Creates the LM Response from the given hash and Type 2 challenge. + * + * @param hash + * The LM or NTLM Hash. + * @param challenge + * The server challenge from the Type 2 message. + * + * @return The response (either LM or NTLM, depending on the provided hash). + */ + private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException { + try { + final byte[] keyBytes = new byte[21]; + System.arraycopy(hash, 0, keyBytes, 0, 16); + final Key lowKey = createDESKey(keyBytes, 0); + final Key middleKey = createDESKey(keyBytes, 7); + final Key highKey = createDESKey(keyBytes, 14); + final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); + des.init(Cipher.ENCRYPT_MODE, lowKey); + final byte[] lowResponse = des.doFinal(challenge); + des.init(Cipher.ENCRYPT_MODE, middleKey); + final byte[] middleResponse = des.doFinal(challenge); + des.init(Cipher.ENCRYPT_MODE, highKey); + final byte[] highResponse = des.doFinal(challenge); + final byte[] lmResponse = new byte[24]; + System.arraycopy(lowResponse, 0, lmResponse, 0, 8); + System.arraycopy(middleResponse, 0, lmResponse, 8, 8); + System.arraycopy(highResponse, 0, lmResponse, 16, 8); + return lmResponse; + } catch (final Exception e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + + /** + * Creates the LMv2 Response from the given hash, client data, and Type 2 + * challenge. + * + * @param hash + * The NTLMv2 Hash. + * @param clientData + * The client data (blob or client challenge). + * @param challenge + * The server challenge from the Type 2 message. + * + * @return The response (either NTLMv2 or LMv2, depending on the client + * data). + */ + private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData) + throws NTLMEngineException { + final HMACMD5 hmacMD5 = new HMACMD5(hash); + hmacMD5.update(challenge); + hmacMD5.update(clientData); + final byte[] mac = hmacMD5.getOutput(); + final byte[] lmv2Response = new byte[mac.length + clientData.length]; + System.arraycopy(mac, 0, lmv2Response, 0, mac.length); + System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length); + return lmv2Response; + } + + /** + * Creates the NTLMv2 blob from the given target information block and + * client challenge. + * + * @param targetInformation + * The target information block from the Type 2 message. + * @param clientChallenge + * The random 8-byte client challenge. + * + * @return The blob, used in the calculation of the NTLMv2 Response. + */ + private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) { + final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 }; + final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; + final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; + final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; + final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8 + + unknown1.length + targetInformation.length + unknown2.length]; + int offset = 0; + System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length); + offset += blobSignature.length; + System.arraycopy(reserved, 0, blob, offset, reserved.length); + offset += reserved.length; + System.arraycopy(timestamp, 0, blob, offset, timestamp.length); + offset += timestamp.length; + System.arraycopy(clientChallenge, 0, blob, offset, 8); + offset += 8; + System.arraycopy(unknown1, 0, blob, offset, unknown1.length); + offset += unknown1.length; + System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length); + offset += targetInformation.length; + System.arraycopy(unknown2, 0, blob, offset, unknown2.length); + offset += unknown2.length; + return blob; + } + + /** + * Creates a DES encryption key from the given key material. + * + * @param bytes + * A byte array containing the DES key material. + * @param offset + * The offset in the given byte array at which the 7-byte key + * material starts. + * + * @return A DES encryption key created from the key material starting at + * the specified offset in the given byte array. + */ + private static Key createDESKey(final byte[] bytes, final int offset) { + final byte[] keyBytes = new byte[7]; + System.arraycopy(bytes, offset, keyBytes, 0, 7); + final byte[] material = new byte[8]; + material[0] = keyBytes[0]; + material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1); + material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2); + material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3); + material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4); + material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5); + material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6); + material[7] = (byte) (keyBytes[6] << 1); + oddParity(material); + return new SecretKeySpec(material, "DES"); + } + + /** + * Applies odd parity to the given byte array. + * + * @param bytes + * The data whose parity bits are to be adjusted for odd parity. + */ + private static void oddParity(final byte[] bytes) { + for (int i = 0; i < bytes.length; i++) { + final byte b = bytes[i]; + final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) + ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0; + if (needsParity) { + bytes[i] |= (byte) 0x01; + } else { + bytes[i] &= (byte) 0xfe; + } + } + } + + /** NTLM message generation, base class */ + static class NTLMMessage { + /** The current response */ + private byte[] messageContents = null; + + /** The current output position */ + private int currentOutputPosition = 0; + + /** Constructor to use when message contents are not yet known */ + NTLMMessage() { + } + + /** Constructor to use when message contents are known */ + NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException { + messageContents = Base64.decodeBase64(EncodingUtils.getBytes(messageBody, + DEFAULT_CHARSET)); + // Look for NTLM message + if (messageContents.length < SIGNATURE.length) { + throw new NTLMEngineException("NTLM message decoding error - packet too short"); + } + int i = 0; + while (i < SIGNATURE.length) { + if (messageContents[i] != SIGNATURE[i]) { + throw new NTLMEngineException( + "NTLM message expected - instead got unrecognized bytes"); + } + i++; + } + + // Check to be sure there's a type 2 message indicator next + final int type = readULong(SIGNATURE.length); + if (type != expectedType) { + throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType) + + " message expected - instead got type " + Integer.toString(type)); + } + + currentOutputPosition = messageContents.length; + } + + /** + * Get the length of the signature and flags, so calculations can adjust + * offsets accordingly. + */ + protected int getPreambleLength() { + return SIGNATURE.length + 4; + } + + /** Get the message length */ + protected int getMessageLength() { + return currentOutputPosition; + } + + /** Read a byte from a position within the message buffer */ + protected byte readByte(final int position) throws NTLMEngineException { + if (messageContents.length < position + 1) { + throw new NTLMEngineException("NTLM: Message too short"); + } + return messageContents[position]; + } + + /** Read a bunch of bytes from a position in the message buffer */ + protected void readBytes(final byte[] buffer, final int position) throws NTLMEngineException { + if (messageContents.length < position + buffer.length) { + throw new NTLMEngineException("NTLM: Message too short"); + } + System.arraycopy(messageContents, position, buffer, 0, buffer.length); + } + + /** Read a ushort from a position within the message buffer */ + protected int readUShort(final int position) throws NTLMEngineException { + return NTLMEngineImpl.readUShort(messageContents, position); + } + + /** Read a ulong from a position within the message buffer */ + protected int readULong(final int position) throws NTLMEngineException { + return NTLMEngineImpl.readULong(messageContents, position); + } + + /** Read a security buffer from a position within the message buffer */ + protected byte[] readSecurityBuffer(final int position) throws NTLMEngineException { + return NTLMEngineImpl.readSecurityBuffer(messageContents, position); + } + + /** + * Prepares the object to create a response of the given length. + * + * @param maxlength + * the maximum length of the response to prepare, not + * including the type and the signature (which this method + * adds). + */ + protected void prepareResponse(final int maxlength, final int messageType) { + messageContents = new byte[maxlength]; + currentOutputPosition = 0; + addBytes(SIGNATURE); + addULong(messageType); + } + + /** + * Adds the given byte to the response. + * + * @param b + * the byte to add. + */ + protected void addByte(final byte b) { + messageContents[currentOutputPosition] = b; + currentOutputPosition++; + } + + /** + * Adds the given bytes to the response. + * + * @param bytes + * the bytes to add. + */ + protected void addBytes(final byte[] bytes) { + if (bytes == null) { + return; + } + for (final byte b : bytes) { + messageContents[currentOutputPosition] = b; + currentOutputPosition++; + } + } + + /** Adds a USHORT to the response */ + protected void addUShort(final int value) { + addByte((byte) (value & 0xff)); + addByte((byte) (value >> 8 & 0xff)); + } + + /** Adds a ULong to the response */ + protected void addULong(final int value) { + addByte((byte) (value & 0xff)); + addByte((byte) (value >> 8 & 0xff)); + addByte((byte) (value >> 16 & 0xff)); + addByte((byte) (value >> 24 & 0xff)); + } + + /** + * Returns the response that has been generated after shrinking the + * array if required and base64 encodes the response. + * + * @return The response as above. + */ + String getResponse() { + final byte[] resp; + if (messageContents.length > currentOutputPosition) { + final byte[] tmp = new byte[currentOutputPosition]; + System.arraycopy(messageContents, 0, tmp, 0, currentOutputPosition); + resp = tmp; + } else { + resp = messageContents; + } + return EncodingUtils.getAsciiString(Base64.encodeBase64(resp)); + } + + } + + /** Type 1 message assembly class */ + static class Type1Message extends NTLMMessage { + protected byte[] hostBytes; + protected byte[] domainBytes; + + /** Constructor. Include the arguments the message will need */ + Type1Message(final String domain, final String host) throws NTLMEngineException { + super(); + try { + // Strip off domain name from the host! + final String unqualifiedHost = convertHost(host); + // Use only the base domain name! + final String unqualifiedDomain = convertDomain(domain); + + hostBytes = unqualifiedHost != null? unqualifiedHost.getBytes("ASCII") : null; + domainBytes = unqualifiedDomain != null ? unqualifiedDomain + .toUpperCase(Locale.ENGLISH).getBytes("ASCII") : null; + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode unsupported: " + e.getMessage(), e); + } + } + + /** + * Getting the response involves building the message before returning + * it + */ + @Override + String getResponse() { + // Now, build the message. Calculate its length first, including + // signature or type. + final int finalLength = 32 + 8 /*+ hostBytes.length + domainBytes.length */; + + // Set up the response. This will initialize the signature, message + // type, and flags. + prepareResponse(finalLength, 1); + + // Flags. These are the complete set of flags we support. + addULong( + //FLAG_WORKSTATION_PRESENT | + //FLAG_DOMAIN_PRESENT | + + // Required flags + //FLAG_REQUEST_LAN_MANAGER_KEY | + FLAG_REQUEST_NTLMv1 | + FLAG_REQUEST_NTLM2_SESSION | + + // Protocol version request + FLAG_REQUEST_VERSION | + + // Recommended privacy settings + FLAG_REQUEST_ALWAYS_SIGN | + //FLAG_REQUEST_SEAL | + //FLAG_REQUEST_SIGN | + + // These must be set according to documentation, based on use of SEAL above + FLAG_REQUEST_128BIT_KEY_EXCH | + FLAG_REQUEST_56BIT_ENCRYPTION | + //FLAG_REQUEST_EXPLICIT_KEY_EXCH | + + FLAG_REQUEST_UNICODE_ENCODING); + + // Domain length (two times). + addUShort(/*domainBytes.length*/0); + addUShort(/*domainBytes.length*/0); + + // Domain offset. + addULong(/*hostBytes.length +*/ 32 + 8); + + // Host length (two times). + addUShort(/*hostBytes.length*/0); + addUShort(/*hostBytes.length*/0); + + // Host offset (always 32 + 8). + addULong(32 + 8); + + // Version + addUShort(0x0105); + // Build + addULong(2600); + // NTLM revision + addUShort(0x0f00); + + + // Host (workstation) String. + //addBytes(hostBytes); + + // Domain String. + //addBytes(domainBytes); + + + return super.getResponse(); + } + + } + + /** Type 2 message class */ + static class Type2Message extends NTLMMessage { + protected byte[] challenge; + protected String target; + protected byte[] targetInfo; + protected int flags; + + Type2Message(final String message) throws NTLMEngineException { + super(message, 2); + + // Type 2 message is laid out as follows: + // First 8 bytes: NTLMSSP[0] + // Next 4 bytes: Ulong, value 2 + // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset) + // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235 + // Next 8 bytes, starting at offset 24: Challenge + // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros) + // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset) + // Next 2 bytes, major/minor version number (e.g. 0x05 0x02) + // Next 8 bytes, build number + // Next 2 bytes, protocol version number (e.g. 0x00 0x0f) + // Next, various text fields, and a ushort of value 0 at the end + + // Parse out the rest of the info we need from the message + // The nonce is the 8 bytes starting from the byte in position 24. + challenge = new byte[8]; + readBytes(challenge, 24); + + flags = readULong(20); + + if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) { + throw new NTLMEngineException( + "NTLM type 2 message has flags that make no sense: " + + Integer.toString(flags)); + } + + // Do the target! + target = null; + // The TARGET_DESIRED flag is said to not have understood semantics + // in Type2 messages, so use the length of the packet to decide + // how to proceed instead + if (getMessageLength() >= 12 + 8) { + final byte[] bytes = readSecurityBuffer(12); + if (bytes.length != 0) { + try { + target = new String(bytes, "UnicodeLittleUnmarked"); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException(e.getMessage(), e); + } + } + } + + // Do the target info! + targetInfo = null; + // TARGET_DESIRED flag cannot be relied on, so use packet length + if (getMessageLength() >= 40 + 8) { + final byte[] bytes = readSecurityBuffer(40); + if (bytes.length != 0) { + targetInfo = bytes; + } + } + } + + /** Retrieve the challenge */ + byte[] getChallenge() { + return challenge; + } + + /** Retrieve the target */ + String getTarget() { + return target; + } + + /** Retrieve the target info */ + byte[] getTargetInfo() { + return targetInfo; + } + + /** Retrieve the response flags */ + int getFlags() { + return flags; + } + + } + + /** Type 3 message assembly class */ + static class Type3Message extends NTLMMessage { + // Response flags from the type2 message + protected int type2Flags; + + protected byte[] domainBytes; + protected byte[] hostBytes; + protected byte[] userBytes; + + protected byte[] lmResp; + protected byte[] ntResp; + protected byte[] sessionKey; + + + /** Constructor. Pass the arguments we will need */ + Type3Message(final String domain, final String host, final String user, final String password, final byte[] nonce, + final int type2Flags, final String target, final byte[] targetInformation) + throws NTLMEngineException { + // Save the flags + this.type2Flags = type2Flags; + + // Strip off domain name from the host! + final String unqualifiedHost = convertHost(host); + // Use only the base domain name! + final String unqualifiedDomain = convertDomain(domain); + + // Create a cipher generator class. Use domain BEFORE it gets modified! + final CipherGen gen = new CipherGen(unqualifiedDomain, user, password, nonce, target, targetInformation); + + // Use the new code to calculate the responses, including v2 if that + // seems warranted. + byte[] userSessionKey; + try { + // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet + // been tested + if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) && + targetInformation != null && target != null) { + // NTLMv2 + ntResp = gen.getNTLMv2Response(); + lmResp = gen.getLMv2Response(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getNTLMv2UserSessionKey(); + } + } else { + // NTLMv1 + if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) { + // NTLM2 session stuff is requested + ntResp = gen.getNTLM2SessionResponse(); + lmResp = gen.getLM2SessionResponse(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getNTLM2SessionResponseUserSessionKey(); + } + } else { + ntResp = gen.getNTLMResponse(); + lmResp = gen.getLMResponse(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getNTLMUserSessionKey(); + } + } + } + } catch (final NTLMEngineException e) { + // This likely means we couldn't find the MD4 hash algorithm - + // fail back to just using LM + ntResp = new byte[0]; + lmResp = gen.getLMResponse(); + if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { + userSessionKey = gen.getLanManagerSessionKey(); + } else { + userSessionKey = gen.getLMUserSessionKey(); + } + } + + if ((type2Flags & FLAG_REQUEST_SIGN) != 0) { + if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) { + sessionKey = RC4(gen.getSecondaryKey(), userSessionKey); + } else { + sessionKey = userSessionKey; + } + } else { + sessionKey = null; + } + + try { + hostBytes = unqualifiedHost != null ? unqualifiedHost + .getBytes("UnicodeLittleUnmarked") : null; + domainBytes = unqualifiedDomain != null ? unqualifiedDomain + .toUpperCase(Locale.ENGLISH).getBytes("UnicodeLittleUnmarked") : null; + userBytes = user.getBytes("UnicodeLittleUnmarked"); + } catch (final UnsupportedEncodingException e) { + throw new NTLMEngineException("Unicode not supported: " + e.getMessage(), e); + } + } + + /** Assemble the response */ + @Override + String getResponse() { + final int ntRespLen = ntResp.length; + final int lmRespLen = lmResp.length; + + final int domainLen = domainBytes != null ? domainBytes.length : 0; + final int hostLen = hostBytes != null ? hostBytes.length: 0; + final int userLen = userBytes.length; + final int sessionKeyLen; + if (sessionKey != null) { + sessionKeyLen = sessionKey.length; + } else { + sessionKeyLen = 0; + } + + // Calculate the layout within the packet + final int lmRespOffset = 72; // allocate space for the version + final int ntRespOffset = lmRespOffset + lmRespLen; + final int domainOffset = ntRespOffset + ntRespLen; + final int userOffset = domainOffset + domainLen; + final int hostOffset = userOffset + userLen; + final int sessionKeyOffset = hostOffset + hostLen; + final int finalLength = sessionKeyOffset + sessionKeyLen; + + // Start the response. Length includes signature and type + prepareResponse(finalLength, 3); + + // LM Resp Length (twice) + addUShort(lmRespLen); + addUShort(lmRespLen); + + // LM Resp Offset + addULong(lmRespOffset); + + // NT Resp Length (twice) + addUShort(ntRespLen); + addUShort(ntRespLen); + + // NT Resp Offset + addULong(ntRespOffset); + + // Domain length (twice) + addUShort(domainLen); + addUShort(domainLen); + + // Domain offset. + addULong(domainOffset); + + // User Length (twice) + addUShort(userLen); + addUShort(userLen); + + // User offset + addULong(userOffset); + + // Host length (twice) + addUShort(hostLen); + addUShort(hostLen); + + // Host offset + addULong(hostOffset); + + // Session key length (twice) + addUShort(sessionKeyLen); + addUShort(sessionKeyLen); + + // Session key offset + addULong(sessionKeyOffset); + + // Flags. + addULong( + //FLAG_WORKSTATION_PRESENT | + //FLAG_DOMAIN_PRESENT | + + // Required flags + (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) | + (type2Flags & FLAG_REQUEST_NTLMv1) | + (type2Flags & FLAG_REQUEST_NTLM2_SESSION) | + + // Protocol version request + FLAG_REQUEST_VERSION | + + // Recommended privacy settings + (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) | + (type2Flags & FLAG_REQUEST_SEAL) | + (type2Flags & FLAG_REQUEST_SIGN) | + + // These must be set according to documentation, based on use of SEAL above + (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) | + (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) | + (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) | + + (type2Flags & FLAG_TARGETINFO_PRESENT) | + (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) | + (type2Flags & FLAG_REQUEST_TARGET) + ); + + // Version + addUShort(0x0105); + // Build + addULong(2600); + // NTLM revision + addUShort(0x0f00); + + // Add the actual data + addBytes(lmResp); + addBytes(ntResp); + addBytes(domainBytes); + addBytes(userBytes); + addBytes(hostBytes); + if (sessionKey != null) { + addBytes(sessionKey); + } + + return super.getResponse(); + } + } + + static void writeULong(final byte[] buffer, final int value, final int offset) { + buffer[offset] = (byte) (value & 0xff); + buffer[offset + 1] = (byte) (value >> 8 & 0xff); + buffer[offset + 2] = (byte) (value >> 16 & 0xff); + buffer[offset + 3] = (byte) (value >> 24 & 0xff); + } + + static int F(final int x, final int y, final int z) { + return ((x & y) | (~x & z)); + } + + static int G(final int x, final int y, final int z) { + return ((x & y) | (x & z) | (y & z)); + } + + static int H(final int x, final int y, final int z) { + return (x ^ y ^ z); + } + + static int rotintlft(final int val, final int numbits) { + return ((val << numbits) | (val >>> (32 - numbits))); + } + + /** + * Cryptography support - MD4. The following class was based loosely on the + * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java. + * Code correctness was verified by looking at MD4.java from the jcifs + * library (http://jcifs.samba.org). It was massaged extensively to the + * final form found here by Karl Wright (kwright@metacarta.com). + */ + static class MD4 { + protected int A = 0x67452301; + protected int B = 0xefcdab89; + protected int C = 0x98badcfe; + protected int D = 0x10325476; + protected long count = 0L; + protected byte[] dataBuffer = new byte[64]; + + MD4() { + } + + void update(final byte[] input) { + // We always deal with 512 bits at a time. Correspondingly, there is + // a buffer 64 bytes long that we write data into until it gets + // full. + int curBufferPos = (int) (count & 63L); + int inputIndex = 0; + while (input.length - inputIndex + curBufferPos >= dataBuffer.length) { + // We have enough data to do the next step. Do a partial copy + // and a transform, updating inputIndex and curBufferPos + // accordingly + final int transferAmt = dataBuffer.length - curBufferPos; + System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); + count += transferAmt; + curBufferPos = 0; + inputIndex += transferAmt; + processBuffer(); + } + + // If there's anything left, copy it into the buffer and leave it. + // We know there's not enough left to process. + if (inputIndex < input.length) { + final int transferAmt = input.length - inputIndex; + System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); + count += transferAmt; + curBufferPos += transferAmt; + } + } + + byte[] getOutput() { + // Feed pad/length data into engine. This must round out the input + // to a multiple of 512 bits. + final int bufferIndex = (int) (count & 63L); + final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex); + final byte[] postBytes = new byte[padLen + 8]; + // Leading 0x80, specified amount of zero padding, then length in + // bits. + postBytes[0] = (byte) 0x80; + // Fill out the last 8 bytes with the length + for (int i = 0; i < 8; i++) { + postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i)); + } + + // Update the engine + update(postBytes); + + // Calculate final result + final byte[] result = new byte[16]; + writeULong(result, A, 0); + writeULong(result, B, 4); + writeULong(result, C, 8); + writeULong(result, D, 12); + return result; + } + + protected void processBuffer() { + // Convert current buffer to 16 ulongs + final int[] d = new int[16]; + + for (int i = 0; i < 16; i++) { + d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8) + + ((dataBuffer[i * 4 + 2] & 0xff) << 16) + + ((dataBuffer[i * 4 + 3] & 0xff) << 24); + } + + // Do a round of processing + final int AA = A; + final int BB = B; + final int CC = C; + final int DD = D; + round1(d); + round2(d); + round3(d); + A += AA; + B += BB; + C += CC; + D += DD; + + } + + protected void round1(final int[] d) { + A = rotintlft((A + F(B, C, D) + d[0]), 3); + D = rotintlft((D + F(A, B, C) + d[1]), 7); + C = rotintlft((C + F(D, A, B) + d[2]), 11); + B = rotintlft((B + F(C, D, A) + d[3]), 19); + + A = rotintlft((A + F(B, C, D) + d[4]), 3); + D = rotintlft((D + F(A, B, C) + d[5]), 7); + C = rotintlft((C + F(D, A, B) + d[6]), 11); + B = rotintlft((B + F(C, D, A) + d[7]), 19); + + A = rotintlft((A + F(B, C, D) + d[8]), 3); + D = rotintlft((D + F(A, B, C) + d[9]), 7); + C = rotintlft((C + F(D, A, B) + d[10]), 11); + B = rotintlft((B + F(C, D, A) + d[11]), 19); + + A = rotintlft((A + F(B, C, D) + d[12]), 3); + D = rotintlft((D + F(A, B, C) + d[13]), 7); + C = rotintlft((C + F(D, A, B) + d[14]), 11); + B = rotintlft((B + F(C, D, A) + d[15]), 19); + } + + protected void round2(final int[] d) { + A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13); + + A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13); + + A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13); + + A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3); + D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5); + C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9); + B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13); + + } + + protected void round3(final int[] d) { + A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15); + + A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15); + + A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15); + + A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3); + D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9); + C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11); + B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15); + + } + + } + + /** + * Cryptography support - HMACMD5 - algorithmically based on various web + * resources by Karl Wright + */ + static class HMACMD5 { + protected byte[] ipad; + protected byte[] opad; + protected MessageDigest md5; + + HMACMD5(final byte[] input) throws NTLMEngineException { + byte[] key = input; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (final Exception ex) { + // Umm, the algorithm doesn't exist - throw an + // NTLMEngineException! + throw new NTLMEngineException( + "Error getting md5 message digest implementation: " + ex.getMessage(), ex); + } + + // Initialize the pad buffers with the key + ipad = new byte[64]; + opad = new byte[64]; + + int keyLength = key.length; + if (keyLength > 64) { + // Use MD5 of the key instead, as described in RFC 2104 + md5.update(key); + key = md5.digest(); + keyLength = key.length; + } + int i = 0; + while (i < keyLength) { + ipad[i] = (byte) (key[i] ^ (byte) 0x36); + opad[i] = (byte) (key[i] ^ (byte) 0x5c); + i++; + } + while (i < 64) { + ipad[i] = (byte) 0x36; + opad[i] = (byte) 0x5c; + i++; + } + + // Very important: update the digest with the ipad buffer + md5.reset(); + md5.update(ipad); + + } + + /** Grab the current digest. This is the "answer". */ + byte[] getOutput() { + final byte[] digest = md5.digest(); + md5.update(opad); + return md5.digest(digest); + } + + /** Update by adding a complete array */ + void update(final byte[] input) { + md5.update(input); + } + + /** Update the algorithm */ + void update(final byte[] input, final int offset, final int length) { + md5.update(input, offset, length); + } + + } + + public String generateType1Msg( + final String domain, + final String workstation) throws NTLMEngineException { + return getType1Message(workstation, domain); + } + + public String generateType3Msg( + final String username, + final String password, + final String domain, + final String workstation, + final String challenge) throws NTLMEngineException { + final Type2Message t2m = new Type2Message(challenge); + return getType3Message( + username, + password, + workstation, + domain, + t2m.getChallenge(), + t2m.getFlags(), + t2m.getTarget(), + t2m.getTargetInfo()); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMScheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMScheme.java new file mode 100644 index 000000000..68873954b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMScheme.java @@ -0,0 +1,164 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.InvalidCredentialsException; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.auth.NTCredentials; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * NTLM is a proprietary authentication scheme developed by Microsoft + * and optimized for Windows platforms. + * + * @since 4.0 + */ +@NotThreadSafe +public class NTLMScheme extends AuthSchemeBase { + + enum State { + UNINITIATED, + CHALLENGE_RECEIVED, + MSG_TYPE1_GENERATED, + MSG_TYPE2_RECEVIED, + MSG_TYPE3_GENERATED, + FAILED, + } + + private final NTLMEngine engine; + + private State state; + private String challenge; + + public NTLMScheme(final NTLMEngine engine) { + super(); + Args.notNull(engine, "NTLM engine"); + this.engine = engine; + this.state = State.UNINITIATED; + this.challenge = null; + } + + /** + * @since 4.3 + */ + public NTLMScheme() { + this(new NTLMEngineImpl()); + } + + public String getSchemeName() { + return "ntlm"; + } + + public String getParameter(final String name) { + // String parameters not supported + return null; + } + + public String getRealm() { + // NTLM does not support the concept of an authentication realm + return null; + } + + public boolean isConnectionBased() { + return true; + } + + @Override + protected void parseChallenge( + final CharArrayBuffer buffer, + final int beginIndex, final int endIndex) throws MalformedChallengeException { + this.challenge = buffer.substringTrimmed(beginIndex, endIndex); + if (this.challenge.length() == 0) { + if (this.state == State.UNINITIATED) { + this.state = State.CHALLENGE_RECEIVED; + } else { + this.state = State.FAILED; + } + } else { + if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) { + this.state = State.FAILED; + throw new MalformedChallengeException("Out of sequence NTLM response message"); + } else if (this.state == State.MSG_TYPE1_GENERATED) { + this.state = State.MSG_TYPE2_RECEVIED; + } + } + } + + public Header authenticate( + final Credentials credentials, + final HttpRequest request) throws AuthenticationException { + NTCredentials ntcredentials = null; + try { + ntcredentials = (NTCredentials) credentials; + } catch (final ClassCastException e) { + throw new InvalidCredentialsException( + "Credentials cannot be used for NTLM authentication: " + + credentials.getClass().getName()); + } + String response = null; + if (this.state == State.FAILED) { + throw new AuthenticationException("NTLM authentication failed"); + } else if (this.state == State.CHALLENGE_RECEIVED) { + response = this.engine.generateType1Msg( + ntcredentials.getDomain(), + ntcredentials.getWorkstation()); + this.state = State.MSG_TYPE1_GENERATED; + } else if (this.state == State.MSG_TYPE2_RECEVIED) { + response = this.engine.generateType3Msg( + ntcredentials.getUserName(), + ntcredentials.getPassword(), + ntcredentials.getDomain(), + ntcredentials.getWorkstation(), + this.challenge); + this.state = State.MSG_TYPE3_GENERATED; + } else { + throw new AuthenticationException("Unexpected state: " + this.state); + } + final CharArrayBuffer buffer = new CharArrayBuffer(32); + if (isProxy()) { + buffer.append(AUTH.PROXY_AUTH_RESP); + } else { + buffer.append(AUTH.WWW_AUTH_RESP); + } + buffer.append(": NTLM "); + buffer.append(response); + return new BufferedHeader(buffer); + } + + public boolean isComplete() { + return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMSchemeFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMSchemeFactory.java new file mode 100644 index 000000000..0484df643 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/NTLMSchemeFactory.java @@ -0,0 +1,56 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeFactory; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link AuthSchemeProvider} implementation that creates and initializes + * {@link NTLMScheme} instances configured to use the default {@link NTLMEngine} + * implementation. + * + * @since 4.1 + */ +@Immutable +@SuppressWarnings("deprecation") +public class NTLMSchemeFactory implements AuthSchemeFactory, AuthSchemeProvider { + + public AuthScheme newInstance(final HttpParams params) { + return new NTLMScheme(); + } + + public AuthScheme create(final HttpContext context) { + return new NTLMScheme(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/RFC2617Scheme.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/RFC2617Scheme.java new file mode 100644 index 000000000..93d1bf843 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/RFC2617Scheme.java @@ -0,0 +1,151 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.ChallengeState; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.auth.params.AuthPNames; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueParser; +import ch.boye.httpclientandroidlib.message.HeaderValueParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract authentication scheme class that lays foundation for all + * RFC 2617 compliant authentication schemes and provides capabilities common + * to all authentication schemes defined in RFC 2617. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe // AuthSchemeBase, params +public abstract class RFC2617Scheme extends AuthSchemeBase { + + private final Map params; + private final Charset credentialsCharset; + + /** + * Creates an instance of RFC2617Scheme with the given challenge + * state. + * + * @since 4.2 + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public RFC2617Scheme(final ChallengeState challengeState) { + super(challengeState); + this.params = new HashMap(); + this.credentialsCharset = Consts.ASCII; + } + + /** + * @since 4.3 + */ + public RFC2617Scheme(final Charset credentialsCharset) { + super(); + this.params = new HashMap(); + this.credentialsCharset = credentialsCharset != null ? credentialsCharset : Consts.ASCII; + } + + public RFC2617Scheme() { + this(Consts.ASCII); + } + + + /** + * @since 4.3 + */ + public Charset getCredentialsCharset() { + return credentialsCharset; + } + + String getCredentialsCharset(final HttpRequest request) { + String charset = (String) request.getParams().getParameter(AuthPNames.CREDENTIAL_CHARSET); + if (charset == null) { + charset = getCredentialsCharset().name(); + } + return charset; + } + + @Override + protected void parseChallenge( + final CharArrayBuffer buffer, final int pos, final int len) throws MalformedChallengeException { + final HeaderValueParser parser = BasicHeaderValueParser.INSTANCE; + final ParserCursor cursor = new ParserCursor(pos, buffer.length()); + final HeaderElement[] elements = parser.parseElements(buffer, cursor); + if (elements.length == 0) { + throw new MalformedChallengeException("Authentication challenge is empty"); + } + this.params.clear(); + for (final HeaderElement element : elements) { + this.params.put(element.getName().toLowerCase(Locale.ENGLISH), element.getValue()); + } + } + + /** + * Returns authentication parameters map. Keys in the map are lower-cased. + * + * @return the map of authentication parameters + */ + protected Map getParameters() { + return this.params; + } + + /** + * Returns authentication parameter with the given name, if available. + * + * @param name The name of the parameter to be returned + * + * @return the parameter with the given name + */ + public String getParameter(final String name) { + if (name == null) { + return null; + } + return this.params.get(name.toLowerCase(Locale.ENGLISH)); + } + + /** + * Returns authentication realm. The realm may not be null. + * + * @return the authentication realm + */ + public String getRealm() { + return getParameter("realm"); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java new file mode 100644 index 000000000..5ab0348ad --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import java.io.IOException; + +/** + * Abstract SPNEGO token generator. Implementations should take an Kerberos ticket and transform + * into a SPNEGO token. + *

    + * Implementations of this interface are expected to be thread-safe. + * + * @since 4.1 + * + * @deprecated (4.2) subclass {@link KerberosScheme} and override + * {@link KerberosScheme#generateGSSToken(byte[], org.ietf.jgss.Oid, String)} + */ +@Deprecated +public interface SpnegoTokenGenerator { + + byte [] generateSpnegoDERObject(byte [] kerberosTicket) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java new file mode 100644 index 000000000..71a6f892f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.auth; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Authentication credentials required to respond to a authentication + * challenge are invalid + * + * + * @since 4.0 + */ +@Immutable +public class UnsupportedDigestAlgorithmException extends RuntimeException { + + private static final long serialVersionUID = 319558534317118022L; + + /** + * Creates a new UnsupportedAuthAlgoritmException with a null detail message. + */ + public UnsupportedDigestAlgorithmException() { + super(); + } + + /** + * Creates a new UnsupportedAuthAlgoritmException with the specified message. + * + * @param message the exception detail message + */ + public UnsupportedDigestAlgorithmException(final String message) { + super(message); + } + + /** + * Creates a new UnsupportedAuthAlgoritmException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public UnsupportedDigestAlgorithmException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/package-info.java new file mode 100644 index 000000000..9e35c852d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/auth/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default implementations of standard and common HTTP authentication + * schemes. + */ +package ch.boye.httpclientandroidlib.impl.auth; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AIMDBackoffManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AIMDBackoffManager.java new file mode 100644 index 000000000..67798a683 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AIMDBackoffManager.java @@ -0,0 +1,164 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.HashMap; +import java.util.Map; + +import ch.boye.httpclientandroidlib.client.BackoffManager; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.pool.ConnPoolControl; +import ch.boye.httpclientandroidlib.util.Args; + +/** + *

    The AIMDBackoffManager applies an additive increase, + * multiplicative decrease (AIMD) to managing a dynamic limit to + * the number of connections allowed to a given host. You may want + * to experiment with the settings for the cooldown periods and the + * backoff factor to get the adaptive behavior you want.

    + * + *

    Generally speaking, shorter cooldowns will lead to more steady-state + * variability but faster reaction times, while longer cooldowns + * will lead to more stable equilibrium behavior but slower reaction + * times.

    + * + *

    Similarly, higher backoff factors promote greater + * utilization of available capacity at the expense of fairness + * among clients. Lower backoff factors allow equal distribution of + * capacity among clients (fairness) to happen faster, at the + * expense of having more server capacity unused in the short term.

    + * + * @since 4.2 + */ +public class AIMDBackoffManager implements BackoffManager { + + private final ConnPoolControl connPerRoute; + private final Clock clock; + private final Map lastRouteProbes; + private final Map lastRouteBackoffs; + private long coolDown = 5 * 1000L; + private double backoffFactor = 0.5; + private int cap = 2; // Per RFC 2616 sec 8.1.4 + + /** + * Creates an AIMDBackoffManager to manage + * per-host connection pool sizes represented by the + * given {@link ConnPoolControl}. + * @param connPerRoute per-host routing maximums to + * be managed + */ + public AIMDBackoffManager(final ConnPoolControl connPerRoute) { + this(connPerRoute, new SystemClock()); + } + + AIMDBackoffManager(final ConnPoolControl connPerRoute, final Clock clock) { + this.clock = clock; + this.connPerRoute = connPerRoute; + this.lastRouteProbes = new HashMap(); + this.lastRouteBackoffs = new HashMap(); + } + + public void backOff(final HttpRoute route) { + synchronized(connPerRoute) { + final int curr = connPerRoute.getMaxPerRoute(route); + final Long lastUpdate = getLastUpdate(lastRouteBackoffs, route); + final long now = clock.getCurrentTime(); + if (now - lastUpdate.longValue() < coolDown) { + return; + } + connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr)); + lastRouteBackoffs.put(route, Long.valueOf(now)); + } + } + + private int getBackedOffPoolSize(final int curr) { + if (curr <= 1) { + return 1; + } + return (int)(Math.floor(backoffFactor * curr)); + } + + public void probe(final HttpRoute route) { + synchronized(connPerRoute) { + final int curr = connPerRoute.getMaxPerRoute(route); + final int max = (curr >= cap) ? cap : curr + 1; + final Long lastProbe = getLastUpdate(lastRouteProbes, route); + final Long lastBackoff = getLastUpdate(lastRouteBackoffs, route); + final long now = clock.getCurrentTime(); + if (now - lastProbe.longValue() < coolDown || now - lastBackoff.longValue() < coolDown) { + return; + } + connPerRoute.setMaxPerRoute(route, max); + lastRouteProbes.put(route, Long.valueOf(now)); + } + } + + private Long getLastUpdate(final Map updates, final HttpRoute route) { + Long lastUpdate = updates.get(route); + if (lastUpdate == null) { + lastUpdate = Long.valueOf(0L); + } + return lastUpdate; + } + + /** + * Sets the factor to use when backing off; the new + * per-host limit will be roughly the current max times + * this factor. Math.floor is applied in the + * case of non-integer outcomes to ensure we actually + * decrease the pool size. Pool sizes are never decreased + * below 1, however. Defaults to 0.5. + * @param d must be between 0.0 and 1.0, exclusive. + */ + public void setBackoffFactor(final double d) { + Args.check(d > 0.0 && d < 1.0, "Backoff factor must be 0.0 < f < 1.0"); + backoffFactor = d; + } + + /** + * Sets the amount of time, in milliseconds, to wait between + * adjustments in pool sizes for a given host, to allow + * enough time for the adjustments to take effect. Defaults + * to 5000L (5 seconds). + * @param l must be positive + */ + public void setCooldownMillis(final long l) { + Args.positive(coolDown, "Cool down"); + coolDown = l; + } + + /** + * Sets the absolute maximum per-host connection pool size to + * probe up to; defaults to 2 (the default per-host max). + * @param cap must be >= 1 + */ + public void setPerHostConnectionCap(final int cap) { + Args.positive(cap, "Per host connection cap"); + this.cap = cap; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractAuthenticationHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractAuthenticationHandler.java new file mode 100644 index 000000000..a02c1a03b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractAuthenticationHandler.java @@ -0,0 +1,189 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.client.AuthenticationHandler; +import ch.boye.httpclientandroidlib.client.params.AuthPolicy; +import ch.boye.httpclientandroidlib.client.protocol.ClientContext; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Base class for {@link AuthenticationHandler} implementations. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.client.AuthenticationStrategy} + */ +@Deprecated +@Immutable +public abstract class AbstractAuthenticationHandler implements AuthenticationHandler { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private static final List DEFAULT_SCHEME_PRIORITY = + Collections.unmodifiableList(Arrays.asList(new String[] { + AuthPolicy.SPNEGO, + AuthPolicy.NTLM, + AuthPolicy.DIGEST, + AuthPolicy.BASIC + })); + + public AbstractAuthenticationHandler() { + super(); + } + + protected Map parseChallenges( + final Header[] headers) throws MalformedChallengeException { + + final Map map = new HashMap(headers.length); + for (final Header header : headers) { + final CharArrayBuffer buffer; + int pos; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + pos = ((FormattedHeader) header).getValuePos(); + } else { + final String s = header.getValue(); + if (s == null) { + throw new MalformedChallengeException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + pos = 0; + } + while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int beginIndex = pos; + while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int endIndex = pos; + final String s = buffer.substring(beginIndex, endIndex); + map.put(s.toLowerCase(Locale.ENGLISH), header); + } + return map; + } + + /** + * Returns default list of auth scheme names in their order of preference. + * + * @return list of auth scheme names + */ + protected List getAuthPreferences() { + return DEFAULT_SCHEME_PRIORITY; + } + + /** + * Returns default list of auth scheme names in their order of preference + * based on the HTTP response and the current execution context. + * + * @param response HTTP response. + * @param context HTTP execution context. + * + * @since 4.1 + */ + protected List getAuthPreferences( + final HttpResponse response, + final HttpContext context) { + return getAuthPreferences(); + } + + public AuthScheme selectScheme( + final Map challenges, + final HttpResponse response, + final HttpContext context) throws AuthenticationException { + + final AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute( + ClientContext.AUTHSCHEME_REGISTRY); + Asserts.notNull(registry, "AuthScheme registry"); + Collection authPrefs = getAuthPreferences(response, context); + if (authPrefs == null) { + authPrefs = DEFAULT_SCHEME_PRIORITY; + } + + if (this.log.isDebugEnabled()) { + this.log.debug("Authentication schemes in the order of preference: " + + authPrefs); + } + + AuthScheme authScheme = null; + for (final String id: authPrefs) { + final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH)); + + if (challenge != null) { + if (this.log.isDebugEnabled()) { + this.log.debug(id + " authentication scheme selected"); + } + try { + authScheme = registry.getAuthScheme(id, response.getParams()); + break; + } catch (final IllegalStateException e) { + if (this.log.isWarnEnabled()) { + this.log.warn("Authentication scheme " + id + " not supported"); + // Try again + } + } + } else { + if (this.log.isDebugEnabled()) { + this.log.debug("Challenge for " + id + " authentication scheme not available"); + // Try again + } + } + } + if (authScheme == null) { + // If none selected, something is wrong + throw new AuthenticationException( + "Unable to respond to any of these challenges: " + + challenges); + } + return authScheme; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractHttpClient.java new file mode 100644 index 000000000..f862c9886 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AbstractHttpClient.java @@ -0,0 +1,990 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry; +import ch.boye.httpclientandroidlib.client.AuthenticationHandler; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.client.BackoffManager; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler; +import ch.boye.httpclientandroidlib.client.RedirectHandler; +import ch.boye.httpclientandroidlib.client.RedirectStrategy; +import ch.boye.httpclientandroidlib.client.RequestDirector; +import ch.boye.httpclientandroidlib.client.UserTokenHandler; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.params.AuthPolicy; +import ch.boye.httpclientandroidlib.client.params.ClientPNames; +import ch.boye.httpclientandroidlib.client.params.CookiePolicy; +import ch.boye.httpclientandroidlib.client.params.HttpClientParamConfig; +import ch.boye.httpclientandroidlib.client.protocol.ClientContext; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManagerFactory; +import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.cookie.CookieSpecRegistry; +import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.auth.BasicSchemeFactory; +import ch.boye.httpclientandroidlib.impl.auth.DigestSchemeFactory; +/* KerberosSchemeFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.impl.auth.NTLMSchemeFactory; +/* SPNegoSchemeFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.impl.conn.BasicClientConnectionManager; +import ch.boye.httpclientandroidlib.impl.conn.DefaultHttpRoutePlanner; +import ch.boye.httpclientandroidlib.impl.conn.SchemeRegistryFactory; +import ch.boye.httpclientandroidlib.impl.cookie.BestMatchSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.BrowserCompatSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.IgnoreSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.NetscapeDraftSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.RFC2109SpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.RFC2965SpecFactory; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.BasicHttpProcessor; +import ch.boye.httpclientandroidlib.protocol.DefaultedHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpProcessor; +import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor; +import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Base class for {@link ch.boye.httpclientandroidlib.client.HttpClient} implementations. + * This class acts as a facade to a number of special purpose handler or + * strategy implementations responsible for handling of a particular aspect + * of the HTTP protocol such as redirect or authentication handling or + * making decision about connection persistence and keep alive duration. + * This enables the users to selectively replace default implementation + * of those aspects with custom, application specific ones. This class + * also provides factory methods to instantiate those objects: + *
      + *
    • {@link HttpRequestExecutor}
    • object used to transmit messages + * over HTTP connections. The {@link #createRequestExecutor()} must be + * implemented by concrete super classes to instantiate this object. + *
    • {@link BasicHttpProcessor}
    • object to manage a list of protocol + * interceptors and apply cross-cutting protocol logic to all incoming + * and outgoing HTTP messages. The {@link #createHttpProcessor()} must be + * implemented by concrete super classes to instantiate this object. + *
    • {@link HttpRequestRetryHandler}
    • object used to decide whether + * or not a failed HTTP request is safe to retry automatically. + * The {@link #createHttpRequestRetryHandler()} must be + * implemented by concrete super classes to instantiate this object. + *
    • {@link ClientConnectionManager}
    • object used to manage + * persistent HTTP connections. + *
    • {@link ConnectionReuseStrategy}
    • object used to decide whether + * or not a HTTP connection can be kept alive and re-used for subsequent + * HTTP requests. The {@link #createConnectionReuseStrategy()} must be + * implemented by concrete super classes to instantiate this object. + *
    • {@link ConnectionKeepAliveStrategy}
    • object used to decide how + * long a persistent HTTP connection can be kept alive. + * The {@link #createConnectionKeepAliveStrategy()} must be + * implemented by concrete super classes to instantiate this object. + *
    • {@link CookieSpecRegistry}
    • object used to maintain a list of + * supported cookie specifications. + * The {@link #createCookieSpecRegistry()} must be implemented by concrete + * super classes to instantiate this object. + *
    • {@link CookieStore}
    • object used to maintain a collection of + * cookies. The {@link #createCookieStore()} must be implemented by + * concrete super classes to instantiate this object. + *
    • {@link AuthSchemeRegistry}
    • object used to maintain a list of + * supported authentication schemes. + * The {@link #createAuthSchemeRegistry()} must be implemented by concrete + * super classes to instantiate this object. + *
    • {@link CredentialsProvider}
    • object used to maintain + * a collection user credentials. The {@link #createCredentialsProvider()} + * must be implemented by concrete super classes to instantiate + * this object. + *
    • {@link AuthenticationStrategy}
    • object used to authenticate + * against the target host. + * The {@link #createTargetAuthenticationStrategy()} must be implemented + * by concrete super classes to instantiate this object. + *
    • {@link AuthenticationStrategy}
    • object used to authenticate + * against the proxy host. + * The {@link #createProxyAuthenticationStrategy()} must be implemented + * by concrete super classes to instantiate this object. + *
    • {@link HttpRoutePlanner}
    • object used to calculate a route + * for establishing a connection to the target host. The route + * may involve multiple intermediate hops. + * The {@link #createHttpRoutePlanner()} must be implemented + * by concrete super classes to instantiate this object. + *
    • {@link RedirectStrategy}
    • object used to determine if an HTTP + * request should be redirected to a new location in response to an HTTP + * response received from the target server. + *
    • {@link UserTokenHandler}
    • object used to determine if the + * execution context is user identity specific. + * The {@link #createUserTokenHandler()} must be implemented by + * concrete super classes to instantiate this object. + *
    + *

    + * This class also maintains a list of protocol interceptors intended + * for processing outgoing requests and incoming responses and provides + * methods for managing those interceptors. New protocol interceptors can be + * introduced to the protocol processor chain or removed from it if needed. + * Internally protocol interceptors are stored in a simple + * {@link java.util.ArrayList}. They are executed in the same natural order + * as they are added to the list. + *

    + * AbstractHttpClient is thread safe. It is recommended that the same + * instance of this class is reused for multiple request executions. + * When an instance of DefaultHttpClient is no longer needed and is about + * to go out of scope the connection manager associated with it must be + * shut down by calling {@link ClientConnectionManager#shutdown()}! + * + * @since 4.0 + * + * @deprecated (4.3) use {@link HttpClientBuilder}. + */ +@ThreadSafe +@Deprecated +public abstract class AbstractHttpClient extends CloseableHttpClient { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** The parameters. */ + @GuardedBy("this") + private HttpParams defaultParams; + + /** The request executor. */ + @GuardedBy("this") + private HttpRequestExecutor requestExec; + + /** The connection manager. */ + @GuardedBy("this") + private ClientConnectionManager connManager; + + /** The connection re-use strategy. */ + @GuardedBy("this") + private ConnectionReuseStrategy reuseStrategy; + + /** The connection keep-alive strategy. */ + @GuardedBy("this") + private ConnectionKeepAliveStrategy keepAliveStrategy; + + /** The cookie spec registry. */ + @GuardedBy("this") + private CookieSpecRegistry supportedCookieSpecs; + + /** The authentication scheme registry. */ + @GuardedBy("this") + private AuthSchemeRegistry supportedAuthSchemes; + + /** The HTTP protocol processor and its immutable copy. */ + @GuardedBy("this") + private BasicHttpProcessor mutableProcessor; + + @GuardedBy("this") + private ImmutableHttpProcessor protocolProcessor; + + /** The request retry handler. */ + @GuardedBy("this") + private HttpRequestRetryHandler retryHandler; + + /** The redirect handler. */ + @GuardedBy("this") + private RedirectStrategy redirectStrategy; + + /** The target authentication handler. */ + @GuardedBy("this") + private AuthenticationStrategy targetAuthStrategy; + + /** The proxy authentication handler. */ + @GuardedBy("this") + private AuthenticationStrategy proxyAuthStrategy; + + /** The cookie store. */ + @GuardedBy("this") + private CookieStore cookieStore; + + /** The credentials provider. */ + @GuardedBy("this") + private CredentialsProvider credsProvider; + + /** The route planner. */ + @GuardedBy("this") + private HttpRoutePlanner routePlanner; + + /** The user token handler. */ + @GuardedBy("this") + private UserTokenHandler userTokenHandler; + + /** The connection backoff strategy. */ + @GuardedBy("this") + private ConnectionBackoffStrategy connectionBackoffStrategy; + + /** The backoff manager. */ + @GuardedBy("this") + private BackoffManager backoffManager; + + /** + * Creates a new HTTP client. + * + * @param conman the connection manager + * @param params the parameters + */ + protected AbstractHttpClient( + final ClientConnectionManager conman, + final HttpParams params) { + super(); + defaultParams = params; + connManager = conman; + } // constructor + + + protected abstract HttpParams createHttpParams(); + + + protected abstract BasicHttpProcessor createHttpProcessor(); + + + protected HttpContext createHttpContext() { + final HttpContext context = new BasicHttpContext(); + context.setAttribute( + ClientContext.SCHEME_REGISTRY, + getConnectionManager().getSchemeRegistry()); + context.setAttribute( + ClientContext.AUTHSCHEME_REGISTRY, + getAuthSchemes()); + context.setAttribute( + ClientContext.COOKIESPEC_REGISTRY, + getCookieSpecs()); + context.setAttribute( + ClientContext.COOKIE_STORE, + getCookieStore()); + context.setAttribute( + ClientContext.CREDS_PROVIDER, + getCredentialsProvider()); + return context; + } + + + protected ClientConnectionManager createClientConnectionManager() { + final SchemeRegistry registry = SchemeRegistryFactory.createDefault(); + + ClientConnectionManager connManager = null; + final HttpParams params = getParams(); + + ClientConnectionManagerFactory factory = null; + + final String className = (String) params.getParameter( + ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME); + if (className != null) { + try { + final Class clazz = Class.forName(className); + factory = (ClientConnectionManagerFactory) clazz.newInstance(); + } catch (final ClassNotFoundException ex) { + throw new IllegalStateException("Invalid class name: " + className); + } catch (final IllegalAccessException ex) { + throw new IllegalAccessError(ex.getMessage()); + } catch (final InstantiationException ex) { + throw new InstantiationError(ex.getMessage()); + } + } + if (factory != null) { + connManager = factory.newInstance(params, registry); + } else { + connManager = new BasicClientConnectionManager(registry); + } + + return connManager; + } + + + protected AuthSchemeRegistry createAuthSchemeRegistry() { + final AuthSchemeRegistry registry = new AuthSchemeRegistry(); + registry.register( + AuthPolicy.BASIC, + new BasicSchemeFactory()); + registry.register( + AuthPolicy.DIGEST, + new DigestSchemeFactory()); + registry.register( + AuthPolicy.NTLM, + new NTLMSchemeFactory()); + /* SPNegoSchemeFactory removed by HttpClient for Android script. */ + /* KerberosSchemeFactory removed by HttpClient for Android script. */ + return registry; + } + + + protected CookieSpecRegistry createCookieSpecRegistry() { + final CookieSpecRegistry registry = new CookieSpecRegistry(); + registry.register( + CookiePolicy.BEST_MATCH, + new BestMatchSpecFactory()); + registry.register( + CookiePolicy.BROWSER_COMPATIBILITY, + new BrowserCompatSpecFactory()); + registry.register( + CookiePolicy.NETSCAPE, + new NetscapeDraftSpecFactory()); + registry.register( + CookiePolicy.RFC_2109, + new RFC2109SpecFactory()); + registry.register( + CookiePolicy.RFC_2965, + new RFC2965SpecFactory()); + registry.register( + CookiePolicy.IGNORE_COOKIES, + new IgnoreSpecFactory()); + return registry; + } + + protected HttpRequestExecutor createRequestExecutor() { + return new HttpRequestExecutor(); + } + + protected ConnectionReuseStrategy createConnectionReuseStrategy() { + return new DefaultConnectionReuseStrategy(); + } + + protected ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() { + return new DefaultConnectionKeepAliveStrategy(); + } + + protected HttpRequestRetryHandler createHttpRequestRetryHandler() { + return new DefaultHttpRequestRetryHandler(); + } + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + protected RedirectHandler createRedirectHandler() { + return new DefaultRedirectHandler(); + } + + protected AuthenticationStrategy createTargetAuthenticationStrategy() { + return new TargetAuthenticationStrategy(); + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + protected AuthenticationHandler createTargetAuthenticationHandler() { + return new DefaultTargetAuthenticationHandler(); + } + + protected AuthenticationStrategy createProxyAuthenticationStrategy() { + return new ProxyAuthenticationStrategy(); + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + protected AuthenticationHandler createProxyAuthenticationHandler() { + return new DefaultProxyAuthenticationHandler(); + } + + protected CookieStore createCookieStore() { + return new BasicCookieStore(); + } + + protected CredentialsProvider createCredentialsProvider() { + return new BasicCredentialsProvider(); + } + + protected HttpRoutePlanner createHttpRoutePlanner() { + return new DefaultHttpRoutePlanner(getConnectionManager().getSchemeRegistry()); + } + + protected UserTokenHandler createUserTokenHandler() { + return new DefaultUserTokenHandler(); + } + + // non-javadoc, see interface HttpClient + public synchronized final HttpParams getParams() { + if (defaultParams == null) { + defaultParams = createHttpParams(); + } + return defaultParams; + } + + /** + * Replaces the parameters. + * The implementation here does not update parameters of dependent objects. + * + * @param params the new default parameters + */ + public synchronized void setParams(final HttpParams params) { + defaultParams = params; + } + + + public synchronized final ClientConnectionManager getConnectionManager() { + if (connManager == null) { + connManager = createClientConnectionManager(); + } + return connManager; + } + + + public synchronized final HttpRequestExecutor getRequestExecutor() { + if (requestExec == null) { + requestExec = createRequestExecutor(); + } + return requestExec; + } + + + public synchronized final AuthSchemeRegistry getAuthSchemes() { + if (supportedAuthSchemes == null) { + supportedAuthSchemes = createAuthSchemeRegistry(); + } + return supportedAuthSchemes; + } + + public synchronized void setAuthSchemes(final AuthSchemeRegistry registry) { + supportedAuthSchemes = registry; + } + + public synchronized final ConnectionBackoffStrategy getConnectionBackoffStrategy() { + return connectionBackoffStrategy; + } + + public synchronized void setConnectionBackoffStrategy(final ConnectionBackoffStrategy strategy) { + connectionBackoffStrategy = strategy; + } + + public synchronized final CookieSpecRegistry getCookieSpecs() { + if (supportedCookieSpecs == null) { + supportedCookieSpecs = createCookieSpecRegistry(); + } + return supportedCookieSpecs; + } + + public synchronized final BackoffManager getBackoffManager() { + return backoffManager; + } + + public synchronized void setBackoffManager(final BackoffManager manager) { + backoffManager = manager; + } + + public synchronized void setCookieSpecs(final CookieSpecRegistry registry) { + supportedCookieSpecs = registry; + } + + public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() { + if (reuseStrategy == null) { + reuseStrategy = createConnectionReuseStrategy(); + } + return reuseStrategy; + } + + + public synchronized void setReuseStrategy(final ConnectionReuseStrategy strategy) { + this.reuseStrategy = strategy; + } + + + public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() { + if (keepAliveStrategy == null) { + keepAliveStrategy = createConnectionKeepAliveStrategy(); + } + return keepAliveStrategy; + } + + + public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy strategy) { + this.keepAliveStrategy = strategy; + } + + + public synchronized final HttpRequestRetryHandler getHttpRequestRetryHandler() { + if (retryHandler == null) { + retryHandler = createHttpRequestRetryHandler(); + } + return retryHandler; + } + + public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler handler) { + this.retryHandler = handler; + } + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + public synchronized final RedirectHandler getRedirectHandler() { + return createRedirectHandler(); + } + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + public synchronized void setRedirectHandler(final RedirectHandler handler) { + this.redirectStrategy = new DefaultRedirectStrategyAdaptor(handler); + } + + /** + * @since 4.1 + */ + public synchronized final RedirectStrategy getRedirectStrategy() { + if (redirectStrategy == null) { + redirectStrategy = new DefaultRedirectStrategy(); + } + return redirectStrategy; + } + + /** + * @since 4.1 + */ + public synchronized void setRedirectStrategy(final RedirectStrategy strategy) { + this.redirectStrategy = strategy; + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + public synchronized final AuthenticationHandler getTargetAuthenticationHandler() { + return createTargetAuthenticationHandler(); + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + public synchronized void setTargetAuthenticationHandler(final AuthenticationHandler handler) { + this.targetAuthStrategy = new AuthenticationStrategyAdaptor(handler); + } + + /** + * @since 4.2 + */ + public synchronized final AuthenticationStrategy getTargetAuthenticationStrategy() { + if (targetAuthStrategy == null) { + targetAuthStrategy = createTargetAuthenticationStrategy(); + } + return targetAuthStrategy; + } + + /** + * @since 4.2 + */ + public synchronized void setTargetAuthenticationStrategy(final AuthenticationStrategy strategy) { + this.targetAuthStrategy = strategy; + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + public synchronized final AuthenticationHandler getProxyAuthenticationHandler() { + return createProxyAuthenticationHandler(); + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + public synchronized void setProxyAuthenticationHandler(final AuthenticationHandler handler) { + this.proxyAuthStrategy = new AuthenticationStrategyAdaptor(handler); + } + + /** + * @since 4.2 + */ + public synchronized final AuthenticationStrategy getProxyAuthenticationStrategy() { + if (proxyAuthStrategy == null) { + proxyAuthStrategy = createProxyAuthenticationStrategy(); + } + return proxyAuthStrategy; + } + + /** + * @since 4.2 + */ + public synchronized void setProxyAuthenticationStrategy(final AuthenticationStrategy strategy) { + this.proxyAuthStrategy = strategy; + } + + public synchronized final CookieStore getCookieStore() { + if (cookieStore == null) { + cookieStore = createCookieStore(); + } + return cookieStore; + } + + public synchronized void setCookieStore(final CookieStore cookieStore) { + this.cookieStore = cookieStore; + } + + public synchronized final CredentialsProvider getCredentialsProvider() { + if (credsProvider == null) { + credsProvider = createCredentialsProvider(); + } + return credsProvider; + } + + public synchronized void setCredentialsProvider(final CredentialsProvider credsProvider) { + this.credsProvider = credsProvider; + } + + public synchronized final HttpRoutePlanner getRoutePlanner() { + if (this.routePlanner == null) { + this.routePlanner = createHttpRoutePlanner(); + } + return this.routePlanner; + } + + public synchronized void setRoutePlanner(final HttpRoutePlanner routePlanner) { + this.routePlanner = routePlanner; + } + + public synchronized final UserTokenHandler getUserTokenHandler() { + if (this.userTokenHandler == null) { + this.userTokenHandler = createUserTokenHandler(); + } + return this.userTokenHandler; + } + + public synchronized void setUserTokenHandler(final UserTokenHandler handler) { + this.userTokenHandler = handler; + } + + protected synchronized final BasicHttpProcessor getHttpProcessor() { + if (mutableProcessor == null) { + mutableProcessor = createHttpProcessor(); + } + return mutableProcessor; + } + + private synchronized HttpProcessor getProtocolProcessor() { + if (protocolProcessor == null) { + // Get mutable HTTP processor + final BasicHttpProcessor proc = getHttpProcessor(); + // and create an immutable copy of it + final int reqc = proc.getRequestInterceptorCount(); + final HttpRequestInterceptor[] reqinterceptors = new HttpRequestInterceptor[reqc]; + for (int i = 0; i < reqc; i++) { + reqinterceptors[i] = proc.getRequestInterceptor(i); + } + final int resc = proc.getResponseInterceptorCount(); + final HttpResponseInterceptor[] resinterceptors = new HttpResponseInterceptor[resc]; + for (int i = 0; i < resc; i++) { + resinterceptors[i] = proc.getResponseInterceptor(i); + } + protocolProcessor = new ImmutableHttpProcessor(reqinterceptors, resinterceptors); + } + return protocolProcessor; + } + + public synchronized int getResponseInterceptorCount() { + return getHttpProcessor().getResponseInterceptorCount(); + } + + public synchronized HttpResponseInterceptor getResponseInterceptor(final int index) { + return getHttpProcessor().getResponseInterceptor(index); + } + + public synchronized HttpRequestInterceptor getRequestInterceptor(final int index) { + return getHttpProcessor().getRequestInterceptor(index); + } + + public synchronized int getRequestInterceptorCount() { + return getHttpProcessor().getRequestInterceptorCount(); + } + + public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) { + getHttpProcessor().addInterceptor(itcp); + protocolProcessor = null; + } + + public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, final int index) { + getHttpProcessor().addInterceptor(itcp, index); + protocolProcessor = null; + } + + public synchronized void clearResponseInterceptors() { + getHttpProcessor().clearResponseInterceptors(); + protocolProcessor = null; + } + + public synchronized void removeResponseInterceptorByClass(final Class clazz) { + getHttpProcessor().removeResponseInterceptorByClass(clazz); + protocolProcessor = null; + } + + public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) { + getHttpProcessor().addInterceptor(itcp); + protocolProcessor = null; + } + + public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, final int index) { + getHttpProcessor().addInterceptor(itcp, index); + protocolProcessor = null; + } + + public synchronized void clearRequestInterceptors() { + getHttpProcessor().clearRequestInterceptors(); + protocolProcessor = null; + } + + public synchronized void removeRequestInterceptorByClass(final Class clazz) { + getHttpProcessor().removeRequestInterceptorByClass(clazz); + protocolProcessor = null; + } + + @Override + protected final CloseableHttpResponse doExecute(final HttpHost target, final HttpRequest request, + final HttpContext context) + throws IOException, ClientProtocolException { + + Args.notNull(request, "HTTP request"); + // a null target may be acceptable, this depends on the route planner + // a null context is acceptable, default context created below + + HttpContext execContext = null; + RequestDirector director = null; + HttpRoutePlanner routePlanner = null; + ConnectionBackoffStrategy connectionBackoffStrategy = null; + BackoffManager backoffManager = null; + + // Initialize the request execution context making copies of + // all shared objects that are potentially threading unsafe. + synchronized (this) { + + final HttpContext defaultContext = createHttpContext(); + if (context == null) { + execContext = defaultContext; + } else { + execContext = new DefaultedHttpContext(context, defaultContext); + } + final HttpParams params = determineParams(request); + final RequestConfig config = HttpClientParamConfig.getRequestConfig(params); + execContext.setAttribute(ClientContext.REQUEST_CONFIG, config); + + // Create a director for this request + director = createClientRequestDirector( + getRequestExecutor(), + getConnectionManager(), + getConnectionReuseStrategy(), + getConnectionKeepAliveStrategy(), + getRoutePlanner(), + getProtocolProcessor(), + getHttpRequestRetryHandler(), + getRedirectStrategy(), + getTargetAuthenticationStrategy(), + getProxyAuthenticationStrategy(), + getUserTokenHandler(), + params); + routePlanner = getRoutePlanner(); + connectionBackoffStrategy = getConnectionBackoffStrategy(); + backoffManager = getBackoffManager(); + } + + try { + if (connectionBackoffStrategy != null && backoffManager != null) { + final HttpHost targetForRoute = (target != null) ? target + : (HttpHost) determineParams(request).getParameter( + ClientPNames.DEFAULT_HOST); + final HttpRoute route = routePlanner.determineRoute(targetForRoute, request, execContext); + + final CloseableHttpResponse out; + try { + out = CloseableHttpResponseProxy.newProxy( + director.execute(target, request, execContext)); + } catch (final RuntimeException re) { + if (connectionBackoffStrategy.shouldBackoff(re)) { + backoffManager.backOff(route); + } + throw re; + } catch (final Exception e) { + if (connectionBackoffStrategy.shouldBackoff(e)) { + backoffManager.backOff(route); + } + if (e instanceof HttpException) { + throw (HttpException)e; + } + if (e instanceof IOException) { + throw (IOException)e; + } + throw new UndeclaredThrowableException(e); + } + if (connectionBackoffStrategy.shouldBackoff(out)) { + backoffManager.backOff(route); + } else { + backoffManager.probe(route); + } + return out; + } else { + return CloseableHttpResponseProxy.newProxy( + director.execute(target, request, execContext)); + } + } catch(final HttpException httpException) { + throw new ClientProtocolException(httpException); + } + } + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + protected RequestDirector createClientRequestDirector( + final HttpRequestExecutor requestExec, + final ClientConnectionManager conman, + final ConnectionReuseStrategy reustrat, + final ConnectionKeepAliveStrategy kastrat, + final HttpRoutePlanner rouplan, + final HttpProcessor httpProcessor, + final HttpRequestRetryHandler retryHandler, + final RedirectHandler redirectHandler, + final AuthenticationHandler targetAuthHandler, + final AuthenticationHandler proxyAuthHandler, + final UserTokenHandler userTokenHandler, + final HttpParams params) { + return new DefaultRequestDirector( + requestExec, + conman, + reustrat, + kastrat, + rouplan, + httpProcessor, + retryHandler, + redirectHandler, + targetAuthHandler, + proxyAuthHandler, + userTokenHandler, + params); + } + + /** + * @deprecated (4.2) do not use + */ + @Deprecated + protected RequestDirector createClientRequestDirector( + final HttpRequestExecutor requestExec, + final ClientConnectionManager conman, + final ConnectionReuseStrategy reustrat, + final ConnectionKeepAliveStrategy kastrat, + final HttpRoutePlanner rouplan, + final HttpProcessor httpProcessor, + final HttpRequestRetryHandler retryHandler, + final RedirectStrategy redirectStrategy, + final AuthenticationHandler targetAuthHandler, + final AuthenticationHandler proxyAuthHandler, + final UserTokenHandler userTokenHandler, + final HttpParams params) { + return new DefaultRequestDirector( + log, + requestExec, + conman, + reustrat, + kastrat, + rouplan, + httpProcessor, + retryHandler, + redirectStrategy, + targetAuthHandler, + proxyAuthHandler, + userTokenHandler, + params); + } + + + /** + * @since 4.2 + */ + protected RequestDirector createClientRequestDirector( + final HttpRequestExecutor requestExec, + final ClientConnectionManager conman, + final ConnectionReuseStrategy reustrat, + final ConnectionKeepAliveStrategy kastrat, + final HttpRoutePlanner rouplan, + final HttpProcessor httpProcessor, + final HttpRequestRetryHandler retryHandler, + final RedirectStrategy redirectStrategy, + final AuthenticationStrategy targetAuthStrategy, + final AuthenticationStrategy proxyAuthStrategy, + final UserTokenHandler userTokenHandler, + final HttpParams params) { + return new DefaultRequestDirector( + log, + requestExec, + conman, + reustrat, + kastrat, + rouplan, + httpProcessor, + retryHandler, + redirectStrategy, + targetAuthStrategy, + proxyAuthStrategy, + userTokenHandler, + params); + } + + /** + * Obtains parameters for executing a request. + * The default implementation in this class creates a new + * {@link ClientParamsStack} from the request parameters + * and the client parameters. + *
    + * This method is called by the default implementation of + * {@link #execute(HttpHost,HttpRequest,HttpContext)} + * to obtain the parameters for the + * {@link DefaultRequestDirector}. + * + * @param req the request that will be executed + * + * @return the parameters to use + */ + protected HttpParams determineParams(final HttpRequest req) { + return new ClientParamsStack + (null, getParams(), req.getParams(), null); + } + + + public void close() { + getConnectionManager().shutdown(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyAdaptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyAdaptor.java new file mode 100644 index 000000000..a65ce7e95 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyAdaptor.java @@ -0,0 +1,172 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.LinkedList; +import java.util.Locale; +import java.util.Map; +import java.util.Queue; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthOption; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.AuthenticationException; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.client.AuthCache; +import ch.boye.httpclientandroidlib.client.AuthenticationHandler; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.params.AuthPolicy; +import ch.boye.httpclientandroidlib.client.protocol.ClientContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * @deprecated (4.2) do not use + */ +@Immutable +@Deprecated +class AuthenticationStrategyAdaptor implements AuthenticationStrategy { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final AuthenticationHandler handler; + + public AuthenticationStrategyAdaptor(final AuthenticationHandler handler) { + super(); + this.handler = handler; + } + + public boolean isAuthenticationRequested( + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) { + return this.handler.isAuthenticationRequested(response, context); + } + + public Map getChallenges( + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + return this.handler.getChallenges(response, context); + } + + public Queue select( + final Map challenges, + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + Args.notNull(challenges, "Map of auth challenges"); + Args.notNull(authhost, "Host"); + Args.notNull(response, "HTTP response"); + Args.notNull(context, "HTTP context"); + + final Queue options = new LinkedList(); + final CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute( + ClientContext.CREDS_PROVIDER); + if (credsProvider == null) { + this.log.debug("Credentials provider not set in the context"); + return options; + } + + final AuthScheme authScheme; + try { + authScheme = this.handler.selectScheme(challenges, response, context); + } catch (final AuthenticationException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn(ex.getMessage(), ex); + } + return options; + } + final String id = authScheme.getSchemeName(); + final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH)); + authScheme.processChallenge(challenge); + + final AuthScope authScope = new AuthScope( + authhost.getHostName(), + authhost.getPort(), + authScheme.getRealm(), + authScheme.getSchemeName()); + + final Credentials credentials = credsProvider.getCredentials(authScope); + if (credentials != null) { + options.add(new AuthOption(authScheme, credentials)); + } + return options; + } + + public void authSucceeded( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + if (isCachable(authScheme)) { + if (authCache == null) { + authCache = new BasicAuthCache(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Caching '" + authScheme.getSchemeName() + + "' auth scheme for " + authhost); + } + authCache.put(authhost, authScheme); + } + } + + public void authFailed( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + final AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + if (authCache == null) { + return; + } + if (this.log.isDebugEnabled()) { + this.log.debug("Removing from cache '" + authScheme.getSchemeName() + + "' auth scheme for " + authhost); + } + authCache.remove(authhost); + } + + private boolean isCachable(final AuthScheme authScheme) { + if (authScheme == null || !authScheme.isComplete()) { + return false; + } + final String schemeName = authScheme.getSchemeName(); + return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) || + schemeName.equalsIgnoreCase(AuthPolicy.DIGEST); + } + + public AuthenticationHandler getHandler() { + return this.handler; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyImpl.java new file mode 100644 index 000000000..38b7df6d2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AuthenticationStrategyImpl.java @@ -0,0 +1,245 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Queue; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthOption; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.client.AuthCache; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.config.AuthSchemes; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +@Immutable +abstract class AuthenticationStrategyImpl implements AuthenticationStrategy { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private static final List DEFAULT_SCHEME_PRIORITY = + Collections.unmodifiableList(Arrays.asList(AuthSchemes.SPNEGO, + AuthSchemes.KERBEROS, + AuthSchemes.NTLM, + AuthSchemes.DIGEST, + AuthSchemes.BASIC)); + + private final int challengeCode; + private final String headerName; + + AuthenticationStrategyImpl(final int challengeCode, final String headerName) { + super(); + this.challengeCode = challengeCode; + this.headerName = headerName; + } + + public boolean isAuthenticationRequested( + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) { + Args.notNull(response, "HTTP response"); + final int status = response.getStatusLine().getStatusCode(); + return status == this.challengeCode; + } + + public Map getChallenges( + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + Args.notNull(response, "HTTP response"); + final Header[] headers = response.getHeaders(this.headerName); + final Map map = new HashMap(headers.length); + for (final Header header : headers) { + final CharArrayBuffer buffer; + int pos; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + pos = ((FormattedHeader) header).getValuePos(); + } else { + final String s = header.getValue(); + if (s == null) { + throw new MalformedChallengeException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + pos = 0; + } + while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int beginIndex = pos; + while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + final int endIndex = pos; + final String s = buffer.substring(beginIndex, endIndex); + map.put(s.toLowerCase(Locale.ENGLISH), header); + } + return map; + } + + abstract Collection getPreferredAuthSchemes(RequestConfig config); + + public Queue select( + final Map challenges, + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + Args.notNull(challenges, "Map of auth challenges"); + Args.notNull(authhost, "Host"); + Args.notNull(response, "HTTP response"); + Args.notNull(context, "HTTP context"); + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + final Queue options = new LinkedList(); + final Lookup registry = clientContext.getAuthSchemeRegistry(); + if (registry == null) { + this.log.debug("Auth scheme registry not set in the context"); + return options; + } + final CredentialsProvider credsProvider = clientContext.getCredentialsProvider(); + if (credsProvider == null) { + this.log.debug("Credentials provider not set in the context"); + return options; + } + final RequestConfig config = clientContext.getRequestConfig(); + Collection authPrefs = getPreferredAuthSchemes(config); + if (authPrefs == null) { + authPrefs = DEFAULT_SCHEME_PRIORITY; + } + if (this.log.isDebugEnabled()) { + this.log.debug("Authentication schemes in the order of preference: " + authPrefs); + } + + for (final String id: authPrefs) { + final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH)); + if (challenge != null) { + final AuthSchemeProvider authSchemeProvider = registry.lookup(id); + if (authSchemeProvider == null) { + if (this.log.isWarnEnabled()) { + this.log.warn("Authentication scheme " + id + " not supported"); + // Try again + } + continue; + } + final AuthScheme authScheme = authSchemeProvider.create(context); + authScheme.processChallenge(challenge); + + final AuthScope authScope = new AuthScope( + authhost.getHostName(), + authhost.getPort(), + authScheme.getRealm(), + authScheme.getSchemeName()); + + final Credentials credentials = credsProvider.getCredentials(authScope); + if (credentials != null) { + options.add(new AuthOption(authScheme, credentials)); + } + } else { + if (this.log.isDebugEnabled()) { + this.log.debug("Challenge for " + id + " authentication scheme not available"); + // Try again + } + } + } + return options; + } + + public void authSucceeded( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + Args.notNull(authhost, "Host"); + Args.notNull(authScheme, "Auth scheme"); + Args.notNull(context, "HTTP context"); + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + if (isCachable(authScheme)) { + AuthCache authCache = clientContext.getAuthCache(); + if (authCache == null) { + authCache = new BasicAuthCache(); + clientContext.setAuthCache(authCache); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Caching '" + authScheme.getSchemeName() + + "' auth scheme for " + authhost); + } + authCache.put(authhost, authScheme); + } + } + + protected boolean isCachable(final AuthScheme authScheme) { + if (authScheme == null || !authScheme.isComplete()) { + return false; + } + final String schemeName = authScheme.getSchemeName(); + return schemeName.equalsIgnoreCase(AuthSchemes.BASIC) || + schemeName.equalsIgnoreCase(AuthSchemes.DIGEST); + } + + public void authFailed( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + Args.notNull(authhost, "Host"); + Args.notNull(context, "HTTP context"); + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + final AuthCache authCache = clientContext.getAuthCache(); + if (authCache != null) { + if (this.log.isDebugEnabled()) { + this.log.debug("Clearing cached auth scheme for " + authhost); + } + authCache.remove(authhost); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AutoRetryHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AutoRetryHttpClient.java new file mode 100644 index 000000000..70c0e3404 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/AutoRetryHttpClient.java @@ -0,0 +1,190 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.ResponseHandler; +import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * {@link HttpClient} implementation that can automatically retry the request in case of + * a non-2xx response using the {@link ServiceUnavailableRetryStrategy} interface. + * + * @since 4.2 + * + * @deprecated (4.3) use {@link HttpClientBuilder}. + */ +@Deprecated +@ThreadSafe +public class AutoRetryHttpClient implements HttpClient { + + private final HttpClient backend; + + private final ServiceUnavailableRetryStrategy retryStrategy; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public AutoRetryHttpClient( + final HttpClient client, final ServiceUnavailableRetryStrategy retryStrategy) { + super(); + Args.notNull(client, "HttpClient"); + Args.notNull(retryStrategy, "ServiceUnavailableRetryStrategy"); + this.backend = client; + this.retryStrategy = retryStrategy; + } + + /** + * Constructs a {@code AutoRetryHttpClient} with default caching settings that + * stores cache entries in memory and uses a vanilla + * {@link DefaultHttpClient} for backend requests. + */ + public AutoRetryHttpClient() { + this(new DefaultHttpClient(), new DefaultServiceUnavailableRetryStrategy()); + } + + /** + * Constructs a {@code AutoRetryHttpClient} with the given caching options that + * stores cache entries in memory and uses a vanilla + * {@link DefaultHttpClient} for backend requests. + * + * @param config + * retry configuration module options + */ + public AutoRetryHttpClient(final ServiceUnavailableRetryStrategy config) { + this(new DefaultHttpClient(), config); + } + + /** + * Constructs a {@code AutoRetryHttpClient} with default caching settings that + * stores cache entries in memory and uses the given {@link HttpClient} for + * backend requests. + * + * @param client + * used to make origin requests + */ + public AutoRetryHttpClient(final HttpClient client) { + this(client, new DefaultServiceUnavailableRetryStrategy()); + } + + public HttpResponse execute(final HttpHost target, final HttpRequest request) + throws IOException { + final HttpContext defaultContext = null; + return execute(target, request, defaultContext); + } + + public T execute(final HttpHost target, final HttpRequest request, + final ResponseHandler responseHandler) throws IOException { + return execute(target, request, responseHandler, null); + } + + public T execute(final HttpHost target, final HttpRequest request, + final ResponseHandler responseHandler, final HttpContext context) + throws IOException { + final HttpResponse resp = execute(target, request, context); + return responseHandler.handleResponse(resp); + } + + public HttpResponse execute(final HttpUriRequest request) throws IOException { + final HttpContext context = null; + return execute(request, context); + } + + public HttpResponse execute(final HttpUriRequest request, final HttpContext context) + throws IOException { + final URI uri = request.getURI(); + final HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(), + uri.getScheme()); + return execute(httpHost, request, context); + } + + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler) throws IOException { + return execute(request, responseHandler, null); + } + + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler, final HttpContext context) + throws IOException { + final HttpResponse resp = execute(request, context); + return responseHandler.handleResponse(resp); + } + + public HttpResponse execute(final HttpHost target, final HttpRequest request, + final HttpContext context) throws IOException { + for (int c = 1;; c++) { + final HttpResponse response = backend.execute(target, request, context); + try { + if (retryStrategy.retryRequest(response, c, context)) { + EntityUtils.consume(response.getEntity()); + final long nextInterval = retryStrategy.getRetryInterval(); + try { + log.trace("Wait for " + nextInterval); + Thread.sleep(nextInterval); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new InterruptedIOException(); + } + } else { + return response; + } + } catch (final RuntimeException ex) { + try { + EntityUtils.consume(response.getEntity()); + } catch (final IOException ioex) { + log.warn("I/O error consuming response content", ioex); + } + throw ex; + } + } + } + + public ClientConnectionManager getConnectionManager() { + return backend.getConnectionManager(); + } + + public HttpParams getParams() { + return backend.getParams(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicAuthCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicAuthCache.java new file mode 100644 index 000000000..810f73a0a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicAuthCache.java @@ -0,0 +1,105 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.HashMap; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.client.AuthCache; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException; +import ch.boye.httpclientandroidlib.impl.conn.DefaultSchemePortResolver; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of {@link AuthCache}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicAuthCache implements AuthCache { + + private final HashMap map; + private final SchemePortResolver schemePortResolver; + + /** + * Default constructor. + * + * @since 4.3 + */ + public BasicAuthCache(final SchemePortResolver schemePortResolver) { + super(); + this.map = new HashMap(); + this.schemePortResolver = schemePortResolver != null ? schemePortResolver : + DefaultSchemePortResolver.INSTANCE; + } + + public BasicAuthCache() { + this(null); + } + + protected HttpHost getKey(final HttpHost host) { + if (host.getPort() <= 0) { + final int port; + try { + port = schemePortResolver.resolve(host); + } catch (final UnsupportedSchemeException ignore) { + return host; + } + return new HttpHost(host.getHostName(), port, host.getSchemeName()); + } else { + return host; + } + } + + public void put(final HttpHost host, final AuthScheme authScheme) { + Args.notNull(host, "HTTP host"); + this.map.put(getKey(host), authScheme); + } + + public AuthScheme get(final HttpHost host) { + Args.notNull(host, "HTTP host"); + return this.map.get(getKey(host)); + } + + public void remove(final HttpHost host) { + Args.notNull(host, "HTTP host"); + this.map.remove(getKey(host)); + } + + public void clear() { + this.map.clear(); + } + + @Override + public String toString() { + return this.map.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCookieStore.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCookieStore.java new file mode 100644 index 000000000..624a8f81d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCookieStore.java @@ -0,0 +1,144 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.TreeSet; + +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieIdentityComparator; + +/** + * Default implementation of {@link CookieStore} + * + * + * @since 4.0 + */ +@ThreadSafe +public class BasicCookieStore implements CookieStore, Serializable { + + private static final long serialVersionUID = -7581093305228232025L; + + @GuardedBy("this") + private final TreeSet cookies; + + public BasicCookieStore() { + super(); + this.cookies = new TreeSet(new CookieIdentityComparator()); + } + + /** + * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies. + * If the given cookie has already expired it will not be added, but existing + * values will still be removed. + * + * @param cookie the {@link Cookie cookie} to be added + * + * @see #addCookies(Cookie[]) + * + */ + public synchronized void addCookie(final Cookie cookie) { + if (cookie != null) { + // first remove any old cookie that is equivalent + cookies.remove(cookie); + if (!cookie.isExpired(new Date())) { + cookies.add(cookie); + } + } + } + + /** + * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and + * in the given array order. If any of the given cookies has already expired it will + * not be added, but existing values will still be removed. + * + * @param cookies the {@link Cookie cookies} to be added + * + * @see #addCookie(Cookie) + * + */ + public synchronized void addCookies(final Cookie[] cookies) { + if (cookies != null) { + for (final Cookie cooky : cookies) { + this.addCookie(cooky); + } + } + } + + /** + * Returns an immutable array of {@link Cookie cookies} that this HTTP + * state currently contains. + * + * @return an array of {@link Cookie cookies}. + */ + public synchronized List getCookies() { + //create defensive copy so it won't be concurrently modified + return new ArrayList(cookies); + } + + /** + * Removes all of {@link Cookie cookies} in this HTTP state + * that have expired by the specified {@link java.util.Date date}. + * + * @return true if any cookies were purged. + * + * @see Cookie#isExpired(Date) + */ + public synchronized boolean clearExpired(final Date date) { + if (date == null) { + return false; + } + boolean removed = false; + for (final Iterator it = cookies.iterator(); it.hasNext();) { + if (it.next().isExpired(date)) { + it.remove(); + removed = true; + } + } + return removed; + } + + /** + * Clears all cookies. + */ + public synchronized void clear() { + cookies.clear(); + } + + @Override + public synchronized String toString() { + return cookies.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCredentialsProvider.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCredentialsProvider.java new file mode 100644 index 000000000..99c5cccc2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicCredentialsProvider.java @@ -0,0 +1,109 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of {@link CredentialsProvider}. + * + * @since 4.0 + */ +@ThreadSafe +public class BasicCredentialsProvider implements CredentialsProvider { + + private final ConcurrentHashMap credMap; + + /** + * Default constructor. + */ + public BasicCredentialsProvider() { + super(); + this.credMap = new ConcurrentHashMap(); + } + + public void setCredentials( + final AuthScope authscope, + final Credentials credentials) { + Args.notNull(authscope, "Authentication scope"); + credMap.put(authscope, credentials); + } + + /** + * Find matching {@link Credentials credentials} for the given authentication scope. + * + * @param map the credentials hash map + * @param authscope the {@link AuthScope authentication scope} + * @return the credentials + * + */ + private static Credentials matchCredentials( + final Map map, + final AuthScope authscope) { + // see if we get a direct hit + Credentials creds = map.get(authscope); + if (creds == null) { + // Nope. + // Do a full scan + int bestMatchFactor = -1; + AuthScope bestMatch = null; + for (final AuthScope current: map.keySet()) { + final int factor = authscope.match(current); + if (factor > bestMatchFactor) { + bestMatchFactor = factor; + bestMatch = current; + } + } + if (bestMatch != null) { + creds = map.get(bestMatch); + } + } + return creds; + } + + public Credentials getCredentials(final AuthScope authscope) { + Args.notNull(authscope, "Authentication scope"); + return matchCredentials(this.credMap, authscope); + } + + public void clear() { + this.credMap.clear(); + } + + @Override + public String toString() { + return credMap.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicResponseHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicResponseHandler.java new file mode 100644 index 000000000..13ae4f856 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/BasicResponseHandler.java @@ -0,0 +1,73 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.HttpResponseException; +import ch.boye.httpclientandroidlib.client.ResponseHandler; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * A {@link ResponseHandler} that returns the response body as a String + * for successful (2xx) responses. If the response code was >= 300, the response + * body is consumed and an {@link HttpResponseException} is thrown. + *

    + * If this is used with + * {@link ch.boye.httpclientandroidlib.client.HttpClient#execute( + * ch.boye.httpclientandroidlib.client.methods.HttpUriRequest, ResponseHandler)}, + * HttpClient may handle redirects (3xx responses) internally. + * + * @since 4.0 + */ +@Immutable +public class BasicResponseHandler implements ResponseHandler { + + /** + * Returns the response body as a String if the response was successful (a + * 2xx status code). If no response body exists, this returns null. If the + * response was unsuccessful (>= 300 status code), throws an + * {@link HttpResponseException}. + */ + public String handleResponse(final HttpResponse response) + throws HttpResponseException, IOException { + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity entity = response.getEntity(); + if (statusLine.getStatusCode() >= 300) { + EntityUtils.consume(entity); + throw new HttpResponseException(statusLine.getStatusCode(), + statusLine.getReasonPhrase()); + } + return entity == null ? null : EntityUtils.toString(entity); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ClientParamsStack.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ClientParamsStack.java new file mode 100644 index 000000000..37f5daedd --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ClientParamsStack.java @@ -0,0 +1,269 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.AbstractHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Represents a stack of parameter collections. + * When retrieving a parameter, the stack is searched in a fixed order + * and the first match returned. Setting parameters via the stack is + * not supported. To minimize overhead, the stack has a fixed size and + * does not maintain an internal array. + * The supported stack entries, sorted by increasing priority, are: + *

      + *
    1. Application parameters: + * expected to be the same for all clients used by an application. + * These provide "global", that is application-wide, defaults. + *
    2. + *
    3. Client parameters: + * specific to an instance of + * {@link ch.boye.httpclientandroidlib.client.HttpClient HttpClient}. + * These provide client specific defaults. + *
    4. + *
    5. Request parameters: + * specific to a single request execution. + * For overriding client and global defaults. + *
    6. + *
    7. Override parameters: + * specific to an instance of + * {@link ch.boye.httpclientandroidlib.client.HttpClient HttpClient}. + * These can be used to set parameters that cannot be overridden + * on a per-request basis. + *
    8. + *
    + * Each stack entry may be null. That is preferable over + * an empty params collection, since it avoids searching the empty collection + * when looking up parameters. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@NotThreadSafe +@Deprecated +public class ClientParamsStack extends AbstractHttpParams { + + /** The application parameter collection, or null. */ + protected final HttpParams applicationParams; + + /** The client parameter collection, or null. */ + protected final HttpParams clientParams; + + /** The request parameter collection, or null. */ + protected final HttpParams requestParams; + + /** The override parameter collection, or null. */ + protected final HttpParams overrideParams; + + + /** + * Creates a new parameter stack from elements. + * The arguments will be stored as-is, there is no copying to + * prevent modification. + * + * @param aparams application parameters, or null + * @param cparams client parameters, or null + * @param rparams request parameters, or null + * @param oparams override parameters, or null + */ + public ClientParamsStack(final HttpParams aparams, final HttpParams cparams, + final HttpParams rparams, final HttpParams oparams) { + applicationParams = aparams; + clientParams = cparams; + requestParams = rparams; + overrideParams = oparams; + } + + + /** + * Creates a copy of a parameter stack. + * The new stack will have the exact same entries as the argument stack. + * There is no copying of parameters. + * + * @param stack the stack to copy + */ + public ClientParamsStack(final ClientParamsStack stack) { + this(stack.getApplicationParams(), + stack.getClientParams(), + stack.getRequestParams(), + stack.getOverrideParams()); + } + + + /** + * Creates a modified copy of a parameter stack. + * The new stack will contain the explicitly passed elements. + * For elements where the explicit argument is null, + * the corresponding element from the argument stack is used. + * There is no copying of parameters. + * + * @param stack the stack to modify + * @param aparams application parameters, or null + * @param cparams client parameters, or null + * @param rparams request parameters, or null + * @param oparams override parameters, or null + */ + public ClientParamsStack(final ClientParamsStack stack, + final HttpParams aparams, final HttpParams cparams, + final HttpParams rparams, final HttpParams oparams) { + this((aparams != null) ? aparams : stack.getApplicationParams(), + (cparams != null) ? cparams : stack.getClientParams(), + (rparams != null) ? rparams : stack.getRequestParams(), + (oparams != null) ? oparams : stack.getOverrideParams()); + } + + + /** + * Obtains the application parameters of this stack. + * + * @return the application parameters, or null + */ + public final HttpParams getApplicationParams() { + return applicationParams; + } + + /** + * Obtains the client parameters of this stack. + * + * @return the client parameters, or null + */ + public final HttpParams getClientParams() { + return clientParams; + } + + /** + * Obtains the request parameters of this stack. + * + * @return the request parameters, or null + */ + public final HttpParams getRequestParams() { + return requestParams; + } + + /** + * Obtains the override parameters of this stack. + * + * @return the override parameters, or null + */ + public final HttpParams getOverrideParams() { + return overrideParams; + } + + + /** + * Obtains a parameter from this stack. + * See class comment for search order. + * + * @param name the name of the parameter to obtain + * + * @return the highest-priority value for that parameter, or + * null if it is not set anywhere in this stack + */ + public Object getParameter(final String name) { + Args.notNull(name, "Parameter name"); + + Object result = null; + + if (overrideParams != null) { + result = overrideParams.getParameter(name); + } + if ((result == null) && (requestParams != null)) { + result = requestParams.getParameter(name); + } + if ((result == null) && (clientParams != null)) { + result = clientParams.getParameter(name); + } + if ((result == null) && (applicationParams != null)) { + result = applicationParams.getParameter(name); + } + return result; + } + + /** + * Does not set a parameter. + * Parameter stacks are read-only. It is possible, though discouraged, + * to access and modify specific stack entries. + * Derived classes may change this behavior. + * + * @param name ignored + * @param value ignored + * + * @return nothing + * + * @throws UnsupportedOperationException always + */ + public HttpParams setParameter(final String name, final Object value) + throws UnsupportedOperationException { + + throw new UnsupportedOperationException + ("Setting parameters in a stack is not supported."); + } + + + /** + * Does not remove a parameter. + * Parameter stacks are read-only. It is possible, though discouraged, + * to access and modify specific stack entries. + * Derived classes may change this behavior. + * + * @param name ignored + * + * @return nothing + * + * @throws UnsupportedOperationException always + */ + public boolean removeParameter(final String name) { + throw new UnsupportedOperationException + ("Removing parameters in a stack is not supported."); + } + + + /** + * Does not copy parameters. + * Parameter stacks are lightweight objects, expected to be instantiated + * as needed and to be used only in a very specific context. On top of + * that, they are read-only. The typical copy operation to prevent + * accidental modification of parameters passed by the application to + * a framework object is therefore pointless and disabled. + * Create a new stack if you really need a copy. + *
    + * Derived classes may change this behavior. + * + * @return this parameter stack + */ + public HttpParams copy() { + return this; + } + + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/Clock.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/Clock.java new file mode 100644 index 000000000..14eeec4a0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/Clock.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +/** + * Interface used to enable easier testing of time-related behavior. + * + * @since 4.2 + * + */ +interface Clock { + + /** + * Returns the current time, expressed as the number of + * milliseconds since the epoch. + * @return current time + */ + long getCurrentTime(); +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpClient.java new file mode 100644 index 000000000..c2fc5a5de --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpClient.java @@ -0,0 +1,244 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; +import java.net.URI; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.ResponseHandler; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * Base implementation of {@link HttpClient} that also implements {@link Closeable}. + * + * @since 4.3 + */ +@ThreadSafe +public abstract class CloseableHttpClient implements HttpClient, Closeable { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request, + HttpContext context) throws IOException, ClientProtocolException; + + /** + * {@inheritDoc} + */ + public CloseableHttpResponse execute( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws IOException, ClientProtocolException { + return doExecute(target, request, context); + } + + /** + * {@inheritDoc} + */ + public CloseableHttpResponse execute( + final HttpUriRequest request, + final HttpContext context) throws IOException, ClientProtocolException { + Args.notNull(request, "HTTP request"); + return doExecute(determineTarget(request), request, context); + } + + private static HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException { + // A null target may be acceptable if there is a default target. + // Otherwise, the null target is detected in the director. + HttpHost target = null; + + final URI requestURI = request.getURI(); + if (requestURI.isAbsolute()) { + target = URIUtils.extractHost(requestURI); + if (target == null) { + throw new ClientProtocolException("URI does not specify a valid host name: " + + requestURI); + } + } + return target; + } + + /** + * {@inheritDoc} + */ + public CloseableHttpResponse execute( + final HttpUriRequest request) throws IOException, ClientProtocolException { + return execute(request, (HttpContext) null); + } + + /** + * {@inheritDoc} + */ + public CloseableHttpResponse execute( + final HttpHost target, + final HttpRequest request) throws IOException, ClientProtocolException { + return doExecute(target, request, (HttpContext) null); + } + + /** + * Executes a request using the default context and processes the + * response using the given response handler. The content entity associated + * with the response is fully consumed and the underlying connection is + * released back to the connection manager automatically in all cases + * relieving individual {@link ResponseHandler}s from having to manage + * resource deallocation internally. + * + * @param request the request to execute + * @param responseHandler the response handler + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler) throws IOException, + ClientProtocolException { + return execute(request, responseHandler, null); + } + + /** + * Executes a request using the default context and processes the + * response using the given response handler. The content entity associated + * with the response is fully consumed and the underlying connection is + * released back to the connection manager automatically in all cases + * relieving individual {@link ResponseHandler}s from having to manage + * resource deallocation internally. + * + * @param request the request to execute + * @param responseHandler the response handler + * @param context the context to use for the execution, or + * null to use the default context + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler, final HttpContext context) + throws IOException, ClientProtocolException { + final HttpHost target = determineTarget(request); + return execute(target, request, responseHandler, context); + } + + /** + * Executes a request using the default context and processes the + * response using the given response handler. The content entity associated + * with the response is fully consumed and the underlying connection is + * released back to the connection manager automatically in all cases + * relieving individual {@link ResponseHandler}s from having to manage + * resource deallocation internally. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param responseHandler the response handler + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + public T execute(final HttpHost target, final HttpRequest request, + final ResponseHandler responseHandler) throws IOException, + ClientProtocolException { + return execute(target, request, responseHandler, null); + } + + /** + * Executes a request using the default context and processes the + * response using the given response handler. The content entity associated + * with the response is fully consumed and the underlying connection is + * released back to the connection manager automatically in all cases + * relieving individual {@link ResponseHandler}s from having to manage + * resource deallocation internally. + * + * @param target the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param responseHandler the response handler + * @param context the context to use for the execution, or + * null to use the default context + * + * @return the response object as generated by the response handler. + * @throws IOException in case of a problem or the connection was aborted + * @throws ClientProtocolException in case of an http protocol error + */ + public T execute(final HttpHost target, final HttpRequest request, + final ResponseHandler responseHandler, final HttpContext context) + throws IOException, ClientProtocolException { + Args.notNull(responseHandler, "Response handler"); + + final HttpResponse response = execute(target, request, context); + + final T result; + try { + result = responseHandler.handleResponse(response); + } catch (final Exception t) { + final HttpEntity entity = response.getEntity(); + try { + EntityUtils.consume(entity); + } catch (final Exception t2) { + // Log this exception. The original exception is more + // important and will be thrown to the caller. + this.log.warn("Error consuming content after an exception.", t2); + } + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + if (t instanceof IOException) { + throw (IOException) t; + } + throw new UndeclaredThrowableException(t); + } + + // Handling the response was successful. Ensure that the content has + // been fully consumed. + final HttpEntity entity = response.getEntity(); + EntityUtils.consume(entity); + return result; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpResponseProxy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpResponseProxy.java new file mode 100644 index 000000000..9869740df --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/CloseableHttpResponseProxy.java @@ -0,0 +1,87 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * @since 4.3 + */ +@NotThreadSafe +class CloseableHttpResponseProxy implements InvocationHandler { + + private final HttpResponse original; + + CloseableHttpResponseProxy(final HttpResponse original) { + super(); + this.original = original; + } + + public void close() throws IOException { + final HttpEntity entity = this.original.getEntity(); + EntityUtils.consume(entity); + } + + public Object invoke( + final Object proxy, final Method method, final Object[] args) throws Throwable { + final String mname = method.getName(); + if (mname.equals("close")) { + close(); + return null; + } else { + try { + return method.invoke(original, args); + } catch (final InvocationTargetException ex) { + final Throwable cause = ex.getCause(); + if (cause != null) { + throw cause; + } else { + throw ex; + } + } + } + } + + public static CloseableHttpResponse newProxy(final HttpResponse original) { + return (CloseableHttpResponse) Proxy.newProxyInstance( + CloseableHttpResponseProxy.class.getClassLoader(), + new Class[] { CloseableHttpResponse.class }, + new CloseableHttpResponseProxy(original)); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ContentEncodingHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ContentEncodingHttpClient.java new file mode 100644 index 000000000..f4169225e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ContentEncodingHttpClient.java @@ -0,0 +1,93 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.protocol.RequestAcceptEncoding; +import ch.boye.httpclientandroidlib.client.protocol.ResponseContentEncoding; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.BasicHttpProcessor; + +/** + * {@link DefaultHttpClient} sub-class which includes a {@link RequestAcceptEncoding} + * for the request and response. + * + * Deprecation note: due to the way this class modifies a response body + * without changing the response headers to reflect the entity changes, it cannot + * be used as the "backend" for a caching {@link + * ch.boye.httpclientandroidlib.client.HttpClient} and still have uncompressed responses be cached. + * Users are encouraged to use the {@link DecompressingHttpClient} instead + * of this class, which can be wired in either before or after caching, depending on + * whether you want to cache responses in compressed or uncompressed form. + * + * @since 4.1 + * + * @deprecated (4.2) use {@link HttpClientBuilder} + */ +@Deprecated +@ThreadSafe // since DefaultHttpClient is +public class ContentEncodingHttpClient extends DefaultHttpClient { + + /** + * Creates a new HTTP client from parameters and a connection manager. + * + * @param params the parameters + * @param conman the connection manager + */ + public ContentEncodingHttpClient(final ClientConnectionManager conman, final HttpParams params) { + super(conman, params); + } + + /** + * @param params + */ + public ContentEncodingHttpClient(final HttpParams params) { + this(null, params); + } + + /** + * + */ + public ContentEncodingHttpClient() { + this(null); + } + + /** + * {@inheritDoc} + */ + @Override + protected BasicHttpProcessor createHttpProcessor() { + final BasicHttpProcessor result = super.createHttpProcessor(); + + result.addRequestInterceptor(new RequestAcceptEncoding()); + result.addResponseInterceptor(new ResponseContentEncoding()); + + return result; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DecompressingHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DecompressingHttpClient.java new file mode 100644 index 000000000..4d55303ad --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DecompressingHttpClient.java @@ -0,0 +1,214 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.net.URI; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.ResponseHandler; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.protocol.RequestAcceptEncoding; +import ch.boye.httpclientandroidlib.client.protocol.ResponseContentEncoding; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + *

    Decorator adding support for compressed responses. This class sets + * the Accept-Encoding header on requests to indicate + * support for the gzip and deflate + * compression schemes; it then checks the Content-Encoding + * header on the response to uncompress any compressed response bodies. + * The {@link java.io.InputStream} of the entity will contain the uncompressed + * content.

    + * + *

    N.B. Any upstream clients of this class need to be aware that + * this effectively obscures visibility into the length of a server + * response body, since the Content-Length header will + * correspond to the compressed entity length received from the server, + * but the content length experienced by reading the response body may + * be different (hopefully higher!).

    + * + *

    That said, this decorator is compatible with the + * CachingHttpClient in that the two decorators can be added + * in either order and still have cacheable responses be cached.

    + * + * @since 4.2 + * + * @deprecated (4.3) use {@link HttpClientBuilder} + */ +@Deprecated +public class DecompressingHttpClient implements HttpClient { + + private final HttpClient backend; + private final HttpRequestInterceptor acceptEncodingInterceptor; + private final HttpResponseInterceptor contentEncodingInterceptor; + + /** + * Constructs a decorator to ask for and handle compressed + * entities on the fly. + */ + public DecompressingHttpClient() { + this(new DefaultHttpClient()); + } + + /** + * Constructs a decorator to ask for and handle compressed + * entities on the fly. + * @param backend the {@link HttpClient} to use for actually + * issuing requests + */ + public DecompressingHttpClient(final HttpClient backend) { + this(backend, new RequestAcceptEncoding(), new ResponseContentEncoding()); + } + + DecompressingHttpClient(final HttpClient backend, + final HttpRequestInterceptor requestInterceptor, + final HttpResponseInterceptor responseInterceptor) { + this.backend = backend; + this.acceptEncodingInterceptor = requestInterceptor; + this.contentEncodingInterceptor = responseInterceptor; + } + + public HttpParams getParams() { + return backend.getParams(); + } + + public ClientConnectionManager getConnectionManager() { + return backend.getConnectionManager(); + } + + public HttpResponse execute(final HttpUriRequest request) throws IOException, + ClientProtocolException { + return execute(getHttpHost(request), request, (HttpContext)null); + } + + /** + * Gets the HttpClient to issue request. + * + * @return the HttpClient to issue request + */ + public HttpClient getHttpClient() { + return this.backend; + } + + HttpHost getHttpHost(final HttpUriRequest request) { + final URI uri = request.getURI(); + return URIUtils.extractHost(uri); + } + + public HttpResponse execute(final HttpUriRequest request, final HttpContext context) + throws IOException, ClientProtocolException { + return execute(getHttpHost(request), request, context); + } + + public HttpResponse execute(final HttpHost target, final HttpRequest request) + throws IOException, ClientProtocolException { + return execute(target, request, (HttpContext)null); + } + + public HttpResponse execute(final HttpHost target, final HttpRequest request, + final HttpContext context) throws IOException, ClientProtocolException { + try { + final HttpContext localContext = context != null ? context : new BasicHttpContext(); + final HttpRequest wrapped; + if (request instanceof HttpEntityEnclosingRequest) { + wrapped = new EntityEnclosingRequestWrapper((HttpEntityEnclosingRequest) request); + } else { + wrapped = new RequestWrapper(request); + } + acceptEncodingInterceptor.process(wrapped, localContext); + final HttpResponse response = backend.execute(target, wrapped, localContext); + try { + contentEncodingInterceptor.process(response, localContext); + if (Boolean.TRUE.equals(localContext.getAttribute(ResponseContentEncoding.UNCOMPRESSED))) { + response.removeHeaders("Content-Length"); + response.removeHeaders("Content-Encoding"); + response.removeHeaders("Content-MD5"); + } + return response; + } catch (final HttpException ex) { + EntityUtils.consume(response.getEntity()); + throw ex; + } catch (final IOException ex) { + EntityUtils.consume(response.getEntity()); + throw ex; + } catch (final RuntimeException ex) { + EntityUtils.consume(response.getEntity()); + throw ex; + } + } catch (final HttpException e) { + throw new ClientProtocolException(e); + } + } + + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler) throws IOException, + ClientProtocolException { + return execute(getHttpHost(request), request, responseHandler); + } + + public T execute(final HttpUriRequest request, + final ResponseHandler responseHandler, final HttpContext context) + throws IOException, ClientProtocolException { + return execute(getHttpHost(request), request, responseHandler, context); + } + + public T execute(final HttpHost target, final HttpRequest request, + final ResponseHandler responseHandler) throws IOException, + ClientProtocolException { + return execute(target, request, responseHandler, null); + } + + public T execute(final HttpHost target, final HttpRequest request, + final ResponseHandler responseHandler, final HttpContext context) + throws IOException, ClientProtocolException { + final HttpResponse response = execute(target, request, context); + try { + return responseHandler.handleResponse(response); + } finally { + final HttpEntity entity = response.getEntity(); + if (entity != null) { + EntityUtils.consume(entity); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultBackoffStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultBackoffStrategy.java new file mode 100644 index 000000000..5a5e67caa --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultBackoffStrategy.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy; + +/** + * This {@link ConnectionBackoffStrategy} backs off either for a raw + * network socket or connection timeout or if the server explicitly + * sends a 503 (Service Unavailable) response. + * + * @since 4.2 + */ +public class DefaultBackoffStrategy implements ConnectionBackoffStrategy { + + public boolean shouldBackoff(final Throwable t) { + return (t instanceof SocketTimeoutException + || t instanceof ConnectException); + } + + public boolean shouldBackoff(final HttpResponse resp) { + return (resp.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultConnectionKeepAliveStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultConnectionKeepAliveStrategy.java new file mode 100644 index 000000000..a5524bd43 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultConnectionKeepAliveStrategy.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HeaderElementIterator; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy; +import ch.boye.httpclientandroidlib.message.BasicHeaderElementIterator; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of a strategy deciding duration + * that a connection can remain idle. + * + * The default implementation looks solely at the 'Keep-Alive' + * header's timeout token. + * + * @since 4.0 + */ +@Immutable +public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy { + + public static final DefaultConnectionKeepAliveStrategy INSTANCE = new DefaultConnectionKeepAliveStrategy(); + + public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) { + Args.notNull(response, "HTTP response"); + final HeaderElementIterator it = new BasicHeaderElementIterator( + response.headerIterator(HTTP.CONN_KEEP_ALIVE)); + while (it.hasNext()) { + final HeaderElement he = it.nextElement(); + final String param = he.getName(); + final String value = he.getValue(); + if (value != null && param.equalsIgnoreCase("timeout")) { + try { + return Long.parseLong(value) * 1000; + } catch(final NumberFormatException ignore) { + } + } + } + return -1; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpClient.java new file mode 100644 index 000000000..55e5ab07e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpClient.java @@ -0,0 +1,225 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.protocol.RequestAddCookies; +import ch.boye.httpclientandroidlib.client.protocol.RequestAuthCache; +import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl; +import ch.boye.httpclientandroidlib.client.protocol.RequestDefaultHeaders; +import ch.boye.httpclientandroidlib.client.protocol.RequestProxyAuthentication; +import ch.boye.httpclientandroidlib.client.protocol.RequestTargetAuthentication; +import ch.boye.httpclientandroidlib.client.protocol.ResponseProcessCookies; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.params.HttpProtocolParams; +import ch.boye.httpclientandroidlib.params.SyncBasicHttpParams; +import ch.boye.httpclientandroidlib.protocol.BasicHttpProcessor; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.RequestContent; +import ch.boye.httpclientandroidlib.protocol.RequestExpectContinue; +import ch.boye.httpclientandroidlib.protocol.RequestTargetHost; +import ch.boye.httpclientandroidlib.protocol.RequestUserAgent; + +/** + * Default implementation of {@link ch.boye.httpclientandroidlib.client.HttpClient} pre-configured + * for most common use scenarios. + *

    + * Please see the Javadoc for {@link #createHttpProcessor()} for the details of the interceptors + * that are set up by default. + *

    + * Additional interceptors can be added as follows, but + * take care not to add the same interceptor more than once. + *

    + * DefaultHttpClient httpclient = new DefaultHttpClient();
    + * httpclient.addRequestInterceptor(new RequestAcceptEncoding());
    + * httpclient.addResponseInterceptor(new ResponseContentEncoding());
    + * 
    + *

    + * This class sets up the following parameters if not explicitly set: + *

      + *
    • Version: HttpVersion.HTTP_1_1
    • + *
    • ContentCharset: HTTP.DEFAULT_CONTENT_CHARSET
    • + *
    • NoTcpDelay: true
    • + *
    • SocketBufferSize: 8192
    • + *
    • UserAgent: Apache-HttpClient/release (java 1.5)
    • + *
    + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#PROTOCOL_VERSION}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USE_EXPECT_CONTINUE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USER_AGENT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#STALE_CONNECTION_CHECK}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY}
    • + *
    • {@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#DATE_PATTERNS}
    • + *
    • {@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#SINGLE_COOKIE_HEADER}
    • + *
    • {@link ch.boye.httpclientandroidlib.auth.params.AuthPNames#CREDENTIAL_CHARSET}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#COOKIE_POLICY}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_AUTHENTICATION}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#MAX_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#ALLOW_CIRCULAR_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#VIRTUAL_HOST}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HOST}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HEADERS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link HttpClientBuilder}. + */ +@ThreadSafe +@Deprecated +public class DefaultHttpClient extends AbstractHttpClient { + + /** + * Creates a new HTTP client from parameters and a connection manager. + * + * @param params the parameters + * @param conman the connection manager + */ + public DefaultHttpClient( + final ClientConnectionManager conman, + final HttpParams params) { + super(conman, params); + } + + + /** + * @since 4.1 + */ + public DefaultHttpClient( + final ClientConnectionManager conman) { + super(conman, null); + } + + + public DefaultHttpClient(final HttpParams params) { + super(null, params); + } + + + public DefaultHttpClient() { + super(null, null); + } + + + /** + * Creates the default set of HttpParams by invoking {@link DefaultHttpClient#setDefaultHttpParams(HttpParams)} + * + * @return a new instance of {@link SyncBasicHttpParams} with the defaults applied to it. + */ + @Override + protected HttpParams createHttpParams() { + final HttpParams params = new SyncBasicHttpParams(); + setDefaultHttpParams(params); + return params; + } + + /** + * Saves the default set of HttpParams in the provided parameter. + * These are: + *
      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#PROTOCOL_VERSION}: + * 1.1
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_CONTENT_CHARSET}: + * ISO-8859-1
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}: + * true
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}: + * 8192
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USER_AGENT}: + * Apache-HttpClient/ (java 1.5)
    • + *
    + */ + public static void setDefaultHttpParams(final HttpParams params) { + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); + HttpProtocolParams.setContentCharset(params, HTTP.DEF_CONTENT_CHARSET.name()); + HttpConnectionParams.setTcpNoDelay(params, true); + HttpConnectionParams.setSocketBufferSize(params, 8192); + HttpProtocolParams.setUserAgent(params, HttpClientBuilder.DEFAULT_USER_AGENT); + } + + /** + * Create the processor with the following interceptors: + *
      + *
    • {@link RequestDefaultHeaders}
    • + *
    • {@link RequestContent}
    • + *
    • {@link RequestTargetHost}
    • + *
    • {@link RequestClientConnControl}
    • + *
    • {@link RequestUserAgent}
    • + *
    • {@link RequestExpectContinue}
    • + *
    • {@link RequestAddCookies}
    • + *
    • {@link ResponseProcessCookies}
    • + *
    • {@link RequestAuthCache}
    • + *
    • {@link RequestTargetAuthentication}
    • + *
    • {@link RequestProxyAuthentication}
    • + *
    + *

    + * @return the processor with the added interceptors. + */ + @Override + protected BasicHttpProcessor createHttpProcessor() { + final BasicHttpProcessor httpproc = new BasicHttpProcessor(); + httpproc.addInterceptor(new RequestDefaultHeaders()); + // Required protocol interceptors + httpproc.addInterceptor(new RequestContent()); + httpproc.addInterceptor(new RequestTargetHost()); + // Recommended protocol interceptors + httpproc.addInterceptor(new RequestClientConnControl()); + httpproc.addInterceptor(new RequestUserAgent()); + httpproc.addInterceptor(new RequestExpectContinue()); + // HTTP state management interceptors + httpproc.addInterceptor(new RequestAddCookies()); + httpproc.addInterceptor(new ResponseProcessCookies()); + // HTTP authentication interceptors + httpproc.addInterceptor(new RequestAuthCache()); + httpproc.addInterceptor(new RequestTargetAuthentication()); + httpproc.addInterceptor(new RequestProxyAuthentication()); + return httpproc; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpRequestRetryHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpRequestRetryHandler.java new file mode 100644 index 000000000..1721c8e70 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultHttpRequestRetryHandler.java @@ -0,0 +1,203 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.ConnectException; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.SSLException; + +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * The default {@link HttpRequestRetryHandler} used by request executors. + * + * @since 4.0 + */ +@Immutable +public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler { + + public static final DefaultHttpRequestRetryHandler INSTANCE = new DefaultHttpRequestRetryHandler(); + + /** the number of times a method will be retried */ + private final int retryCount; + + /** Whether or not methods that have successfully sent their request will be retried */ + private final boolean requestSentRetryEnabled; + + private final Set> nonRetriableClasses; + + /** + * Create the request retry handler using the specified IOException classes + * + * @param retryCount how many times to retry; 0 means no retries + * @param requestSentRetryEnabled true if it's OK to retry requests that have been sent + * @param clazzes the IOException types that should not be retried + * @since 4.3 + */ + protected DefaultHttpRequestRetryHandler( + final int retryCount, + final boolean requestSentRetryEnabled, + final Collection> clazzes) { + super(); + this.retryCount = retryCount; + this.requestSentRetryEnabled = requestSentRetryEnabled; + this.nonRetriableClasses = new HashSet>(); + for (final Class clazz: clazzes) { + this.nonRetriableClasses.add(clazz); + } + } + + /** + * Create the request retry handler using the following list of + * non-retriable IOException classes:
    + *

      + *
    • InterruptedIOException
    • + *
    • UnknownHostException
    • + *
    • ConnectException
    • + *
    • SSLException
    • + *
    + * @param retryCount how many times to retry; 0 means no retries + * @param requestSentRetryEnabled true if it's OK to retry requests that have been sent + */ + @SuppressWarnings("unchecked") + public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) { + this(retryCount, requestSentRetryEnabled, Arrays.asList( + InterruptedIOException.class, + UnknownHostException.class, + ConnectException.class, + SSLException.class)); + } + + /** + * Create the request retry handler with a retry count of 3, requestSentRetryEnabled false + * and using the following list of non-retriable IOException classes:
    + *
      + *
    • InterruptedIOException
    • + *
    • UnknownHostException
    • + *
    • ConnectException
    • + *
    • SSLException
    • + *
    + */ + public DefaultHttpRequestRetryHandler() { + this(3, false); + } + /** + * Used retryCount and requestSentRetryEnabled to determine + * if the given method should be retried. + */ + public boolean retryRequest( + final IOException exception, + final int executionCount, + final HttpContext context) { + Args.notNull(exception, "Exception parameter"); + Args.notNull(context, "HTTP context"); + if (executionCount > this.retryCount) { + // Do not retry if over max retry count + return false; + } + if (this.nonRetriableClasses.contains(exception.getClass())) { + return false; + } else { + for (final Class rejectException : this.nonRetriableClasses) { + if (rejectException.isInstance(exception)) { + return false; + } + } + } + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final HttpRequest request = clientContext.getRequest(); + + if(requestIsAborted(request)){ + return false; + } + + if (handleAsIdempotent(request)) { + // Retry if the request is considered idempotent + return true; + } + + if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) { + // Retry if the request has not been sent fully or + // if it's OK to retry methods that have been sent + return true; + } + // otherwise do not retry + return false; + } + + /** + * @return true if this handler will retry methods that have + * successfully sent their request, false otherwise + */ + public boolean isRequestSentRetryEnabled() { + return requestSentRetryEnabled; + } + + /** + * @return the maximum number of times a method will be retried + */ + public int getRetryCount() { + return retryCount; + } + + /** + * @since 4.2 + */ + protected boolean handleAsIdempotent(final HttpRequest request) { + return !(request instanceof HttpEntityEnclosingRequest); + } + + /** + * @since 4.2 + * + * @deprecated (4.3) + */ + @Deprecated + protected boolean requestIsAborted(final HttpRequest request) { + HttpRequest req = request; + if (request instanceof RequestWrapper) { // does not forward request to original + req = ((RequestWrapper) request).getOriginal(); + } + return (req instanceof HttpUriRequest && ((HttpUriRequest)req).isAborted()); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultProxyAuthenticationHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultProxyAuthenticationHandler.java new file mode 100644 index 000000000..a70057ae5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultProxyAuthenticationHandler.java @@ -0,0 +1,90 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.List; +import java.util.Map; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.auth.params.AuthPNames; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default {@link ch.boye.httpclientandroidlib.client.AuthenticationHandler} implementation + * for proxy host authentication. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ProxyAuthenticationStrategy} + */ +@Deprecated +@Immutable +public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHandler { + + public DefaultProxyAuthenticationHandler() { + super(); + } + + public boolean isAuthenticationRequested( + final HttpResponse response, + final HttpContext context) { + Args.notNull(response, "HTTP response"); + final int status = response.getStatusLine().getStatusCode(); + return status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED; + } + + public Map getChallenges( + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + Args.notNull(response, "HTTP response"); + final Header[] headers = response.getHeaders(AUTH.PROXY_AUTH); + return parseChallenges(headers); + } + + @Override + protected List getAuthPreferences( + final HttpResponse response, + final HttpContext context) { + @SuppressWarnings("unchecked") + final + List authpref = (List) response.getParams().getParameter( + AuthPNames.PROXY_AUTH_PREF); + if (authpref != null) { + return authpref; + } else { + return super.getAuthPreferences(response, context); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectHandler.java new file mode 100644 index 000000000..6bd58c035 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectHandler.java @@ -0,0 +1,180 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.URI; +import java.net.URISyntaxException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.CircularRedirectException; +import ch.boye.httpclientandroidlib.client.RedirectHandler; +import ch.boye.httpclientandroidlib.client.methods.HttpGet; +import ch.boye.httpclientandroidlib.client.methods.HttpHead; +import ch.boye.httpclientandroidlib.client.params.ClientPNames; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.ExecutionContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Default implementation of {@link RedirectHandler}. + * + * @since 4.0 + * + * @deprecated (4.1) use {@link DefaultRedirectStrategy}. + */ +@Immutable +@Deprecated +public class DefaultRedirectHandler implements RedirectHandler { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations"; + + public DefaultRedirectHandler() { + super(); + } + + public boolean isRedirectRequested( + final HttpResponse response, + final HttpContext context) { + Args.notNull(response, "HTTP response"); + + final int statusCode = response.getStatusLine().getStatusCode(); + switch (statusCode) { + case HttpStatus.SC_MOVED_TEMPORARILY: + case HttpStatus.SC_MOVED_PERMANENTLY: + case HttpStatus.SC_TEMPORARY_REDIRECT: + final HttpRequest request = (HttpRequest) context.getAttribute( + ExecutionContext.HTTP_REQUEST); + final String method = request.getRequestLine().getMethod(); + return method.equalsIgnoreCase(HttpGet.METHOD_NAME) + || method.equalsIgnoreCase(HttpHead.METHOD_NAME); + case HttpStatus.SC_SEE_OTHER: + return true; + default: + return false; + } //end of switch + } + + public URI getLocationURI( + final HttpResponse response, + final HttpContext context) throws ProtocolException { + Args.notNull(response, "HTTP response"); + //get the location header to find out where to redirect to + final Header locationHeader = response.getFirstHeader("location"); + if (locationHeader == null) { + // got a redirect response, but no location header + throw new ProtocolException( + "Received redirect response " + response.getStatusLine() + + " but no location header"); + } + final String location = locationHeader.getValue(); + if (this.log.isDebugEnabled()) { + this.log.debug("Redirect requested to location '" + location + "'"); + } + + URI uri; + try { + uri = new URI(location); + } catch (final URISyntaxException ex) { + throw new ProtocolException("Invalid redirect URI: " + location, ex); + } + + final HttpParams params = response.getParams(); + // rfc2616 demands the location value be a complete URI + // Location = "Location" ":" absoluteURI + if (!uri.isAbsolute()) { + if (params.isParameterTrue(ClientPNames.REJECT_RELATIVE_REDIRECT)) { + throw new ProtocolException("Relative redirect location '" + + uri + "' not allowed"); + } + // Adjust location URI + final HttpHost target = (HttpHost) context.getAttribute( + ExecutionContext.HTTP_TARGET_HOST); + Asserts.notNull(target, "Target host"); + + final HttpRequest request = (HttpRequest) context.getAttribute( + ExecutionContext.HTTP_REQUEST); + + try { + final URI requestURI = new URI(request.getRequestLine().getUri()); + final URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true); + uri = URIUtils.resolve(absoluteRequestURI, uri); + } catch (final URISyntaxException ex) { + throw new ProtocolException(ex.getMessage(), ex); + } + } + + if (params.isParameterFalse(ClientPNames.ALLOW_CIRCULAR_REDIRECTS)) { + + RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute( + REDIRECT_LOCATIONS); + + if (redirectLocations == null) { + redirectLocations = new RedirectLocations(); + context.setAttribute(REDIRECT_LOCATIONS, redirectLocations); + } + + final URI redirectURI; + if (uri.getFragment() != null) { + try { + final HttpHost target = new HttpHost( + uri.getHost(), + uri.getPort(), + uri.getScheme()); + redirectURI = URIUtils.rewriteURI(uri, target, true); + } catch (final URISyntaxException ex) { + throw new ProtocolException(ex.getMessage(), ex); + } + } else { + redirectURI = uri; + } + + if (redirectLocations.contains(redirectURI)) { + throw new CircularRedirectException("Circular redirect to '" + + redirectURI + "'"); + } else { + redirectLocations.add(redirectURI); + } + } + + return uri; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategy.java new file mode 100644 index 000000000..e7d9315d2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategy.java @@ -0,0 +1,233 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.CircularRedirectException; +import ch.boye.httpclientandroidlib.client.RedirectStrategy; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.HttpGet; +import ch.boye.httpclientandroidlib.client.methods.HttpHead; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.methods.RequestBuilder; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.utils.URIBuilder; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.TextUtils; + +/** + * Default implementation of {@link RedirectStrategy}. This strategy honors the restrictions + * on automatic redirection of entity enclosing methods such as POST and PUT imposed by the + * HTTP specification. 302 Moved Temporarily, 301 Moved Permanently and + * 307 Temporary Redirect status codes will result in an automatic redirect of + * HEAD and GET methods only. POST and PUT methods will not be automatically redirected + * as requiring user confirmation. + *

    + * The restriction on automatic redirection of POST methods can be relaxed by using + * {@link LaxRedirectStrategy} instead of {@link DefaultRedirectStrategy}. + * + * @see LaxRedirectStrategy + * @since 4.1 + */ +@Immutable +public class DefaultRedirectStrategy implements RedirectStrategy { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.protocol.HttpClientContext#REDIRECT_LOCATIONS}. + */ + @Deprecated + public static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations"; + + public static final DefaultRedirectStrategy INSTANCE = new DefaultRedirectStrategy(); + + /** + * Redirectable methods. + */ + private static final String[] REDIRECT_METHODS = new String[] { + HttpGet.METHOD_NAME, + HttpHead.METHOD_NAME + }; + + public DefaultRedirectStrategy() { + super(); + } + + public boolean isRedirected( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws ProtocolException { + Args.notNull(request, "HTTP request"); + Args.notNull(response, "HTTP response"); + + final int statusCode = response.getStatusLine().getStatusCode(); + final String method = request.getRequestLine().getMethod(); + final Header locationHeader = response.getFirstHeader("location"); + switch (statusCode) { + case HttpStatus.SC_MOVED_TEMPORARILY: + return isRedirectable(method) && locationHeader != null; + case HttpStatus.SC_MOVED_PERMANENTLY: + case HttpStatus.SC_TEMPORARY_REDIRECT: + return isRedirectable(method); + case HttpStatus.SC_SEE_OTHER: + return true; + default: + return false; + } //end of switch + } + + public URI getLocationURI( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws ProtocolException { + Args.notNull(request, "HTTP request"); + Args.notNull(response, "HTTP response"); + Args.notNull(context, "HTTP context"); + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + //get the location header to find out where to redirect to + final Header locationHeader = response.getFirstHeader("location"); + if (locationHeader == null) { + // got a redirect response, but no location header + throw new ProtocolException( + "Received redirect response " + response.getStatusLine() + + " but no location header"); + } + final String location = locationHeader.getValue(); + if (this.log.isDebugEnabled()) { + this.log.debug("Redirect requested to location '" + location + "'"); + } + + final RequestConfig config = clientContext.getRequestConfig(); + + URI uri = createLocationURI(location); + + // rfc2616 demands the location value be a complete URI + // Location = "Location" ":" absoluteURI + try { + if (!uri.isAbsolute()) { + if (!config.isRelativeRedirectsAllowed()) { + throw new ProtocolException("Relative redirect location '" + + uri + "' not allowed"); + } + // Adjust location URI + final HttpHost target = clientContext.getTargetHost(); + Asserts.notNull(target, "Target host"); + final URI requestURI = new URI(request.getRequestLine().getUri()); + final URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, false); + uri = URIUtils.resolve(absoluteRequestURI, uri); + } + } catch (final URISyntaxException ex) { + throw new ProtocolException(ex.getMessage(), ex); + } + + RedirectLocations redirectLocations = (RedirectLocations) clientContext.getAttribute( + HttpClientContext.REDIRECT_LOCATIONS); + if (redirectLocations == null) { + redirectLocations = new RedirectLocations(); + context.setAttribute(HttpClientContext.REDIRECT_LOCATIONS, redirectLocations); + } + if (!config.isCircularRedirectsAllowed()) { + if (redirectLocations.contains(uri)) { + throw new CircularRedirectException("Circular redirect to '" + uri + "'"); + } + } + redirectLocations.add(uri); + return uri; + } + + /** + * @since 4.1 + */ + protected URI createLocationURI(final String location) throws ProtocolException { + try { + final URIBuilder b = new URIBuilder(new URI(location).normalize()); + final String host = b.getHost(); + if (host != null) { + b.setHost(host.toLowerCase(Locale.ENGLISH)); + } + final String path = b.getPath(); + if (TextUtils.isEmpty(path)) { + b.setPath("/"); + } + return b.build(); + } catch (final URISyntaxException ex) { + throw new ProtocolException("Invalid redirect URI: " + location, ex); + } + } + + /** + * @since 4.2 + */ + protected boolean isRedirectable(final String method) { + for (final String m: REDIRECT_METHODS) { + if (m.equalsIgnoreCase(method)) { + return true; + } + } + return false; + } + + public HttpUriRequest getRedirect( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws ProtocolException { + final URI uri = getLocationURI(request, response, context); + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) { + return new HttpHead(uri); + } else if (method.equalsIgnoreCase(HttpGet.METHOD_NAME)) { + return new HttpGet(uri); + } else { + final int status = response.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_TEMPORARY_REDIRECT) { + return RequestBuilder.copy(request).setUri(uri).build(); + } else { + return new HttpGet(uri); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategyAdaptor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategyAdaptor.java new file mode 100644 index 000000000..b49fb7f73 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRedirectStrategyAdaptor.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.RedirectHandler; +import ch.boye.httpclientandroidlib.client.RedirectStrategy; +import ch.boye.httpclientandroidlib.client.methods.HttpGet; +import ch.boye.httpclientandroidlib.client.methods.HttpHead; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * @deprecated (4.1) do not use + */ +@Immutable +@Deprecated +class DefaultRedirectStrategyAdaptor implements RedirectStrategy { + + private final RedirectHandler handler; + + public DefaultRedirectStrategyAdaptor(final RedirectHandler handler) { + super(); + this.handler = handler; + } + + public boolean isRedirected( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws ProtocolException { + return this.handler.isRedirectRequested(response, context); + } + + public HttpUriRequest getRedirect( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws ProtocolException { + final URI uri = this.handler.getLocationURI(response, context); + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) { + return new HttpHead(uri); + } else { + return new HttpGet(uri); + } + } + + public RedirectHandler getHandler() { + return this.handler; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRequestDirector.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRequestDirector.java new file mode 100644 index 000000000..03d9e1e5f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultRequestDirector.java @@ -0,0 +1,1150 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthProtocolState; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.auth.UsernamePasswordCredentials; +import ch.boye.httpclientandroidlib.client.AuthenticationHandler; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler; +import ch.boye.httpclientandroidlib.client.NonRepeatableRequestException; +import ch.boye.httpclientandroidlib.client.RedirectException; +import ch.boye.httpclientandroidlib.client.RedirectHandler; +import ch.boye.httpclientandroidlib.client.RedirectStrategy; +import ch.boye.httpclientandroidlib.client.RequestDirector; +import ch.boye.httpclientandroidlib.client.UserTokenHandler; +import ch.boye.httpclientandroidlib.client.methods.AbortableHttpRequest; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.params.ClientPNames; +import ch.boye.httpclientandroidlib.client.params.HttpClientParams; +import ch.boye.httpclientandroidlib.client.protocol.ClientContext; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.conn.BasicManagedEntity; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.BasicRouteDirector; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRouteDirector; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.entity.BufferedHttpEntity; +import ch.boye.httpclientandroidlib.impl.auth.BasicScheme; +import ch.boye.httpclientandroidlib.impl.conn.ConnectionShutdownException; +import ch.boye.httpclientandroidlib.message.BasicHttpRequest; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.params.HttpProtocolParams; +import ch.boye.httpclientandroidlib.protocol.ExecutionContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpProcessor; +import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * Default implementation of {@link RequestDirector}. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#PROTOCOL_VERSION}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USE_EXPECT_CONTINUE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USER_AGENT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#STALE_CONNECTION_CHECK}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY}
    • + *
    • {@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#DATE_PATTERNS}
    • + *
    • {@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#SINGLE_COOKIE_HEADER}
    • + *
    • {@link ch.boye.httpclientandroidlib.auth.params.AuthPNames#CREDENTIAL_CHARSET}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#COOKIE_POLICY}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_AUTHENTICATION}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#MAX_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#ALLOW_CIRCULAR_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#VIRTUAL_HOST}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HOST}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HEADERS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) + */ +@Deprecated +@NotThreadSafe // e.g. managedConn +public class DefaultRequestDirector implements RequestDirector { + + public HttpClientAndroidLog log; + + /** The connection manager. */ + protected final ClientConnectionManager connManager; + + /** The route planner. */ + protected final HttpRoutePlanner routePlanner; + + /** The connection re-use strategy. */ + protected final ConnectionReuseStrategy reuseStrategy; + + /** The keep-alive duration strategy. */ + protected final ConnectionKeepAliveStrategy keepAliveStrategy; + + /** The request executor. */ + protected final HttpRequestExecutor requestExec; + + /** The HTTP protocol processor. */ + protected final HttpProcessor httpProcessor; + + /** The request retry handler. */ + protected final HttpRequestRetryHandler retryHandler; + + /** The redirect handler. */ + @Deprecated + protected final RedirectHandler redirectHandler; + + /** The redirect strategy. */ + protected final RedirectStrategy redirectStrategy; + + /** The target authentication handler. */ + @Deprecated + protected final AuthenticationHandler targetAuthHandler; + + /** The target authentication handler. */ + protected final AuthenticationStrategy targetAuthStrategy; + + /** The proxy authentication handler. */ + @Deprecated + protected final AuthenticationHandler proxyAuthHandler; + + /** The proxy authentication handler. */ + protected final AuthenticationStrategy proxyAuthStrategy; + + /** The user token handler. */ + protected final UserTokenHandler userTokenHandler; + + /** The HTTP parameters. */ + protected final HttpParams params; + + /** The currently allocated connection. */ + protected ManagedClientConnection managedConn; + + protected final AuthState targetAuthState; + + protected final AuthState proxyAuthState; + + private final HttpAuthenticator authenticator; + + private int execCount; + + private int redirectCount; + + private final int maxRedirects; + + private HttpHost virtualHost; + + @Deprecated + public DefaultRequestDirector( + final HttpRequestExecutor requestExec, + final ClientConnectionManager conman, + final ConnectionReuseStrategy reustrat, + final ConnectionKeepAliveStrategy kastrat, + final HttpRoutePlanner rouplan, + final HttpProcessor httpProcessor, + final HttpRequestRetryHandler retryHandler, + final RedirectHandler redirectHandler, + final AuthenticationHandler targetAuthHandler, + final AuthenticationHandler proxyAuthHandler, + final UserTokenHandler userTokenHandler, + final HttpParams params) { + this(new HttpClientAndroidLog(DefaultRequestDirector.class), + requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler, + new DefaultRedirectStrategyAdaptor(redirectHandler), + new AuthenticationStrategyAdaptor(targetAuthHandler), + new AuthenticationStrategyAdaptor(proxyAuthHandler), + userTokenHandler, + params); + } + + + @Deprecated + public DefaultRequestDirector( + final HttpClientAndroidLog log, + final HttpRequestExecutor requestExec, + final ClientConnectionManager conman, + final ConnectionReuseStrategy reustrat, + final ConnectionKeepAliveStrategy kastrat, + final HttpRoutePlanner rouplan, + final HttpProcessor httpProcessor, + final HttpRequestRetryHandler retryHandler, + final RedirectStrategy redirectStrategy, + final AuthenticationHandler targetAuthHandler, + final AuthenticationHandler proxyAuthHandler, + final UserTokenHandler userTokenHandler, + final HttpParams params) { + this(new HttpClientAndroidLog(DefaultRequestDirector.class), + requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler, + redirectStrategy, + new AuthenticationStrategyAdaptor(targetAuthHandler), + new AuthenticationStrategyAdaptor(proxyAuthHandler), + userTokenHandler, + params); + } + + /** + * @since 4.2 + */ + public DefaultRequestDirector( + final HttpClientAndroidLog log, + final HttpRequestExecutor requestExec, + final ClientConnectionManager conman, + final ConnectionReuseStrategy reustrat, + final ConnectionKeepAliveStrategy kastrat, + final HttpRoutePlanner rouplan, + final HttpProcessor httpProcessor, + final HttpRequestRetryHandler retryHandler, + final RedirectStrategy redirectStrategy, + final AuthenticationStrategy targetAuthStrategy, + final AuthenticationStrategy proxyAuthStrategy, + final UserTokenHandler userTokenHandler, + final HttpParams params) { + + Args.notNull(log, "Log"); + Args.notNull(requestExec, "Request executor"); + Args.notNull(conman, "Client connection manager"); + Args.notNull(reustrat, "Connection reuse strategy"); + Args.notNull(kastrat, "Connection keep alive strategy"); + Args.notNull(rouplan, "Route planner"); + Args.notNull(httpProcessor, "HTTP protocol processor"); + Args.notNull(retryHandler, "HTTP request retry handler"); + Args.notNull(redirectStrategy, "Redirect strategy"); + Args.notNull(targetAuthStrategy, "Target authentication strategy"); + Args.notNull(proxyAuthStrategy, "Proxy authentication strategy"); + Args.notNull(userTokenHandler, "User token handler"); + Args.notNull(params, "HTTP parameters"); + this.log = log; + this.authenticator = new HttpAuthenticator(log); + this.requestExec = requestExec; + this.connManager = conman; + this.reuseStrategy = reustrat; + this.keepAliveStrategy = kastrat; + this.routePlanner = rouplan; + this.httpProcessor = httpProcessor; + this.retryHandler = retryHandler; + this.redirectStrategy = redirectStrategy; + this.targetAuthStrategy = targetAuthStrategy; + this.proxyAuthStrategy = proxyAuthStrategy; + this.userTokenHandler = userTokenHandler; + this.params = params; + + if (redirectStrategy instanceof DefaultRedirectStrategyAdaptor) { + this.redirectHandler = ((DefaultRedirectStrategyAdaptor) redirectStrategy).getHandler(); + } else { + this.redirectHandler = null; + } + if (targetAuthStrategy instanceof AuthenticationStrategyAdaptor) { + this.targetAuthHandler = ((AuthenticationStrategyAdaptor) targetAuthStrategy).getHandler(); + } else { + this.targetAuthHandler = null; + } + if (proxyAuthStrategy instanceof AuthenticationStrategyAdaptor) { + this.proxyAuthHandler = ((AuthenticationStrategyAdaptor) proxyAuthStrategy).getHandler(); + } else { + this.proxyAuthHandler = null; + } + + this.managedConn = null; + + this.execCount = 0; + this.redirectCount = 0; + this.targetAuthState = new AuthState(); + this.proxyAuthState = new AuthState(); + this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100); + } + + + private RequestWrapper wrapRequest( + final HttpRequest request) throws ProtocolException { + if (request instanceof HttpEntityEnclosingRequest) { + return new EntityEnclosingRequestWrapper( + (HttpEntityEnclosingRequest) request); + } else { + return new RequestWrapper( + request); + } + } + + + protected void rewriteRequestURI( + final RequestWrapper request, + final HttpRoute route) throws ProtocolException { + try { + + URI uri = request.getURI(); + if (route.getProxyHost() != null && !route.isTunnelled()) { + // Make sure the request URI is absolute + if (!uri.isAbsolute()) { + final HttpHost target = route.getTargetHost(); + uri = URIUtils.rewriteURI(uri, target, true); + } else { + uri = URIUtils.rewriteURI(uri); + } + } else { + // Make sure the request URI is relative + if (uri.isAbsolute()) { + uri = URIUtils.rewriteURI(uri, null, true); + } else { + uri = URIUtils.rewriteURI(uri); + } + } + request.setURI(uri); + + } catch (final URISyntaxException ex) { + throw new ProtocolException("Invalid URI: " + + request.getRequestLine().getUri(), ex); + } + } + + + // non-javadoc, see interface ClientRequestDirector + public HttpResponse execute(final HttpHost targetHost, final HttpRequest request, + final HttpContext context) + throws HttpException, IOException { + + context.setAttribute(ClientContext.TARGET_AUTH_STATE, targetAuthState); + context.setAttribute(ClientContext.PROXY_AUTH_STATE, proxyAuthState); + + HttpHost target = targetHost; + + final HttpRequest orig = request; + final RequestWrapper origWrapper = wrapRequest(orig); + origWrapper.setParams(params); + final HttpRoute origRoute = determineRoute(target, origWrapper, context); + + virtualHost = (HttpHost) origWrapper.getParams().getParameter(ClientPNames.VIRTUAL_HOST); + + // HTTPCLIENT-1092 - add the port if necessary + if (virtualHost != null && virtualHost.getPort() == -1) { + final HttpHost host = (target != null) ? target : origRoute.getTargetHost(); + final int port = host.getPort(); + if (port != -1){ + virtualHost = new HttpHost(virtualHost.getHostName(), port, virtualHost.getSchemeName()); + } + } + + RoutedRequest roureq = new RoutedRequest(origWrapper, origRoute); + + boolean reuse = false; + boolean done = false; + try { + HttpResponse response = null; + while (!done) { + // In this loop, the RoutedRequest may be replaced by a + // followup request and route. The request and route passed + // in the method arguments will be replaced. The original + // request is still available in 'orig'. + + final RequestWrapper wrapper = roureq.getRequest(); + final HttpRoute route = roureq.getRoute(); + response = null; + + // See if we have a user token bound to the execution context + Object userToken = context.getAttribute(ClientContext.USER_TOKEN); + + // Allocate connection if needed + if (managedConn == null) { + final ClientConnectionRequest connRequest = connManager.requestConnection( + route, userToken); + if (orig instanceof AbortableHttpRequest) { + ((AbortableHttpRequest) orig).setConnectionRequest(connRequest); + } + + final long timeout = HttpClientParams.getConnectionManagerTimeout(params); + try { + managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS); + } catch(final InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw new InterruptedIOException(); + } + + if (HttpConnectionParams.isStaleCheckingEnabled(params)) { + // validate connection + if (managedConn.isOpen()) { + this.log.debug("Stale connection check"); + if (managedConn.isStale()) { + this.log.debug("Stale connection detected"); + managedConn.close(); + } + } + } + } + + if (orig instanceof AbortableHttpRequest) { + ((AbortableHttpRequest) orig).setReleaseTrigger(managedConn); + } + + try { + tryConnect(roureq, context); + } catch (final TunnelRefusedException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage()); + } + response = ex.getResponse(); + break; + } + + final String userinfo = wrapper.getURI().getUserInfo(); + if (userinfo != null) { + targetAuthState.update( + new BasicScheme(), new UsernamePasswordCredentials(userinfo)); + } + + // Get target. Even if there's virtual host, we may need the target to set the port. + if (virtualHost != null) { + target = virtualHost; + } else { + final URI requestURI = wrapper.getURI(); + if (requestURI.isAbsolute()) { + target = URIUtils.extractHost(requestURI); + } + } + if (target == null) { + target = route.getTargetHost(); + } + + // Reset headers on the request wrapper + wrapper.resetHeaders(); + // Re-write request URI if needed + rewriteRequestURI(wrapper, route); + + // Populate the execution context + context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target); + context.setAttribute(ClientContext.ROUTE, route); + context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn); + + // Run request protocol interceptors + requestExec.preProcess(wrapper, httpProcessor, context); + + response = tryExecute(roureq, context); + if (response == null) { + // Need to start over + continue; + } + + // Run response protocol interceptors + response.setParams(params); + requestExec.postProcess(response, httpProcessor, context); + + + // The connection is in or can be brought to a re-usable state. + reuse = reuseStrategy.keepAlive(response, context); + if (reuse) { + // Set the idle duration of this connection + final long duration = keepAliveStrategy.getKeepAliveDuration(response, context); + if (this.log.isDebugEnabled()) { + final String s; + if (duration > 0) { + s = "for " + duration + " " + TimeUnit.MILLISECONDS; + } else { + s = "indefinitely"; + } + this.log.debug("Connection can be kept alive " + s); + } + managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS); + } + + final RoutedRequest followup = handleResponse(roureq, response, context); + if (followup == null) { + done = true; + } else { + if (reuse) { + // Make sure the response body is fully consumed, if present + final HttpEntity entity = response.getEntity(); + EntityUtils.consume(entity); + // entity consumed above is not an auto-release entity, + // need to mark the connection re-usable explicitly + managedConn.markReusable(); + } else { + managedConn.close(); + if (proxyAuthState.getState().compareTo(AuthProtocolState.CHALLENGED) > 0 + && proxyAuthState.getAuthScheme() != null + && proxyAuthState.getAuthScheme().isConnectionBased()) { + this.log.debug("Resetting proxy auth state"); + proxyAuthState.reset(); + } + if (targetAuthState.getState().compareTo(AuthProtocolState.CHALLENGED) > 0 + && targetAuthState.getAuthScheme() != null + && targetAuthState.getAuthScheme().isConnectionBased()) { + this.log.debug("Resetting target auth state"); + targetAuthState.reset(); + } + } + // check if we can use the same connection for the followup + if (!followup.getRoute().equals(roureq.getRoute())) { + releaseConnection(); + } + roureq = followup; + } + + if (managedConn != null) { + if (userToken == null) { + userToken = userTokenHandler.getUserToken(context); + context.setAttribute(ClientContext.USER_TOKEN, userToken); + } + if (userToken != null) { + managedConn.setState(userToken); + } + } + + } // while not done + + + // check for entity, release connection if possible + if ((response == null) || (response.getEntity() == null) || + !response.getEntity().isStreaming()) { + // connection not needed and (assumed to be) in re-usable state + if (reuse) { + managedConn.markReusable(); + } + releaseConnection(); + } else { + // install an auto-release entity + HttpEntity entity = response.getEntity(); + entity = new BasicManagedEntity(entity, managedConn, reuse); + response.setEntity(entity); + } + + return response; + + } catch (final ConnectionShutdownException ex) { + final InterruptedIOException ioex = new InterruptedIOException( + "Connection has been shut down"); + ioex.initCause(ex); + throw ioex; + } catch (final HttpException ex) { + abortConnection(); + throw ex; + } catch (final IOException ex) { + abortConnection(); + throw ex; + } catch (final RuntimeException ex) { + abortConnection(); + throw ex; + } + } // execute + + /** + * Establish connection either directly or through a tunnel and retry in case of + * a recoverable I/O failure + */ + private void tryConnect( + final RoutedRequest req, final HttpContext context) throws HttpException, IOException { + final HttpRoute route = req.getRoute(); + final HttpRequest wrapper = req.getRequest(); + + int connectCount = 0; + for (;;) { + context.setAttribute(ExecutionContext.HTTP_REQUEST, wrapper); + // Increment connect count + connectCount++; + try { + if (!managedConn.isOpen()) { + managedConn.open(route, context, params); + } else { + managedConn.setSocketTimeout(HttpConnectionParams.getSoTimeout(params)); + } + establishRoute(route, context); + break; + } catch (final IOException ex) { + try { + managedConn.close(); + } catch (final IOException ignore) { + } + if (retryHandler.retryRequest(ex, connectCount, context)) { + if (this.log.isInfoEnabled()) { + this.log.info("I/O exception ("+ ex.getClass().getName() + + ") caught when connecting to " + + route + + ": " + + ex.getMessage()); + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage(), ex); + } + this.log.info("Retrying connect to " + route); + } + } else { + throw ex; + } + } + } + } + + /** + * Execute request and retry in case of a recoverable I/O failure + */ + private HttpResponse tryExecute( + final RoutedRequest req, final HttpContext context) throws HttpException, IOException { + final RequestWrapper wrapper = req.getRequest(); + final HttpRoute route = req.getRoute(); + HttpResponse response = null; + + Exception retryReason = null; + for (;;) { + // Increment total exec count (with redirects) + execCount++; + // Increment exec count for this particular request + wrapper.incrementExecCount(); + if (!wrapper.isRepeatable()) { + this.log.debug("Cannot retry non-repeatable request"); + if (retryReason != null) { + throw new NonRepeatableRequestException("Cannot retry request " + + "with a non-repeatable request entity. The cause lists the " + + "reason the original request failed.", retryReason); + } else { + throw new NonRepeatableRequestException("Cannot retry request " + + "with a non-repeatable request entity."); + } + } + + try { + if (!managedConn.isOpen()) { + // If we have a direct route to the target host + // just re-open connection and re-try the request + if (!route.isTunnelled()) { + this.log.debug("Reopening the direct connection."); + managedConn.open(route, context, params); + } else { + // otherwise give up + this.log.debug("Proxied connection. Need to start over."); + break; + } + } + + if (this.log.isDebugEnabled()) { + this.log.debug("Attempt " + execCount + " to execute request"); + } + response = requestExec.execute(wrapper, managedConn, context); + break; + + } catch (final IOException ex) { + this.log.debug("Closing the connection."); + try { + managedConn.close(); + } catch (final IOException ignore) { + } + if (retryHandler.retryRequest(ex, wrapper.getExecCount(), context)) { + if (this.log.isInfoEnabled()) { + this.log.info("I/O exception ("+ ex.getClass().getName() + + ") caught when processing request to " + + route + + ": " + + ex.getMessage()); + } + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage(), ex); + } + if (this.log.isInfoEnabled()) { + this.log.info("Retrying request to " + route); + } + retryReason = ex; + } else { + if (ex instanceof NoHttpResponseException) { + final NoHttpResponseException updatedex = new NoHttpResponseException( + route.getTargetHost().toHostString() + " failed to respond"); + updatedex.setStackTrace(ex.getStackTrace()); + throw updatedex; + } else { + throw ex; + } + } + } + } + return response; + } + + /** + * Returns the connection back to the connection manager + * and prepares for retrieving a new connection during + * the next request. + */ + protected void releaseConnection() { + // Release the connection through the ManagedConnection instead of the + // ConnectionManager directly. This lets the connection control how + // it is released. + try { + managedConn.releaseConnection(); + } catch(final IOException ignored) { + this.log.debug("IOException releasing connection", ignored); + } + managedConn = null; + } + + /** + * Determines the route for a request. + * Called by {@link #execute} + * to determine the route for either the original or a followup request. + * + * @param targetHost the target host for the request. + * Implementations may accept null + * if they can still determine a route, for example + * to a default target or by inspecting the request. + * @param request the request to execute + * @param context the context to use for the execution, + * never null + * + * @return the route the request should take + * + * @throws HttpException in case of a problem + */ + protected HttpRoute determineRoute(final HttpHost targetHost, + final HttpRequest request, + final HttpContext context) + throws HttpException { + return this.routePlanner.determineRoute( + targetHost != null ? targetHost : (HttpHost) request.getParams() + .getParameter(ClientPNames.DEFAULT_HOST), + request, context); + } + + + /** + * Establishes the target route. + * + * @param route the route to establish + * @param context the context for the request execution + * + * @throws HttpException in case of a problem + * @throws IOException in case of an IO problem + */ + protected void establishRoute(final HttpRoute route, final HttpContext context) + throws HttpException, IOException { + + final HttpRouteDirector rowdy = new BasicRouteDirector(); + int step; + do { + final HttpRoute fact = managedConn.getRoute(); + step = rowdy.nextStep(route, fact); + + switch (step) { + + case HttpRouteDirector.CONNECT_TARGET: + case HttpRouteDirector.CONNECT_PROXY: + managedConn.open(route, context, this.params); + break; + + case HttpRouteDirector.TUNNEL_TARGET: { + final boolean secure = createTunnelToTarget(route, context); + this.log.debug("Tunnel to target created."); + managedConn.tunnelTarget(secure, this.params); + } break; + + case HttpRouteDirector.TUNNEL_PROXY: { + // The most simple example for this case is a proxy chain + // of two proxies, where P1 must be tunnelled to P2. + // route: Source -> P1 -> P2 -> Target (3 hops) + // fact: Source -> P1 -> Target (2 hops) + final int hop = fact.getHopCount()-1; // the hop to establish + final boolean secure = createTunnelToProxy(route, hop, context); + this.log.debug("Tunnel to proxy created."); + managedConn.tunnelProxy(route.getHopTarget(hop), + secure, this.params); + } break; + + + case HttpRouteDirector.LAYER_PROTOCOL: + managedConn.layerProtocol(context, this.params); + break; + + case HttpRouteDirector.UNREACHABLE: + throw new HttpException("Unable to establish route: " + + "planned = " + route + "; current = " + fact); + case HttpRouteDirector.COMPLETE: + // do nothing + break; + default: + throw new IllegalStateException("Unknown step indicator " + + step + " from RouteDirector."); + } + + } while (step > HttpRouteDirector.COMPLETE); + + } // establishConnection + + + /** + * Creates a tunnel to the target server. + * The connection must be established to the (last) proxy. + * A CONNECT request for tunnelling through the proxy will + * be created and sent, the response received and checked. + * This method does not update the connection with + * information about the tunnel, that is left to the caller. + * + * @param route the route to establish + * @param context the context for request execution + * + * @return true if the tunnelled route is secure, + * false otherwise. + * The implementation here always returns false, + * but derived classes may override. + * + * @throws HttpException in case of a problem + * @throws IOException in case of an IO problem + */ + protected boolean createTunnelToTarget(final HttpRoute route, + final HttpContext context) + throws HttpException, IOException { + + final HttpHost proxy = route.getProxyHost(); + final HttpHost target = route.getTargetHost(); + HttpResponse response = null; + + for (;;) { + if (!this.managedConn.isOpen()) { + this.managedConn.open(route, context, this.params); + } + + final HttpRequest connect = createConnectRequest(route, context); + connect.setParams(this.params); + + // Populate the execution context + context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target); + context.setAttribute(ClientContext.ROUTE, route); + context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy); + context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn); + context.setAttribute(ExecutionContext.HTTP_REQUEST, connect); + + this.requestExec.preProcess(connect, this.httpProcessor, context); + + response = this.requestExec.execute(connect, this.managedConn, context); + + response.setParams(this.params); + this.requestExec.postProcess(response, this.httpProcessor, context); + + final int status = response.getStatusLine().getStatusCode(); + if (status < 200) { + throw new HttpException("Unexpected response to CONNECT request: " + + response.getStatusLine()); + } + + if (HttpClientParams.isAuthenticating(this.params)) { + if (this.authenticator.isAuthenticationRequested(proxy, response, + this.proxyAuthStrategy, this.proxyAuthState, context)) { + if (this.authenticator.authenticate(proxy, response, + this.proxyAuthStrategy, this.proxyAuthState, context)) { + // Retry request + if (this.reuseStrategy.keepAlive(response, context)) { + this.log.debug("Connection kept alive"); + // Consume response content + final HttpEntity entity = response.getEntity(); + EntityUtils.consume(entity); + } else { + this.managedConn.close(); + } + } else { + break; + } + } else { + break; + } + } + } + + final int status = response.getStatusLine().getStatusCode(); + + if (status > 299) { + + // Buffer response content + final HttpEntity entity = response.getEntity(); + if (entity != null) { + response.setEntity(new BufferedHttpEntity(entity)); + } + + this.managedConn.close(); + throw new TunnelRefusedException("CONNECT refused by proxy: " + + response.getStatusLine(), response); + } + + this.managedConn.markReusable(); + + // How to decide on security of the tunnelled connection? + // The socket factory knows only about the segment to the proxy. + // Even if that is secure, the hop to the target may be insecure. + // Leave it to derived classes, consider insecure by default here. + return false; + + } // createTunnelToTarget + + + + /** + * Creates a tunnel to an intermediate proxy. + * This method is not implemented in this class. + * It just throws an exception here. + * + * @param route the route to establish + * @param hop the hop in the route to establish now. + * route.getHopTarget(hop) + * will return the proxy to tunnel to. + * @param context the context for request execution + * + * @return true if the partially tunnelled connection + * is secure, false otherwise. + * + * @throws HttpException in case of a problem + * @throws IOException in case of an IO problem + */ + protected boolean createTunnelToProxy(final HttpRoute route, final int hop, + final HttpContext context) + throws HttpException, IOException { + + // Have a look at createTunnelToTarget and replicate the parts + // you need in a custom derived class. If your proxies don't require + // authentication, it is not too hard. But for the stock version of + // HttpClient, we cannot make such simplifying assumptions and would + // have to include proxy authentication code. The HttpComponents team + // is currently not in a position to support rarely used code of this + // complexity. Feel free to submit patches that refactor the code in + // createTunnelToTarget to facilitate re-use for proxy tunnelling. + + throw new HttpException("Proxy chains are not supported."); + } + + + + /** + * Creates the CONNECT request for tunnelling. + * Called by {@link #createTunnelToTarget createTunnelToTarget}. + * + * @param route the route to establish + * @param context the context for request execution + * + * @return the CONNECT request for tunnelling + */ + protected HttpRequest createConnectRequest(final HttpRoute route, + final HttpContext context) { + // see RFC 2817, section 5.2 and + // INTERNET-DRAFT: Tunneling TCP based protocols through + // Web proxy servers + + final HttpHost target = route.getTargetHost(); + + final String host = target.getHostName(); + int port = target.getPort(); + if (port < 0) { + final Scheme scheme = connManager.getSchemeRegistry(). + getScheme(target.getSchemeName()); + port = scheme.getDefaultPort(); + } + + final StringBuilder buffer = new StringBuilder(host.length() + 6); + buffer.append(host); + buffer.append(':'); + buffer.append(Integer.toString(port)); + + final String authority = buffer.toString(); + final ProtocolVersion ver = HttpProtocolParams.getVersion(params); + final HttpRequest req = new BasicHttpRequest + ("CONNECT", authority, ver); + + return req; + } + + + /** + * Analyzes a response to check need for a followup. + * + * @param roureq the request and route. + * @param response the response to analayze + * @param context the context used for the current request execution + * + * @return the followup request and route if there is a followup, or + * null if the response should be returned as is + * + * @throws HttpException in case of a problem + * @throws IOException in case of an IO problem + */ + protected RoutedRequest handleResponse(final RoutedRequest roureq, + final HttpResponse response, + final HttpContext context) + throws HttpException, IOException { + + final HttpRoute route = roureq.getRoute(); + final RequestWrapper request = roureq.getRequest(); + + final HttpParams params = request.getParams(); + + if (HttpClientParams.isAuthenticating(params)) { + HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); + if (target == null) { + target = route.getTargetHost(); + } + if (target.getPort() < 0) { + final Scheme scheme = connManager.getSchemeRegistry().getScheme(target); + target = new HttpHost(target.getHostName(), scheme.getDefaultPort(), target.getSchemeName()); + } + + final boolean targetAuthRequested = this.authenticator.isAuthenticationRequested( + target, response, this.targetAuthStrategy, targetAuthState, context); + + HttpHost proxy = route.getProxyHost(); + // if proxy is not set use target host instead + if (proxy == null) { + proxy = route.getTargetHost(); + } + final boolean proxyAuthRequested = this.authenticator.isAuthenticationRequested( + proxy, response, this.proxyAuthStrategy, proxyAuthState, context); + + if (targetAuthRequested) { + if (this.authenticator.authenticate(target, response, + this.targetAuthStrategy, this.targetAuthState, context)) { + // Re-try the same request via the same route + return roureq; + } + } + if (proxyAuthRequested) { + if (this.authenticator.authenticate(proxy, response, + this.proxyAuthStrategy, this.proxyAuthState, context)) { + // Re-try the same request via the same route + return roureq; + } + } + } + + if (HttpClientParams.isRedirecting(params) && + this.redirectStrategy.isRedirected(request, response, context)) { + + if (redirectCount >= maxRedirects) { + throw new RedirectException("Maximum redirects (" + + maxRedirects + ") exceeded"); + } + redirectCount++; + + // Virtual host cannot be used any longer + virtualHost = null; + + final HttpUriRequest redirect = redirectStrategy.getRedirect(request, response, context); + final HttpRequest orig = request.getOriginal(); + redirect.setHeaders(orig.getAllHeaders()); + + final URI uri = redirect.getURI(); + final HttpHost newTarget = URIUtils.extractHost(uri); + if (newTarget == null) { + throw new ProtocolException("Redirect URI does not specify a valid host name: " + uri); + } + + // Reset auth states if redirecting to another host + if (!route.getTargetHost().equals(newTarget)) { + this.log.debug("Resetting target auth state"); + targetAuthState.reset(); + final AuthScheme authScheme = proxyAuthState.getAuthScheme(); + if (authScheme != null && authScheme.isConnectionBased()) { + this.log.debug("Resetting proxy auth state"); + proxyAuthState.reset(); + } + } + + final RequestWrapper wrapper = wrapRequest(redirect); + wrapper.setParams(params); + + final HttpRoute newRoute = determineRoute(newTarget, wrapper, context); + final RoutedRequest newRequest = new RoutedRequest(wrapper, newRoute); + + if (this.log.isDebugEnabled()) { + this.log.debug("Redirecting to '" + uri + "' via " + newRoute); + } + + return newRequest; + } + + return null; + } // handleResponse + + + /** + * Shuts down the connection. + * This method is called from a catch block in + * {@link #execute execute} during exception handling. + */ + private void abortConnection() { + final ManagedClientConnection mcc = managedConn; + if (mcc != null) { + // we got here as the result of an exception + // no response will be returned, release the connection + managedConn = null; + try { + mcc.abortConnection(); + } catch (final IOException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage(), ex); + } + } + // ensure the connection manager properly releases this connection + try { + mcc.releaseConnection(); + } catch(final IOException ignored) { + this.log.debug("Error releasing connection", ignored); + } + } + } // abortConnection + + +} // class DefaultClientRequestDirector diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultServiceUnavailableRetryStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultServiceUnavailableRetryStrategy.java new file mode 100644 index 000000000..8e7279aab --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultServiceUnavailableRetryStrategy.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of the {@link ServiceUnavailableRetryStrategy} interface. + * that retries 503 (Service Unavailable) responses for a fixed number of times + * at a fixed interval. + * + * @since 4.2 + */ +@Immutable +public class DefaultServiceUnavailableRetryStrategy implements ServiceUnavailableRetryStrategy { + + /** + * Maximum number of allowed retries if the server responds with a HTTP code + * in our retry code list. Default value is 1. + */ + private final int maxRetries; + + /** + * Retry interval between subsequent requests, in milliseconds. Default + * value is 1 second. + */ + private final long retryInterval; + + public DefaultServiceUnavailableRetryStrategy(final int maxRetries, final int retryInterval) { + super(); + Args.positive(maxRetries, "Max retries"); + Args.positive(retryInterval, "Retry interval"); + this.maxRetries = maxRetries; + this.retryInterval = retryInterval; + } + + public DefaultServiceUnavailableRetryStrategy() { + this(1, 1000); + } + + public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) { + return executionCount <= maxRetries && + response.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE; + } + + public long getRetryInterval() { + return retryInterval; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultTargetAuthenticationHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultTargetAuthenticationHandler.java new file mode 100644 index 000000000..a2a57e1d5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultTargetAuthenticationHandler.java @@ -0,0 +1,91 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.List; +import java.util.Map; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; +import ch.boye.httpclientandroidlib.auth.params.AuthPNames; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default {@link ch.boye.httpclientandroidlib.client.AuthenticationHandler} implementation + * for target host authentication. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link TargetAuthenticationStrategy} + */ +@Deprecated +@Immutable +public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHandler { + + public DefaultTargetAuthenticationHandler() { + super(); + } + + public boolean isAuthenticationRequested( + final HttpResponse response, + final HttpContext context) { + Args.notNull(response, "HTTP response"); + final int status = response.getStatusLine().getStatusCode(); + return status == HttpStatus.SC_UNAUTHORIZED; + } + + public Map getChallenges( + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + Args.notNull(response, "HTTP response"); + final Header[] headers = response.getHeaders(AUTH.WWW_AUTH); + return parseChallenges(headers); + } + + @Override + protected List getAuthPreferences( + final HttpResponse response, + final HttpContext context) { + @SuppressWarnings("unchecked") + final + List authpref = (List) response.getParams().getParameter( + AuthPNames.TARGET_AUTH_PREF); + if (authpref != null) { + return authpref; + } else { + return super.getAuthPreferences(response, context); + } + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultUserTokenHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultUserTokenHandler.java new file mode 100644 index 000000000..c6d2a8daa --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/DefaultUserTokenHandler.java @@ -0,0 +1,101 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.security.Principal; + +import javax.net.ssl.SSLSession; + +import ch.boye.httpclientandroidlib.HttpConnection; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.client.UserTokenHandler; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Default implementation of {@link UserTokenHandler}. This class will use + * an instance of {@link Principal} as a state object for HTTP connections, + * if it can be obtained from the given execution context. This helps ensure + * persistent connections created with a particular user identity within + * a particular security context can be reused by the same user only. + *

    + * DefaultUserTokenHandler will use the user principle of connection + * based authentication schemes such as NTLM or that of the SSL session + * with the client authentication turned on. If both are unavailable, + * null token will be returned. + * + * @since 4.0 + */ +@Immutable +public class DefaultUserTokenHandler implements UserTokenHandler { + + public static final DefaultUserTokenHandler INSTANCE = new DefaultUserTokenHandler(); + + public Object getUserToken(final HttpContext context) { + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + + Principal userPrincipal = null; + + final AuthState targetAuthState = clientContext.getTargetAuthState(); + if (targetAuthState != null) { + userPrincipal = getAuthPrincipal(targetAuthState); + if (userPrincipal == null) { + final AuthState proxyAuthState = clientContext.getProxyAuthState(); + userPrincipal = getAuthPrincipal(proxyAuthState); + } + } + + if (userPrincipal == null) { + final HttpConnection conn = clientContext.getConnection(); + if (conn.isOpen() && conn instanceof ManagedHttpClientConnection) { + final SSLSession sslsession = ((ManagedHttpClientConnection) conn).getSSLSession(); + if (sslsession != null) { + userPrincipal = sslsession.getLocalPrincipal(); + } + } + } + + return userPrincipal; + } + + private static Principal getAuthPrincipal(final AuthState authState) { + final AuthScheme scheme = authState.getAuthScheme(); + if (scheme != null && scheme.isComplete() && scheme.isConnectionBased()) { + final Credentials creds = authState.getCredentials(); + if (creds != null) { + return creds.getUserPrincipal(); + } + } + return null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/EntityEnclosingRequestWrapper.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/EntityEnclosingRequestWrapper.java new file mode 100644 index 000000000..45fbbe0a6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/EntityEnclosingRequestWrapper.java @@ -0,0 +1,113 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * A wrapper class for {@link HttpEntityEnclosingRequest}s that can + * be used to change properties of the current request without + * modifying the original object. + *

    + * This class is also capable of resetting the request headers to + * the state of the original request. + * + * @since 4.0 + * + * @deprecated (4.3) do not use. + */ +@Deprecated +@NotThreadSafe // e.g. [gs]etEntity() +public class EntityEnclosingRequestWrapper extends RequestWrapper + implements HttpEntityEnclosingRequest { + + private HttpEntity entity; + private boolean consumed; + + public EntityEnclosingRequestWrapper(final HttpEntityEnclosingRequest request) + throws ProtocolException { + super(request); + setEntity(request.getEntity()); + } + + public HttpEntity getEntity() { + return this.entity; + } + + public void setEntity(final HttpEntity entity) { + this.entity = entity != null ? new EntityWrapper(entity) : null; + this.consumed = false; + } + + public boolean expectContinue() { + final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE); + return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue()); + } + + @Override + public boolean isRepeatable() { + return this.entity == null || this.entity.isRepeatable() || !this.consumed; + } + + class EntityWrapper extends HttpEntityWrapper { + + EntityWrapper(final HttpEntity entity) { + super(entity); + } + + @Override + public void consumeContent() throws IOException { + consumed = true; + super.consumeContent(); + } + + @Override + public InputStream getContent() throws IOException { + consumed = true; + return super.getContent(); + } + + @Override + public void writeTo(final OutputStream outstream) throws IOException { + consumed = true; + super.writeTo(outstream); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionMetrics.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionMetrics.java new file mode 100644 index 000000000..888e7e3a5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionMetrics.java @@ -0,0 +1,156 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Collection of different counters used to gather metrics for {@link FutureRequestExecutionService}. + */ +public final class FutureRequestExecutionMetrics { + + private final AtomicLong activeConnections = new AtomicLong(); + private final AtomicLong scheduledConnections = new AtomicLong(); + private final DurationCounter successfulConnections = new DurationCounter(); + private final DurationCounter failedConnections = new DurationCounter(); + private final DurationCounter requests = new DurationCounter(); + private final DurationCounter tasks = new DurationCounter(); + + FutureRequestExecutionMetrics() { + } + + AtomicLong getActiveConnections() { + return activeConnections; + } + + AtomicLong getScheduledConnections() { + return scheduledConnections; + } + + DurationCounter getSuccessfulConnections() { + return successfulConnections; + } + + DurationCounter getFailedConnections() { + return failedConnections; + } + + DurationCounter getRequests() { + return requests; + } + + DurationCounter getTasks() { + return tasks; + } + + public long getActiveConnectionCount() { + return activeConnections.get(); + } + + public long getScheduledConnectionCount() { + return scheduledConnections.get(); + } + + public long getSuccessfulConnectionCount() { + return successfulConnections.count(); + } + + public long getSuccessfulConnectionAverageDuration() { + return successfulConnections.averageDuration(); + } + + public long getFailedConnectionCount() { + return failedConnections.count(); + } + + public long getFailedConnectionAverageDuration() { + return failedConnections.averageDuration(); + } + + public long getRequestCount() { + return requests.count(); + } + + public long getRequestAverageDuration() { + return requests.averageDuration(); + } + + public long getTaskCount() { + return tasks.count(); + } + + public long getTaskAverageDuration() { + return tasks.averageDuration(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("[activeConnections=").append(activeConnections) + .append(", scheduledConnections=").append(scheduledConnections) + .append(", successfulConnections=").append(successfulConnections) + .append(", failedConnections=").append(failedConnections) + .append(", requests=").append(requests) + .append(", tasks=").append(tasks) + .append("]"); + return builder.toString(); + } + + /** + * A counter that can measure duration and number of events. + */ + static class DurationCounter { + + private final AtomicLong count = new AtomicLong(0); + private final AtomicLong cumulativeDuration = new AtomicLong(0); + + public void increment(final long startTime) { + count.incrementAndGet(); + cumulativeDuration.addAndGet(System.currentTimeMillis() - startTime); + } + + public long count() { + return count.get(); + } + + public long averageDuration() { + final long counter = count.get(); + return counter > 0 ? cumulativeDuration.get() / counter : 0; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("[count=").append(count()) + .append(", averageDuration=").append(averageDuration()) + .append("]"); + return builder.toString(); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionService.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionService.java new file mode 100644 index 000000000..26fa9dba3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/FutureRequestExecutionService.java @@ -0,0 +1,142 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.ResponseHandler; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.concurrent.FutureCallback; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * HttpAsyncClientWithFuture wraps calls to execute with a {@link HttpRequestFutureTask} + * and schedules them using the provided executor service. Scheduled calls may be cancelled. + */ +@ThreadSafe +public class FutureRequestExecutionService implements Closeable { + + private final HttpClient httpclient; + private final ExecutorService executorService; + private final FutureRequestExecutionMetrics metrics = new FutureRequestExecutionMetrics(); + private final AtomicBoolean closed = new AtomicBoolean(false); + + /** + * Create a new FutureRequestExecutionService. + * + * @param httpclient + * you should tune your httpclient instance to match your needs. You should + * align the max number of connections in the pool and the number of threads + * in the executor; it doesn't make sense to have more threads than connections + * and if you have less connections than threads, the threads will just end up + * blocking on getting a connection from the pool. + * @param executorService + * any executorService will do here. E.g. + * {@link java.util.concurrent.Executors#newFixedThreadPool(int)} + */ + public FutureRequestExecutionService( + final HttpClient httpclient, + final ExecutorService executorService) { + this.httpclient = httpclient; + this.executorService = executorService; + } + + /** + * Schedule a request for execution. + * + * @param + * + * @param request + * request to execute + * @param responseHandler + * handler that will process the response. + * @return HttpAsyncClientFutureTask for the scheduled request. + * @throws InterruptedException + */ + public HttpRequestFutureTask execute( + final HttpUriRequest request, + final HttpContext context, + final ResponseHandler responseHandler) { + return execute(request, context, responseHandler, null); + } + + /** + * Schedule a request for execution. + * + * @param + * + * @param request + * request to execute + * @param context + * optional context; use null if not needed. + * @param responseHandler + * handler that will process the response. + * @param callback + * callback handler that will be called when the request is scheduled, + * started, completed, failed, or cancelled. + * @return HttpAsyncClientFutureTask for the scheduled request. + * @throws InterruptedException + */ + public HttpRequestFutureTask execute( + final HttpUriRequest request, + final HttpContext context, + final ResponseHandler responseHandler, + final FutureCallback callback) { + if(closed.get()) { + throw new IllegalStateException("Close has been called on this httpclient instance."); + } + metrics.getScheduledConnections().incrementAndGet(); + final HttpRequestTaskCallable callable = new HttpRequestTaskCallable( + httpclient, request, context, responseHandler, callback, metrics); + final HttpRequestFutureTask httpRequestFutureTask = new HttpRequestFutureTask( + request, callable); + executorService.execute(httpRequestFutureTask); + + return httpRequestFutureTask; + } + + /** + * @return metrics gathered for this instance. + * @see FutureRequestExecutionMetrics + */ + public FutureRequestExecutionMetrics metrics() { + return metrics; + } + + public void close() throws IOException { + closed.set(true); + executorService.shutdownNow(); + if (httpclient instanceof Closeable) { + ((Closeable) httpclient).close(); + } + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpAuthenticator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpAuthenticator.java new file mode 100644 index 000000000..745ae4735 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpAuthenticator.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * @deprecated (4.3) reserved for internal use. + * + */ +@Deprecated +public class HttpAuthenticator extends ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator { + + public HttpAuthenticator(final HttpClientAndroidLog log) { + super(log); + } + + public HttpAuthenticator() { + super(); + } + + public boolean authenticate ( + final HttpHost host, + final HttpResponse response, + final AuthenticationStrategy authStrategy, + final AuthState authState, + final HttpContext context) { + return handleAuthChallenge(host, response, authStrategy, authState, context); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClientBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClientBuilder.java new file mode 100644 index 000000000..00313e668 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClientBuilder.java @@ -0,0 +1,954 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.Closeable; +import java.net.ProxySelector; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; + +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.client.BackoffManager; +import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler; +import ch.boye.httpclientandroidlib.client.RedirectStrategy; +import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy; +import ch.boye.httpclientandroidlib.client.UserTokenHandler; +import ch.boye.httpclientandroidlib.client.config.AuthSchemes; +import ch.boye.httpclientandroidlib.client.config.CookieSpecs; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.protocol.RequestAcceptEncoding; +import ch.boye.httpclientandroidlib.client.protocol.RequestAddCookies; +import ch.boye.httpclientandroidlib.client.protocol.RequestAuthCache; +import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl; +import ch.boye.httpclientandroidlib.client.protocol.RequestDefaultHeaders; +import ch.boye.httpclientandroidlib.client.protocol.RequestExpectContinue; +import ch.boye.httpclientandroidlib.client.protocol.ResponseContentEncoding; +import ch.boye.httpclientandroidlib.client.protocol.ResponseProcessCookies; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.config.RegistryBuilder; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.SSLConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.SSLContexts; +import ch.boye.httpclientandroidlib.conn.ssl.X509HostnameVerifier; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.NoConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.auth.BasicSchemeFactory; +import ch.boye.httpclientandroidlib.impl.auth.DigestSchemeFactory; +/* KerberosSchemeFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.impl.auth.NTLMSchemeFactory; +/* SPNegoSchemeFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.impl.conn.DefaultProxyRoutePlanner; +import ch.boye.httpclientandroidlib.impl.conn.DefaultRoutePlanner; +import ch.boye.httpclientandroidlib.impl.conn.DefaultSchemePortResolver; +import ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager; +import ch.boye.httpclientandroidlib.impl.conn.SystemDefaultRoutePlanner; +import ch.boye.httpclientandroidlib.impl.cookie.BestMatchSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.BrowserCompatSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.IgnoreSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.NetscapeDraftSpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.RFC2109SpecFactory; +import ch.boye.httpclientandroidlib.impl.cookie.RFC2965SpecFactory; +import ch.boye.httpclientandroidlib.impl.execchain.BackoffStrategyExec; +import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain; +import ch.boye.httpclientandroidlib.impl.execchain.MainClientExec; +import ch.boye.httpclientandroidlib.impl.execchain.ProtocolExec; +import ch.boye.httpclientandroidlib.impl.execchain.RedirectExec; +import ch.boye.httpclientandroidlib.impl.execchain.RetryExec; +import ch.boye.httpclientandroidlib.impl.execchain.ServiceUnavailableRetryExec; +import ch.boye.httpclientandroidlib.protocol.HttpProcessor; +import ch.boye.httpclientandroidlib.protocol.HttpProcessorBuilder; +import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor; +import ch.boye.httpclientandroidlib.protocol.RequestContent; +import ch.boye.httpclientandroidlib.protocol.RequestTargetHost; +import ch.boye.httpclientandroidlib.protocol.RequestUserAgent; +import ch.boye.httpclientandroidlib.util.TextUtils; +import ch.boye.httpclientandroidlib.util.VersionInfo; + +/** + * Builder for {@link CloseableHttpClient} instances. + *

    + * When a particular component is not explicitly this class will + * use its default implementation. System properties will be taken + * into account when configuring the default implementations when + * {@link #useSystemProperties()} method is called prior to calling + * {@link #build()}. + *

      + *
    • ssl.TrustManagerFactory.algorithm
    • + *
    • javax.net.ssl.trustStoreType
    • + *
    • javax.net.ssl.trustStore
    • + *
    • javax.net.ssl.trustStoreProvider
    • + *
    • javax.net.ssl.trustStorePassword
    • + *
    • ssl.KeyManagerFactory.algorithm
    • + *
    • javax.net.ssl.keyStoreType
    • + *
    • javax.net.ssl.keyStore
    • + *
    • javax.net.ssl.keyStoreProvider
    • + *
    • javax.net.ssl.keyStorePassword
    • + *
    • https.protocols
    • + *
    • https.cipherSuites
    • + *
    • http.proxyHost
    • + *
    • http.proxyPort
    • + *
    • http.nonProxyHosts
    • + *
    • http.keepAlive
    • + *
    • http.maxConnections
    • + *
    • http.agent
    • + *
    + *

    + * Please note that some settings used by this class can be mutually + * exclusive and may not apply when building {@link CloseableHttpClient} + * instances. + * + * @since 4.3 + */ +@NotThreadSafe +public class HttpClientBuilder { + + private HttpRequestExecutor requestExec; + private X509HostnameVerifier hostnameVerifier; + private LayeredConnectionSocketFactory sslSocketFactory; + private SSLContext sslcontext; + private HttpClientConnectionManager connManager; + private SchemePortResolver schemePortResolver; + private ConnectionReuseStrategy reuseStrategy; + private ConnectionKeepAliveStrategy keepAliveStrategy; + private AuthenticationStrategy targetAuthStrategy; + private AuthenticationStrategy proxyAuthStrategy; + private UserTokenHandler userTokenHandler; + private HttpProcessor httpprocessor; + + private LinkedList requestFirst; + private LinkedList requestLast; + private LinkedList responseFirst; + private LinkedList responseLast; + + private HttpRequestRetryHandler retryHandler; + private HttpRoutePlanner routePlanner; + private RedirectStrategy redirectStrategy; + private ConnectionBackoffStrategy connectionBackoffStrategy; + private BackoffManager backoffManager; + private ServiceUnavailableRetryStrategy serviceUnavailStrategy; + private Lookup authSchemeRegistry; + private Lookup cookieSpecRegistry; + private CookieStore cookieStore; + private CredentialsProvider credentialsProvider; + private String userAgent; + private HttpHost proxy; + private Collection defaultHeaders; + private SocketConfig defaultSocketConfig; + private ConnectionConfig defaultConnectionConfig; + private RequestConfig defaultRequestConfig; + + private boolean systemProperties; + private boolean redirectHandlingDisabled; + private boolean automaticRetriesDisabled; + private boolean contentCompressionDisabled; + private boolean cookieManagementDisabled; + private boolean authCachingDisabled; + private boolean connectionStateDisabled; + + private int maxConnTotal = 0; + private int maxConnPerRoute = 0; + + private List closeables; + + static final String DEFAULT_USER_AGENT; + static { + final VersionInfo vi = VersionInfo.loadVersionInfo + ("ch.boye.httpclientandroidlib.client", HttpClientBuilder.class.getClassLoader()); + final String release = (vi != null) ? + vi.getRelease() : VersionInfo.UNAVAILABLE; + DEFAULT_USER_AGENT = "Apache-HttpClient/" + release + " (java 1.5)"; + } + + public static HttpClientBuilder create() { + return new HttpClientBuilder(); + } + + protected HttpClientBuilder() { + super(); + } + + /** + * Assigns {@link HttpRequestExecutor} instance. + */ + public final HttpClientBuilder setRequestExecutor(final HttpRequestExecutor requestExec) { + this.requestExec = requestExec; + return this; + } + + /** + * Assigns {@link X509HostnameVerifier} instance. + *

    + * Please note this value can be overridden by the {@link #setConnectionManager( + * ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} and the {@link #setSSLSocketFactory( + * ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory)} methods. + */ + public final HttpClientBuilder setHostnameVerifier(final X509HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + + /** + * Assigns {@link SSLContext} instance. + *

    + *

    + * Please note this value can be overridden by the {@link #setConnectionManager( + * ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} and the {@link #setSSLSocketFactory( + * ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory)} methods. + */ + public final HttpClientBuilder setSslcontext(final SSLContext sslcontext) { + this.sslcontext = sslcontext; + return this; + } + + /** + * Assigns {@link LayeredConnectionSocketFactory} instance. + *

    + * Please note this value can be overridden by the {@link #setConnectionManager( + * ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method. + */ + public final HttpClientBuilder setSSLSocketFactory( + final LayeredConnectionSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + return this; + } + + /** + * Assigns maximum total connection value. + *

    + * Please note this value can be overridden by the {@link #setConnectionManager( + * ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method. + */ + public final HttpClientBuilder setMaxConnTotal(final int maxConnTotal) { + this.maxConnTotal = maxConnTotal; + return this; + } + + /** + * Assigns maximum connection per route value. + *

    + * Please note this value can be overridden by the {@link #setConnectionManager( + * ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method. + */ + public final HttpClientBuilder setMaxConnPerRoute(final int maxConnPerRoute) { + this.maxConnPerRoute = maxConnPerRoute; + return this; + } + + /** + * Assigns default {@link SocketConfig}. + *

    + * Please note this value can be overridden by the {@link #setConnectionManager( + * ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method. + */ + public final HttpClientBuilder setDefaultSocketConfig(final SocketConfig config) { + this.defaultSocketConfig = config; + return this; + } + + /** + * Assigns default {@link ConnectionConfig}. + *

    + * Please note this value can be overridden by the {@link #setConnectionManager( + * ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method. + */ + public final HttpClientBuilder setDefaultConnectionConfig(final ConnectionConfig config) { + this.defaultConnectionConfig = config; + return this; + } + + /** + * Assigns {@link HttpClientConnectionManager} instance. + */ + public final HttpClientBuilder setConnectionManager( + final HttpClientConnectionManager connManager) { + this.connManager = connManager; + return this; + } + + /** + * Assigns {@link ConnectionReuseStrategy} instance. + */ + public final HttpClientBuilder setConnectionReuseStrategy( + final ConnectionReuseStrategy reuseStrategy) { + this.reuseStrategy = reuseStrategy; + return this; + } + + /** + * Assigns {@link ConnectionKeepAliveStrategy} instance. + */ + public final HttpClientBuilder setKeepAliveStrategy( + final ConnectionKeepAliveStrategy keepAliveStrategy) { + this.keepAliveStrategy = keepAliveStrategy; + return this; + } + + /** + * Assigns {@link AuthenticationStrategy} instance for proxy + * authentication. + */ + public final HttpClientBuilder setTargetAuthenticationStrategy( + final AuthenticationStrategy targetAuthStrategy) { + this.targetAuthStrategy = targetAuthStrategy; + return this; + } + + /** + * Assigns {@link AuthenticationStrategy} instance for target + * host authentication. + */ + public final HttpClientBuilder setProxyAuthenticationStrategy( + final AuthenticationStrategy proxyAuthStrategy) { + this.proxyAuthStrategy = proxyAuthStrategy; + return this; + } + + /** + * Assigns {@link UserTokenHandler} instance. + *

    + * Please note this value can be overridden by the {@link #disableConnectionState()} + * method. + */ + public final HttpClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) { + this.userTokenHandler = userTokenHandler; + return this; + } + + /** + * Disables connection state tracking. + */ + public final HttpClientBuilder disableConnectionState() { + connectionStateDisabled = true; + return this; + } + + /** + * Assigns {@link SchemePortResolver} instance. + */ + public final HttpClientBuilder setSchemePortResolver( + final SchemePortResolver schemePortResolver) { + this.schemePortResolver = schemePortResolver; + return this; + } + + /** + * Assigns User-Agent value. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder setUserAgent(final String userAgent) { + this.userAgent = userAgent; + return this; + } + + /** + * Assigns default request header values. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder setDefaultHeaders(final Collection defaultHeaders) { + this.defaultHeaders = defaultHeaders; + return this; + } + + /** + * Adds this protocol interceptor to the head of the protocol processing list. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder addInterceptorFirst(final HttpResponseInterceptor itcp) { + if (itcp == null) { + return this; + } + if (responseFirst == null) { + responseFirst = new LinkedList(); + } + responseFirst.addFirst(itcp); + return this; + } + + /** + * Adds this protocol interceptor to the tail of the protocol processing list. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder addInterceptorLast(final HttpResponseInterceptor itcp) { + if (itcp == null) { + return this; + } + if (responseLast == null) { + responseLast = new LinkedList(); + } + responseLast.addLast(itcp); + return this; + } + + /** + * Adds this protocol interceptor to the head of the protocol processing list. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder addInterceptorFirst(final HttpRequestInterceptor itcp) { + if (itcp == null) { + return this; + } + if (requestFirst == null) { + requestFirst = new LinkedList(); + } + requestFirst.addFirst(itcp); + return this; + } + + /** + * Adds this protocol interceptor to the tail of the protocol processing list. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder addInterceptorLast(final HttpRequestInterceptor itcp) { + if (itcp == null) { + return this; + } + if (requestLast == null) { + requestLast = new LinkedList(); + } + requestLast.addLast(itcp); + return this; + } + + /** + * Disables state (cookie) management. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder disableCookieManagement() { + this.cookieManagementDisabled = true; + return this; + } + + /** + * Disables automatic content decompression. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder disableContentCompression() { + contentCompressionDisabled = true; + return this; + } + + /** + * Disables authentication scheme caching. + *

    + * Please note this value can be overridden by the {@link #setHttpProcessor( + * ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method. + */ + public final HttpClientBuilder disableAuthCaching() { + this.authCachingDisabled = true; + return this; + } + + /** + * Assigns {@link HttpProcessor} instance. + */ + public final HttpClientBuilder setHttpProcessor(final HttpProcessor httpprocessor) { + this.httpprocessor = httpprocessor; + return this; + } + + /** + * Assigns {@link HttpRequestRetryHandler} instance. + *

    + * Please note this value can be overridden by the {@link #disableAutomaticRetries()} + * method. + */ + public final HttpClientBuilder setRetryHandler(final HttpRequestRetryHandler retryHandler) { + this.retryHandler = retryHandler; + return this; + } + + /** + * Disables automatic request recovery and re-execution. + */ + public final HttpClientBuilder disableAutomaticRetries() { + automaticRetriesDisabled = true; + return this; + } + + /** + * Assigns default proxy value. + *

    + * Please note this value can be overridden by the {@link #setRoutePlanner( + * ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner)} method. + */ + public final HttpClientBuilder setProxy(final HttpHost proxy) { + this.proxy = proxy; + return this; + } + + /** + * Assigns {@link HttpRoutePlanner} instance. + */ + public final HttpClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) { + this.routePlanner = routePlanner; + return this; + } + + /** + * Assigns {@link RedirectStrategy} instance. + *

    + * Please note this value can be overridden by the {@link #disableRedirectHandling()} + * method. +` */ + public final HttpClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) { + this.redirectStrategy = redirectStrategy; + return this; + } + + /** + * Disables automatic redirect handling. + */ + public final HttpClientBuilder disableRedirectHandling() { + redirectHandlingDisabled = true; + return this; + } + + /** + * Assigns {@link ConnectionBackoffStrategy} instance. + */ + public final HttpClientBuilder setConnectionBackoffStrategy( + final ConnectionBackoffStrategy connectionBackoffStrategy) { + this.connectionBackoffStrategy = connectionBackoffStrategy; + return this; + } + + /** + * Assigns {@link BackoffManager} instance. + */ + public final HttpClientBuilder setBackoffManager(final BackoffManager backoffManager) { + this.backoffManager = backoffManager; + return this; + } + + /** + * Assigns {@link ServiceUnavailableRetryStrategy} instance. + */ + public final HttpClientBuilder setServiceUnavailableRetryStrategy( + final ServiceUnavailableRetryStrategy serviceUnavailStrategy) { + this.serviceUnavailStrategy = serviceUnavailStrategy; + return this; + } + + /** + * Assigns default {@link CookieStore} instance which will be used for + * request execution if not explicitly set in the client execution context. + */ + public final HttpClientBuilder setDefaultCookieStore(final CookieStore cookieStore) { + this.cookieStore = cookieStore; + return this; + } + + /** + * Assigns default {@link CredentialsProvider} instance which will be used + * for request execution if not explicitly set in the client execution + * context. + */ + public final HttpClientBuilder setDefaultCredentialsProvider( + final CredentialsProvider credentialsProvider) { + this.credentialsProvider = credentialsProvider; + return this; + } + + /** + * Assigns default {@link ch.boye.httpclientandroidlib.auth.AuthScheme} registry which will + * be used for request execution if not explicitly set in the client execution + * context. + */ + public final HttpClientBuilder setDefaultAuthSchemeRegistry( + final Lookup authSchemeRegistry) { + this.authSchemeRegistry = authSchemeRegistry; + return this; + } + + /** + * Assigns default {@link ch.boye.httpclientandroidlib.cookie.CookieSpec} registry which will + * be used for request execution if not explicitly set in the client execution + * context. + */ + public final HttpClientBuilder setDefaultCookieSpecRegistry( + final Lookup cookieSpecRegistry) { + this.cookieSpecRegistry = cookieSpecRegistry; + return this; + } + + /** + * Assigns default {@link RequestConfig} instance which will be used + * for request execution if not explicitly set in the client execution + * context. + */ + public final HttpClientBuilder setDefaultRequestConfig(final RequestConfig config) { + this.defaultRequestConfig = config; + return this; + } + + /** + * Use system properties when creating and configuring default + * implementations. + */ + public final HttpClientBuilder useSystemProperties() { + systemProperties = true; + return this; + } + + /** + * For internal use. + */ + protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) { + return mainExec; + } + + /** + * For internal use. + */ + protected ClientExecChain decorateProtocolExec(final ClientExecChain protocolExec) { + return protocolExec; + } + + /** + * For internal use. + */ + protected void addCloseable(final Closeable closeable) { + if (closeable == null) { + return; + } + if (closeables == null) { + closeables = new ArrayList(); + } + closeables.add(closeable); + } + + private static String[] split(final String s) { + if (TextUtils.isBlank(s)) { + return null; + } + return s.split(" *, *"); + } + + public CloseableHttpClient build() { + // Create main request executor + HttpRequestExecutor requestExec = this.requestExec; + if (requestExec == null) { + requestExec = new HttpRequestExecutor(); + } + HttpClientConnectionManager connManager = this.connManager; + if (connManager == null) { + LayeredConnectionSocketFactory sslSocketFactory = this.sslSocketFactory; + if (sslSocketFactory == null) { + final String[] supportedProtocols = systemProperties ? split( + System.getProperty("https.protocols")) : null; + final String[] supportedCipherSuites = systemProperties ? split( + System.getProperty("https.cipherSuites")) : null; + X509HostnameVerifier hostnameVerifier = this.hostnameVerifier; + if (hostnameVerifier == null) { + hostnameVerifier = SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; + } + if (sslcontext != null) { + sslSocketFactory = new SSLConnectionSocketFactory( + sslcontext, supportedProtocols, supportedCipherSuites, hostnameVerifier); + } else { + if (systemProperties) { + sslSocketFactory = new SSLConnectionSocketFactory( + (SSLSocketFactory) SSLSocketFactory.getDefault(), + supportedProtocols, supportedCipherSuites, hostnameVerifier); + } else { + sslSocketFactory = new SSLConnectionSocketFactory( + SSLContexts.createDefault(), + hostnameVerifier); + } + } + } + @SuppressWarnings("resource") + final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager( + RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", sslSocketFactory) + .build()); + if (defaultSocketConfig != null) { + poolingmgr.setDefaultSocketConfig(defaultSocketConfig); + } + if (defaultConnectionConfig != null) { + poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig); + } + if (systemProperties) { + String s = System.getProperty("http.keepAlive", "true"); + if ("true".equalsIgnoreCase(s)) { + s = System.getProperty("http.maxConnections", "5"); + final int max = Integer.parseInt(s); + poolingmgr.setDefaultMaxPerRoute(max); + poolingmgr.setMaxTotal(2 * max); + } + } + if (maxConnTotal > 0) { + poolingmgr.setMaxTotal(maxConnTotal); + } + if (maxConnPerRoute > 0) { + poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute); + } + connManager = poolingmgr; + } + ConnectionReuseStrategy reuseStrategy = this.reuseStrategy; + if (reuseStrategy == null) { + if (systemProperties) { + final String s = System.getProperty("http.keepAlive", "true"); + if ("true".equalsIgnoreCase(s)) { + reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE; + } else { + reuseStrategy = NoConnectionReuseStrategy.INSTANCE; + } + } else { + reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE; + } + } + ConnectionKeepAliveStrategy keepAliveStrategy = this.keepAliveStrategy; + if (keepAliveStrategy == null) { + keepAliveStrategy = DefaultConnectionKeepAliveStrategy.INSTANCE; + } + AuthenticationStrategy targetAuthStrategy = this.targetAuthStrategy; + if (targetAuthStrategy == null) { + targetAuthStrategy = TargetAuthenticationStrategy.INSTANCE; + } + AuthenticationStrategy proxyAuthStrategy = this.proxyAuthStrategy; + if (proxyAuthStrategy == null) { + proxyAuthStrategy = ProxyAuthenticationStrategy.INSTANCE; + } + UserTokenHandler userTokenHandler = this.userTokenHandler; + if (userTokenHandler == null) { + if (!connectionStateDisabled) { + userTokenHandler = DefaultUserTokenHandler.INSTANCE; + } else { + userTokenHandler = NoopUserTokenHandler.INSTANCE; + } + } + ClientExecChain execChain = new MainClientExec( + requestExec, + connManager, + reuseStrategy, + keepAliveStrategy, + targetAuthStrategy, + proxyAuthStrategy, + userTokenHandler); + + execChain = decorateMainExec(execChain); + + HttpProcessor httpprocessor = this.httpprocessor; + if (httpprocessor == null) { + + String userAgent = this.userAgent; + if (userAgent == null) { + if (systemProperties) { + userAgent = System.getProperty("http.agent"); + } + if (userAgent == null) { + userAgent = DEFAULT_USER_AGENT; + } + } + + final HttpProcessorBuilder b = HttpProcessorBuilder.create(); + if (requestFirst != null) { + for (final HttpRequestInterceptor i: requestFirst) { + b.addFirst(i); + } + } + if (responseFirst != null) { + for (final HttpResponseInterceptor i: responseFirst) { + b.addFirst(i); + } + } + b.addAll( + new RequestDefaultHeaders(defaultHeaders), + new RequestContent(), + new RequestTargetHost(), + new RequestClientConnControl(), + new RequestUserAgent(userAgent), + new RequestExpectContinue()); + if (!cookieManagementDisabled) { + b.add(new RequestAddCookies()); + } + if (!contentCompressionDisabled) { + b.add(new RequestAcceptEncoding()); + } + if (!authCachingDisabled) { + b.add(new RequestAuthCache()); + } + if (!cookieManagementDisabled) { + b.add(new ResponseProcessCookies()); + } + if (!contentCompressionDisabled) { + b.add(new ResponseContentEncoding()); + } + if (requestLast != null) { + for (final HttpRequestInterceptor i: requestLast) { + b.addLast(i); + } + } + if (responseLast != null) { + for (final HttpResponseInterceptor i: responseLast) { + b.addLast(i); + } + } + httpprocessor = b.build(); + } + execChain = new ProtocolExec(execChain, httpprocessor); + + execChain = decorateProtocolExec(execChain); + + // Add request retry executor, if not disabled + if (!automaticRetriesDisabled) { + HttpRequestRetryHandler retryHandler = this.retryHandler; + if (retryHandler == null) { + retryHandler = DefaultHttpRequestRetryHandler.INSTANCE; + } + execChain = new RetryExec(execChain, retryHandler); + } + + HttpRoutePlanner routePlanner = this.routePlanner; + if (routePlanner == null) { + SchemePortResolver schemePortResolver = this.schemePortResolver; + if (schemePortResolver == null) { + schemePortResolver = DefaultSchemePortResolver.INSTANCE; + } + if (proxy != null) { + routePlanner = new DefaultProxyRoutePlanner(proxy, schemePortResolver); + } else if (systemProperties) { + routePlanner = new SystemDefaultRoutePlanner( + schemePortResolver, ProxySelector.getDefault()); + } else { + routePlanner = new DefaultRoutePlanner(schemePortResolver); + } + } + // Add redirect executor, if not disabled + if (!redirectHandlingDisabled) { + RedirectStrategy redirectStrategy = this.redirectStrategy; + if (redirectStrategy == null) { + redirectStrategy = DefaultRedirectStrategy.INSTANCE; + } + execChain = new RedirectExec(execChain, routePlanner, redirectStrategy); + } + + // Optionally, add service unavailable retry executor + final ServiceUnavailableRetryStrategy serviceUnavailStrategy = this.serviceUnavailStrategy; + if (serviceUnavailStrategy != null) { + execChain = new ServiceUnavailableRetryExec(execChain, serviceUnavailStrategy); + } + // Optionally, add connection back-off executor + final BackoffManager backoffManager = this.backoffManager; + final ConnectionBackoffStrategy connectionBackoffStrategy = this.connectionBackoffStrategy; + if (backoffManager != null && connectionBackoffStrategy != null) { + execChain = new BackoffStrategyExec(execChain, connectionBackoffStrategy, backoffManager); + } + + Lookup authSchemeRegistry = this.authSchemeRegistry; + if (authSchemeRegistry == null) { + authSchemeRegistry = RegistryBuilder.create() + .register(AuthSchemes.BASIC, new BasicSchemeFactory()) + .register(AuthSchemes.DIGEST, new DigestSchemeFactory()) + .register(AuthSchemes.NTLM, new NTLMSchemeFactory()) + /* SPNegoSchemeFactory removed by HttpClient for Android script. */ + /* KerberosSchemeFactory removed by HttpClient for Android script. */ + .build(); + } + Lookup cookieSpecRegistry = this.cookieSpecRegistry; + if (cookieSpecRegistry == null) { + cookieSpecRegistry = RegistryBuilder.create() + .register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory()) + .register(CookieSpecs.STANDARD, new RFC2965SpecFactory()) + .register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory()) + .register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecFactory()) + .register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecFactory()) + .register("rfc2109", new RFC2109SpecFactory()) + .register("rfc2965", new RFC2965SpecFactory()) + .build(); + } + + CookieStore defaultCookieStore = this.cookieStore; + if (defaultCookieStore == null) { + defaultCookieStore = new BasicCookieStore(); + } + + CredentialsProvider defaultCredentialsProvider = this.credentialsProvider; + if (defaultCredentialsProvider == null) { + if (systemProperties) { + defaultCredentialsProvider = new SystemDefaultCredentialsProvider(); + } else { + defaultCredentialsProvider = new BasicCredentialsProvider(); + } + } + + return new InternalHttpClient( + execChain, + connManager, + routePlanner, + cookieSpecRegistry, + authSchemeRegistry, + defaultCookieStore, + defaultCredentialsProvider, + defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT, + closeables != null ? new ArrayList(closeables) : null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClients.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClients.java new file mode 100644 index 000000000..294ac636b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpClients.java @@ -0,0 +1,85 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager; + +/** + * Factory methods for {@link CloseableHttpClient} instances. + * @since 4.3 + */ +@Immutable +public class HttpClients { + + private HttpClients() { + super(); + } + + /** + * Creates builder object for construction of custom + * {@link CloseableHttpClient} instances. + */ + public static HttpClientBuilder custom() { + return HttpClientBuilder.create(); + } + + /** + * Creates {@link CloseableHttpClient} instance with default + * configuration. + */ + public static CloseableHttpClient createDefault() { + return HttpClientBuilder.create().build(); + } + + /** + * Creates {@link CloseableHttpClient} instance with default + * configuration based on ssytem properties. + */ + public static CloseableHttpClient createSystem() { + return HttpClientBuilder.create().useSystemProperties().build(); + } + + /** + * Creates {@link CloseableHttpClient} instance that implements + * the most basic HTTP protocol support. + */ + public static CloseableHttpClient createMinimal() { + return new MinimalHttpClient(new PoolingHttpClientConnectionManager()); + } + + /** + * Creates {@link CloseableHttpClient} instance that implements + * the most basic HTTP protocol support. + */ + public static CloseableHttpClient createMinimal(final HttpClientConnectionManager connManager) { + return new MinimalHttpClient(connManager); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestFutureTask.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestFutureTask.java new file mode 100644 index 000000000..b5f932403 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestFutureTask.java @@ -0,0 +1,118 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.concurrent.FutureTask; + +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; + +/** + * FutureTask implementation that wraps a HttpAsyncClientCallable and exposes various task + * specific metrics. + * + * @param + */ +public class HttpRequestFutureTask extends FutureTask { + + private final HttpUriRequest request; + private final HttpRequestTaskCallable callable; + + public HttpRequestFutureTask( + final HttpUriRequest request, + final HttpRequestTaskCallable httpCallable) { + super(httpCallable); + this.request = request; + this.callable = httpCallable; + } + + /* + * (non-Javadoc) + * @see java.util.concurrent.FutureTask#cancel(boolean) + */ + @Override + public boolean cancel(final boolean mayInterruptIfRunning) { + callable.cancel(); + if (mayInterruptIfRunning) { + request.abort(); + } + return super.cancel(mayInterruptIfRunning); + } + + /** + * @return the time in millis the task was scheduled. + */ + public long scheduledTime() { + return callable.getScheduled(); + } + + /** + * @return the time in millis the task was started. + */ + public long startedTime() { + return callable.getStarted(); + } + + /** + * @return the time in millis the task was finished/cancelled. + */ + public long endedTime() { + if (isDone()) { + return callable.getEnded(); + } else { + throw new IllegalStateException("Task is not done yet"); + } + } + + /** + * @return the time in millis it took to make the request (excluding the time it was + * scheduled to be executed). + */ + public long requestDuration() { + if (isDone()) { + return endedTime() - startedTime(); + } else { + throw new IllegalStateException("Task is not done yet"); + } + } + + /** + * @return the time in millis it took to execute the task from the moment it was scheduled. + */ + public long taskDuration() { + if (isDone()) { + return endedTime() - scheduledTime(); + } else { + throw new IllegalStateException("Task is not done yet"); + } + } + + @Override + public String toString() { + return request.getRequestLine().getUri(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestTaskCallable.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestTaskCallable.java new file mode 100644 index 000000000..42e95f0aa --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/HttpRequestTaskCallable.java @@ -0,0 +1,119 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; + +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.ResponseHandler; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.concurrent.FutureCallback; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +class HttpRequestTaskCallable implements Callable { + + private final HttpUriRequest request; + private final HttpClient httpclient; + private final AtomicBoolean cancelled = new AtomicBoolean(false); + + private final long scheduled = System.currentTimeMillis(); + private long started = -1; + private long ended = -1; + + private final HttpContext context; + private final ResponseHandler responseHandler; + private final FutureCallback callback; + + private final FutureRequestExecutionMetrics metrics; + + HttpRequestTaskCallable( + final HttpClient httpClient, + final HttpUriRequest request, + final HttpContext context, + final ResponseHandler responseHandler, + final FutureCallback callback, + final FutureRequestExecutionMetrics metrics) { + this.httpclient = httpClient; + this.responseHandler = responseHandler; + this.request = request; + this.context = context; + this.callback = callback; + this.metrics = metrics; + } + + public long getScheduled() { + return scheduled; + } + + public long getStarted() { + return started; + } + + public long getEnded() { + return ended; + } + + public V call() throws Exception { + if (!cancelled.get()) { + try { + metrics.getActiveConnections().incrementAndGet(); + started = System.currentTimeMillis(); + try { + metrics.getScheduledConnections().decrementAndGet(); + final V result = httpclient.execute(request, responseHandler, context); + ended = System.currentTimeMillis(); + metrics.getSuccessfulConnections().increment(started); + if (callback != null) { + callback.completed(result); + } + return result; + } catch (final Exception e) { + metrics.getFailedConnections().increment(started); + ended = System.currentTimeMillis(); + if (callback != null) { + callback.failed(e); + } + throw e; + } + } finally { + metrics.getRequests().increment(started); + metrics.getTasks().increment(started); + metrics.getActiveConnections().decrementAndGet(); + } + } else { + throw new IllegalStateException("call has been cancelled for request " + request.getURI()); + } + } + + public void cancel() { + cancelled.set(true); + if (callback != null) { + callback.cancelled(); + } + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/InternalHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/InternalHttpClient.java new file mode 100644 index 000000000..c445fe181 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/InternalHttpClient.java @@ -0,0 +1,242 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.Closeable; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.CookieStore; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.Configurable; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.params.ClientPNames; +import ch.boye.httpclientandroidlib.client.params.HttpClientParamConfig; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.params.HttpParamsNames; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Internal class. + * + * @since 4.3 + */ +@ThreadSafe +@SuppressWarnings("deprecation") +class InternalHttpClient extends CloseableHttpClient { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final ClientExecChain execChain; + private final HttpClientConnectionManager connManager; + private final HttpRoutePlanner routePlanner; + private final Lookup cookieSpecRegistry; + private final Lookup authSchemeRegistry; + private final CookieStore cookieStore; + private final CredentialsProvider credentialsProvider; + private final RequestConfig defaultConfig; + private final List closeables; + + public InternalHttpClient( + final ClientExecChain execChain, + final HttpClientConnectionManager connManager, + final HttpRoutePlanner routePlanner, + final Lookup cookieSpecRegistry, + final Lookup authSchemeRegistry, + final CookieStore cookieStore, + final CredentialsProvider credentialsProvider, + final RequestConfig defaultConfig, + final List closeables) { + super(); + Args.notNull(execChain, "HTTP client exec chain"); + Args.notNull(connManager, "HTTP connection manager"); + Args.notNull(routePlanner, "HTTP route planner"); + this.execChain = execChain; + this.connManager = connManager; + this.routePlanner = routePlanner; + this.cookieSpecRegistry = cookieSpecRegistry; + this.authSchemeRegistry = authSchemeRegistry; + this.cookieStore = cookieStore; + this.credentialsProvider = credentialsProvider; + this.defaultConfig = defaultConfig; + this.closeables = closeables; + } + + private HttpRoute determineRoute( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws HttpException { + HttpHost host = target; + if (host == null) { + host = (HttpHost) request.getParams().getParameter(ClientPNames.DEFAULT_HOST); + } + return this.routePlanner.determineRoute(host, request, context); + } + + private void setupContext(final HttpClientContext context) { + if (context.getAttribute(HttpClientContext.TARGET_AUTH_STATE) == null) { + context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, new AuthState()); + } + if (context.getAttribute(HttpClientContext.PROXY_AUTH_STATE) == null) { + context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, new AuthState()); + } + if (context.getAttribute(HttpClientContext.AUTHSCHEME_REGISTRY) == null) { + context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry); + } + if (context.getAttribute(HttpClientContext.COOKIESPEC_REGISTRY) == null) { + context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); + } + if (context.getAttribute(HttpClientContext.COOKIE_STORE) == null) { + context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); + } + if (context.getAttribute(HttpClientContext.CREDS_PROVIDER) == null) { + context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credentialsProvider); + } + if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) { + context.setAttribute(HttpClientContext.REQUEST_CONFIG, this.defaultConfig); + } + } + + @Override + protected CloseableHttpResponse doExecute( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws IOException, ClientProtocolException { + Args.notNull(request, "HTTP request"); + HttpExecutionAware execAware = null; + if (request instanceof HttpExecutionAware) { + execAware = (HttpExecutionAware) request; + } + try { + final HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request); + final HttpClientContext localcontext = HttpClientContext.adapt( + context != null ? context : new BasicHttpContext()); + RequestConfig config = null; + if (request instanceof Configurable) { + config = ((Configurable) request).getConfig(); + } + if (config == null) { + final HttpParams params = request.getParams(); + if (params instanceof HttpParamsNames) { + if (!((HttpParamsNames) params).getNames().isEmpty()) { + config = HttpClientParamConfig.getRequestConfig(params); + } + } else { + config = HttpClientParamConfig.getRequestConfig(params); + } + } + if (config != null) { + localcontext.setRequestConfig(config); + } + setupContext(localcontext); + final HttpRoute route = determineRoute(target, wrapper, localcontext); + return this.execChain.execute(route, wrapper, localcontext, execAware); + } catch (final HttpException httpException) { + throw new ClientProtocolException(httpException); + } + } + + public void close() { + this.connManager.shutdown(); + if (this.closeables != null) { + for (final Closeable closeable: this.closeables) { + try { + closeable.close(); + } catch (final IOException ex) { + this.log.error(ex.getMessage(), ex); + } + } + } + } + + public HttpParams getParams() { + throw new UnsupportedOperationException(); + } + + public ClientConnectionManager getConnectionManager() { + + return new ClientConnectionManager() { + + public void shutdown() { + connManager.shutdown(); + } + + public ClientConnectionRequest requestConnection( + final HttpRoute route, final Object state) { + throw new UnsupportedOperationException(); + } + + public void releaseConnection( + final ManagedClientConnection conn, + final long validDuration, final TimeUnit timeUnit) { + throw new UnsupportedOperationException(); + } + + public SchemeRegistry getSchemeRegistry() { + throw new UnsupportedOperationException(); + } + + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + connManager.closeIdleConnections(idletime, tunit); + } + + public void closeExpiredConnections() { + connManager.closeExpiredConnections(); + } + + }; + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/LaxRedirectStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/LaxRedirectStrategy.java new file mode 100644 index 000000000..47688e642 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/LaxRedirectStrategy.java @@ -0,0 +1,65 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.methods.HttpGet; +import ch.boye.httpclientandroidlib.client.methods.HttpHead; +import ch.boye.httpclientandroidlib.client.methods.HttpPost; + +/** + * Lax {@link ch.boye.httpclientandroidlib.client.RedirectStrategy} implementation + * that automatically redirects all HEAD, GET and POST requests. + * This strategy relaxes restrictions on automatic redirection of + * POST methods imposed by the HTTP specification. + * + * @since 4.2 + */ +@Immutable +public class LaxRedirectStrategy extends DefaultRedirectStrategy { + + /** + * Redirectable methods. + */ + private static final String[] REDIRECT_METHODS = new String[] { + HttpGet.METHOD_NAME, + HttpPost.METHOD_NAME, + HttpHead.METHOD_NAME + }; + + @Override + protected boolean isRedirectable(final String method) { + for (final String m: REDIRECT_METHODS) { + if (m.equalsIgnoreCase(method)) { + return true; + } + } + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/MinimalHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/MinimalHttpClient.java new file mode 100644 index 000000000..8b0c21127 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/MinimalHttpClient.java @@ -0,0 +1,156 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.Configurable; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.execchain.MinimalClientExec; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Internal class. + * + * @since 4.3 + */ +@ThreadSafe +@SuppressWarnings("deprecation") +class MinimalHttpClient extends CloseableHttpClient { + + private final HttpClientConnectionManager connManager; + private final MinimalClientExec requestExecutor; + private final HttpParams params; + + public MinimalHttpClient( + final HttpClientConnectionManager connManager) { + super(); + this.connManager = Args.notNull(connManager, "HTTP connection manager"); + this.requestExecutor = new MinimalClientExec( + new HttpRequestExecutor(), + connManager, + DefaultConnectionReuseStrategy.INSTANCE, + DefaultConnectionKeepAliveStrategy.INSTANCE); + this.params = new BasicHttpParams(); + } + + @Override + protected CloseableHttpResponse doExecute( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws IOException, ClientProtocolException { + Args.notNull(target, "Target host"); + Args.notNull(request, "HTTP request"); + HttpExecutionAware execAware = null; + if (request instanceof HttpExecutionAware) { + execAware = (HttpExecutionAware) request; + } + try { + final HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request); + final HttpClientContext localcontext = HttpClientContext.adapt( + context != null ? context : new BasicHttpContext()); + final HttpRoute route = new HttpRoute(target); + RequestConfig config = null; + if (request instanceof Configurable) { + config = ((Configurable) request).getConfig(); + } + if (config != null) { + localcontext.setRequestConfig(config); + } + return this.requestExecutor.execute(route, wrapper, localcontext, execAware); + } catch (final HttpException httpException) { + throw new ClientProtocolException(httpException); + } + } + + public HttpParams getParams() { + return this.params; + } + + public void close() { + this.connManager.shutdown(); + } + + public ClientConnectionManager getConnectionManager() { + + return new ClientConnectionManager() { + + public void shutdown() { + connManager.shutdown(); + } + + public ClientConnectionRequest requestConnection( + final HttpRoute route, final Object state) { + throw new UnsupportedOperationException(); + } + + public void releaseConnection( + final ManagedClientConnection conn, + final long validDuration, final TimeUnit timeUnit) { + throw new UnsupportedOperationException(); + } + + public SchemeRegistry getSchemeRegistry() { + throw new UnsupportedOperationException(); + } + + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + connManager.closeIdleConnections(idletime, tunit); + } + + public void closeExpiredConnections() { + connManager.closeExpiredConnections(); + } + + }; + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NoopUserTokenHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NoopUserTokenHandler.java new file mode 100644 index 000000000..1e8eb3bde --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NoopUserTokenHandler.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.UserTokenHandler; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Noop implementation of {@link UserTokenHandler} that always returns null. + * + * @since 4.3 + */ +@Immutable +public class NoopUserTokenHandler implements UserTokenHandler { + + public static final NoopUserTokenHandler INSTANCE = new NoopUserTokenHandler(); + + public Object getUserToken(final HttpContext context) { + return null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NullBackoffStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NullBackoffStrategy.java new file mode 100644 index 000000000..8cad78d75 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/NullBackoffStrategy.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy; + +/** + * This is a {@link ConnectionBackoffStrategy} that never backs off, + * for compatibility with existing behavior. + * + * @since 4.2 + */ +public class NullBackoffStrategy implements ConnectionBackoffStrategy { + + public boolean shouldBackoff(final Throwable t) { + return false; + } + + public boolean shouldBackoff(final HttpResponse resp) { + return false; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyAuthenticationStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyAuthenticationStrategy.java new file mode 100644 index 000000000..636143630 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyAuthenticationStrategy.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; + +/** + * Default {@link ch.boye.httpclientandroidlib.client.AuthenticationStrategy} implementation + * for proxy host authentication. + * + * @since 4.2 + */ +@Immutable +public class ProxyAuthenticationStrategy extends AuthenticationStrategyImpl { + + public static final ProxyAuthenticationStrategy INSTANCE = new ProxyAuthenticationStrategy(); + + public ProxyAuthenticationStrategy() { + super(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, AUTH.PROXY_AUTH); + } + + @Override + Collection getPreferredAuthSchemes(final RequestConfig config) { + return config.getProxyPreferredAuthSchemes(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyClient.java new file mode 100644 index 000000000..e1211bc4b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/ProxyClient.java @@ -0,0 +1,254 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.io.IOException; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.client.config.AuthSchemes; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.params.HttpClientParamConfig; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteInfo.LayerType; +import ch.boye.httpclientandroidlib.conn.routing.RouteInfo.TunnelType; +import ch.boye.httpclientandroidlib.entity.BufferedHttpEntity; +import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.auth.BasicSchemeFactory; +import ch.boye.httpclientandroidlib.impl.auth.DigestSchemeFactory; +import ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator; +/* KerberosSchemeFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.impl.auth.NTLMSchemeFactory; +/* SPNegoSchemeFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.impl.conn.ManagedHttpClientConnectionFactory; +import ch.boye.httpclientandroidlib.impl.execchain.TunnelRefusedException; +import ch.boye.httpclientandroidlib.message.BasicHttpRequest; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParamConfig; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.BasicHttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.protocol.HttpCoreContext; +import ch.boye.httpclientandroidlib.protocol.HttpProcessor; +import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor; +import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor; +import ch.boye.httpclientandroidlib.protocol.RequestTargetHost; +import ch.boye.httpclientandroidlib.protocol.RequestUserAgent; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * ProxyClient can be used to establish a tunnel via an HTTP proxy. + */ +@SuppressWarnings("deprecation") +public class ProxyClient { + + private final HttpConnectionFactory connFactory; + private final ConnectionConfig connectionConfig; + private final RequestConfig requestConfig; + private final HttpProcessor httpProcessor; + private final HttpRequestExecutor requestExec; + private final ProxyAuthenticationStrategy proxyAuthStrategy; + private final HttpAuthenticator authenticator; + private final AuthState proxyAuthState; + private final AuthSchemeRegistry authSchemeRegistry; + private final ConnectionReuseStrategy reuseStrategy; + + /** + * @since 4.3 + */ + public ProxyClient( + final HttpConnectionFactory connFactory, + final ConnectionConfig connectionConfig, + final RequestConfig requestConfig) { + super(); + this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; + this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT; + this.requestConfig = requestConfig != null ? requestConfig : RequestConfig.DEFAULT; + this.httpProcessor = new ImmutableHttpProcessor( + new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent()); + this.requestExec = new HttpRequestExecutor(); + this.proxyAuthStrategy = new ProxyAuthenticationStrategy(); + this.authenticator = new HttpAuthenticator(); + this.proxyAuthState = new AuthState(); + this.authSchemeRegistry = new AuthSchemeRegistry(); + this.authSchemeRegistry.register(AuthSchemes.BASIC, new BasicSchemeFactory()); + this.authSchemeRegistry.register(AuthSchemes.DIGEST, new DigestSchemeFactory()); + this.authSchemeRegistry.register(AuthSchemes.NTLM, new NTLMSchemeFactory()); + /* SPNegoSchemeFactory removed by HttpClient for Android script. */ + /* KerberosSchemeFactory removed by HttpClient for Android script. */ + this.reuseStrategy = new DefaultConnectionReuseStrategy(); + } + + /** + * @deprecated (4.3) use {@link ProxyClient#ProxyClient(HttpConnectionFactory, ConnectionConfig, RequestConfig)} + */ + @Deprecated + public ProxyClient(final HttpParams params) { + this(null, + HttpParamConfig.getConnectionConfig(params), + HttpClientParamConfig.getRequestConfig(params)); + } + + /** + * @since 4.3 + */ + public ProxyClient(final RequestConfig requestConfig) { + this(null, null, requestConfig); + } + + public ProxyClient() { + this(null, null, null); + } + + /** + * @deprecated (4.3) do not use. + */ + @Deprecated + public HttpParams getParams() { + return new BasicHttpParams(); + } + + /** + * @deprecated (4.3) do not use. + */ + @Deprecated + public AuthSchemeRegistry getAuthSchemeRegistry() { + return this.authSchemeRegistry; + } + + public Socket tunnel( + final HttpHost proxy, + final HttpHost target, + final Credentials credentials) throws IOException, HttpException { + Args.notNull(proxy, "Proxy host"); + Args.notNull(target, "Target host"); + Args.notNull(credentials, "Credentials"); + HttpHost host = target; + if (host.getPort() <= 0) { + host = new HttpHost(host.getHostName(), 80, host.getSchemeName()); + } + final HttpRoute route = new HttpRoute( + host, + this.requestConfig.getLocalAddress(), + proxy, false, TunnelType.TUNNELLED, LayerType.PLAIN); + + final ManagedHttpClientConnection conn = this.connFactory.create( + route, this.connectionConfig); + final HttpContext context = new BasicHttpContext(); + HttpResponse response; + + final HttpRequest connect = new BasicHttpRequest( + "CONNECT", host.toHostString(), HttpVersion.HTTP_1_1); + + final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(new AuthScope(proxy), credentials); + + // Populate the execution context + context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); + context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); + context.setAttribute(HttpCoreContext.HTTP_REQUEST, connect); + context.setAttribute(HttpClientContext.HTTP_ROUTE, route); + context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, this.proxyAuthState); + context.setAttribute(HttpClientContext.CREDS_PROVIDER, credsProvider); + context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry); + context.setAttribute(HttpClientContext.REQUEST_CONFIG, this.requestConfig); + + this.requestExec.preProcess(connect, this.httpProcessor, context); + + for (;;) { + if (!conn.isOpen()) { + final Socket socket = new Socket(proxy.getHostName(), proxy.getPort()); + conn.bind(socket); + } + + this.authenticator.generateAuthResponse(connect, this.proxyAuthState, context); + + response = this.requestExec.execute(connect, conn, context); + + final int status = response.getStatusLine().getStatusCode(); + if (status < 200) { + throw new HttpException("Unexpected response to CONNECT request: " + + response.getStatusLine()); + } + if (this.authenticator.isAuthenticationRequested(proxy, response, + this.proxyAuthStrategy, this.proxyAuthState, context)) { + if (this.authenticator.handleAuthChallenge(proxy, response, + this.proxyAuthStrategy, this.proxyAuthState, context)) { + // Retry request + if (this.reuseStrategy.keepAlive(response, context)) { + // Consume response content + final HttpEntity entity = response.getEntity(); + EntityUtils.consume(entity); + } else { + conn.close(); + } + // discard previous auth header + connect.removeHeaders(AUTH.PROXY_AUTH_RESP); + } else { + break; + } + } else { + break; + } + } + + final int status = response.getStatusLine().getStatusCode(); + + if (status > 299) { + + // Buffer response content + final HttpEntity entity = response.getEntity(); + if (entity != null) { + response.setEntity(new BufferedHttpEntity(entity)); + } + + conn.close(); + throw new TunnelRefusedException("CONNECT refused by proxy: " + + response.getStatusLine(), response); + } + return conn.getSocket(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RedirectLocations.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RedirectLocations.java new file mode 100644 index 000000000..872dc167f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RedirectLocations.java @@ -0,0 +1,227 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.URI; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * This class represents a collection of {@link java.net.URI}s used + * as redirect locations. + * + * @since 4.0 + */ +@NotThreadSafe // HashSet/ArrayList are not synch. +public class RedirectLocations extends AbstractList { + + private final Set unique; + private final List all; + + public RedirectLocations() { + super(); + this.unique = new HashSet(); + this.all = new ArrayList(); + } + + /** + * Test if the URI is present in the collection. + */ + public boolean contains(final URI uri) { + return this.unique.contains(uri); + } + + /** + * Adds a new URI to the collection. + */ + public void add(final URI uri) { + this.unique.add(uri); + this.all.add(uri); + } + + /** + * Removes a URI from the collection. + */ + public boolean remove(final URI uri) { + final boolean removed = this.unique.remove(uri); + if (removed) { + final Iterator it = this.all.iterator(); + while (it.hasNext()) { + final URI current = it.next(); + if (current.equals(uri)) { + it.remove(); + } + } + } + return removed; + } + + /** + * Returns all redirect {@link URI}s in the order they were added to the collection. + * + * @return list of all URIs + * + * @since 4.1 + */ + public List getAll() { + return new ArrayList(this.all); + } + + /** + * Returns the URI at the specified position in this list. + * + * @param index + * index of the location to return + * @return the URI at the specified position in this list + * @throws IndexOutOfBoundsException + * if the index is out of range ( + * index < 0 || index >= size()) + * @since 4.3 + */ + @Override + public URI get(final int index) { + return this.all.get(index); + } + + /** + * Returns the number of elements in this list. If this list contains more + * than Integer.MAX_VALUE elements, returns + * Integer.MAX_VALUE. + * + * @return the number of elements in this list + * @since 4.3 + */ + @Override + public int size() { + return this.all.size(); + } + + /** + * Replaces the URI at the specified position in this list with the + * specified element (must be a URI). + * + * @param index + * index of the element to replace + * @param element + * URI to be stored at the specified position + * @return the URI previously at the specified position + * @throws UnsupportedOperationException + * if the set operation is not supported by this list + * @throws ClassCastException + * if the element is not a {@link URI} + * @throws NullPointerException + * if the specified element is null and this list does not + * permit null elements + * @throws IndexOutOfBoundsException + * if the index is out of range ( + * index < 0 || index >= size()) + * @since 4.3 + */ + @Override + public Object set(final int index, final Object element) { + final URI removed = this.all.set(index, (URI) element); + this.unique.remove(removed); + this.unique.add((URI) element); + if (this.all.size() != this.unique.size()) { + this.unique.addAll(this.all); + } + return removed; + } + + /** + * Inserts the specified element at the specified position in this list + * (must be a URI). Shifts the URI currently at that position (if any) and + * any subsequent URIs to the right (adds one to their indices). + * + * @param index + * index at which the specified element is to be inserted + * @param element + * URI to be inserted + * @throws UnsupportedOperationException + * if the add operation is not supported by this list + * @throws ClassCastException + * if the element is not a {@link URI} + * @throws NullPointerException + * if the specified element is null and this list does not + * permit null elements + * @throws IndexOutOfBoundsException + * if the index is out of range ( + * index < 0 || index > size()) + * @since 4.3 + */ + @Override + public void add(final int index, final Object element) { + this.all.add(index, (URI) element); + this.unique.add((URI) element); + } + + /** + * Removes the URI at the specified position in this list. Shifts any + * subsequent URIs to the left (subtracts one from their indices). Returns + * the URI that was removed from the list. + * + * @param index + * the index of the URI to be removed + * @return the URI previously at the specified position + * @throws IndexOutOfBoundsException + * if the index is out of range ( + * index < 0 || index >= size()) + * @since 4.3 + */ + @Override + public URI remove(final int index) { + final URI removed = this.all.remove(index); + this.unique.remove(removed); + if (this.all.size() != this.unique.size()) { + this.unique.addAll(this.all); + } + return removed; + } + + /** + * Returns true if this collection contains the specified element. + * More formally, returns true if and only if this collection + * contains at least one element e such that + * (o==null ? e==null : o.equals(e)). + * + * @param o element whose presence in this collection is to be tested + * @return true if this collection contains the specified + * element + */ + @Override + public boolean contains(final Object o) { + return this.unique.contains(o); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RequestWrapper.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RequestWrapper.java new file mode 100644 index 000000000..3577b61a6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RequestWrapper.java @@ -0,0 +1,164 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.URI; +import java.net.URISyntaxException; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.message.AbstractHttpMessage; +import ch.boye.httpclientandroidlib.message.BasicRequestLine; +import ch.boye.httpclientandroidlib.params.HttpProtocolParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A wrapper class for {@link HttpRequest}s that can be used to change + * properties of the current request without modifying the original + * object. + *

    + * This class is also capable of resetting the request headers to + * the state of the original request. + * + * @since 4.0 + * + * @deprecated (4.3) do not use. + */ +@NotThreadSafe +@Deprecated +public class RequestWrapper extends AbstractHttpMessage implements HttpUriRequest { + + private final HttpRequest original; + + private URI uri; + private String method; + private ProtocolVersion version; + private int execCount; + + public RequestWrapper(final HttpRequest request) throws ProtocolException { + super(); + Args.notNull(request, "HTTP request"); + this.original = request; + setParams(request.getParams()); + setHeaders(request.getAllHeaders()); + // Make a copy of the original URI + if (request instanceof HttpUriRequest) { + this.uri = ((HttpUriRequest) request).getURI(); + this.method = ((HttpUriRequest) request).getMethod(); + this.version = null; + } else { + final RequestLine requestLine = request.getRequestLine(); + try { + this.uri = new URI(requestLine.getUri()); + } catch (final URISyntaxException ex) { + throw new ProtocolException("Invalid request URI: " + + requestLine.getUri(), ex); + } + this.method = requestLine.getMethod(); + this.version = request.getProtocolVersion(); + } + this.execCount = 0; + } + + public void resetHeaders() { + // Make a copy of original headers + this.headergroup.clear(); + setHeaders(this.original.getAllHeaders()); + } + + public String getMethod() { + return this.method; + } + + public void setMethod(final String method) { + Args.notNull(method, "Method name"); + this.method = method; + } + + public ProtocolVersion getProtocolVersion() { + if (this.version == null) { + this.version = HttpProtocolParams.getVersion(getParams()); + } + return this.version; + } + + public void setProtocolVersion(final ProtocolVersion version) { + this.version = version; + } + + + public URI getURI() { + return this.uri; + } + + public void setURI(final URI uri) { + this.uri = uri; + } + + public RequestLine getRequestLine() { + final String method = getMethod(); + final ProtocolVersion ver = getProtocolVersion(); + String uritext = null; + if (uri != null) { + uritext = uri.toASCIIString(); + } + if (uritext == null || uritext.length() == 0) { + uritext = "/"; + } + return new BasicRequestLine(method, uritext, ver); + } + + public void abort() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public boolean isAborted() { + return false; + } + + public HttpRequest getOriginal() { + return this.original; + } + + public boolean isRepeatable() { + return true; + } + + public int getExecCount() { + return this.execCount; + } + + public void incrementExecCount() { + this.execCount++; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RoutedRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RoutedRequest.java new file mode 100644 index 000000000..2e5ff76e9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/RoutedRequest.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; + +/** + * A request with the route along which it should be sent. + * + * @since 4.0 + * + * @deprecated (4.3) do not use. + */ +@Deprecated +@NotThreadSafe // RequestWrapper is @NotThreadSafe +public class RoutedRequest { + + protected final RequestWrapper request; // @NotThreadSafe + protected final HttpRoute route; // @Immutable + + /** + * Creates a new routed request. + * + * @param req the request + * @param route the route + */ + public RoutedRequest(final RequestWrapper req, final HttpRoute route) { + super(); + this.request = req; + this.route = route; + } + + public final RequestWrapper getRequest() { + return request; + } + + public final HttpRoute getRoute() { + return route; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/StandardHttpRequestRetryHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/StandardHttpRequestRetryHandler.java new file mode 100644 index 000000000..ec0ce7e00 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/StandardHttpRequestRetryHandler.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * {@link ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler} which assumes + * that all requested HTTP methods which should be idempotent according + * to RFC-2616 are in fact idempotent and can be retried. + *

    + * According to RFC-2616 section 9.1.2 the idempotent HTTP methods are: + * GET, HEAD, PUT, DELETE, OPTIONS, and TRACE + * + * @since 4.2 + */ +@Immutable +public class StandardHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler { + + private final Map idempotentMethods; + + /** + * Default constructor + */ + public StandardHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) { + super(retryCount, requestSentRetryEnabled); + this.idempotentMethods = new ConcurrentHashMap(); + this.idempotentMethods.put("GET", Boolean.TRUE); + this.idempotentMethods.put("HEAD", Boolean.TRUE); + this.idempotentMethods.put("PUT", Boolean.TRUE); + this.idempotentMethods.put("DELETE", Boolean.TRUE); + this.idempotentMethods.put("OPTIONS", Boolean.TRUE); + this.idempotentMethods.put("TRACE", Boolean.TRUE); + } + + /** + * Default constructor + */ + public StandardHttpRequestRetryHandler() { + this(3, false); + } + + @Override + protected boolean handleAsIdempotent(final HttpRequest request) { + final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH); + final Boolean b = this.idempotentMethods.get(method); + return b != null && b.booleanValue(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemClock.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemClock.java new file mode 100644 index 000000000..b037af841 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemClock.java @@ -0,0 +1,40 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +/** + * The actual system clock. + * + * @since 4.2 + */ +class SystemClock implements Clock { + + public long getCurrentTime() { + return System.currentTimeMillis(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultCredentialsProvider.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultCredentialsProvider.java new file mode 100644 index 000000000..1327b71dc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultCredentialsProvider.java @@ -0,0 +1,145 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.Credentials; +import ch.boye.httpclientandroidlib.auth.NTCredentials; +import ch.boye.httpclientandroidlib.auth.UsernamePasswordCredentials; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.config.AuthSchemes; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Implementation of {@link CredentialsProvider} backed by standard + * JRE {@link Authenticator}. + * + * @since 4.3 + */ +@ThreadSafe +public class SystemDefaultCredentialsProvider implements CredentialsProvider { + + private static final Map SCHEME_MAP; + + static { + SCHEME_MAP = new ConcurrentHashMap(); + SCHEME_MAP.put(AuthSchemes.BASIC.toUpperCase(Locale.ENGLISH), "Basic"); + SCHEME_MAP.put(AuthSchemes.DIGEST.toUpperCase(Locale.ENGLISH), "Digest"); + SCHEME_MAP.put(AuthSchemes.NTLM.toUpperCase(Locale.ENGLISH), "NTLM"); + SCHEME_MAP.put(AuthSchemes.SPNEGO.toUpperCase(Locale.ENGLISH), "SPNEGO"); + SCHEME_MAP.put(AuthSchemes.KERBEROS.toUpperCase(Locale.ENGLISH), "Kerberos"); + } + + private static String translateScheme(final String key) { + if (key == null) { + return null; + } + final String s = SCHEME_MAP.get(key); + return s != null ? s : key; + } + + private final BasicCredentialsProvider internal; + + /** + * Default constructor. + */ + public SystemDefaultCredentialsProvider() { + super(); + this.internal = new BasicCredentialsProvider(); + } + + public void setCredentials(final AuthScope authscope, final Credentials credentials) { + internal.setCredentials(authscope, credentials); + } + + private static PasswordAuthentication getSystemCreds( + final AuthScope authscope, + final Authenticator.RequestorType requestorType) { + final String hostname = authscope.getHost(); + final int port = authscope.getPort(); + final String protocol = port == 443 ? "https" : "http"; + return Authenticator.requestPasswordAuthentication( + hostname, + null, + port, + protocol, + null, + translateScheme(authscope.getScheme()), + null, + requestorType); + } + + public Credentials getCredentials(final AuthScope authscope) { + Args.notNull(authscope, "Auth scope"); + final Credentials localcreds = internal.getCredentials(authscope); + if (localcreds != null) { + return localcreds; + } + if (authscope.getHost() != null) { + PasswordAuthentication systemcreds = getSystemCreds( + authscope, Authenticator.RequestorType.SERVER); + if (systemcreds == null) { + systemcreds = getSystemCreds( + authscope, Authenticator.RequestorType.PROXY); + } + if (systemcreds != null) { + final String domain = System.getProperty("http.auth.ntlm.domain"); + if (domain != null) { + return new NTCredentials( + systemcreds.getUserName(), + new String(systemcreds.getPassword()), + null, domain); + } else { + if (AuthSchemes.NTLM.equalsIgnoreCase(authscope.getScheme())) { + // Domian may be specified in a fully qualified user name + return new NTCredentials( + systemcreds.getUserName(), + new String(systemcreds.getPassword()), + null, null); + } else { + return new UsernamePasswordCredentials( + systemcreds.getUserName(), + new String(systemcreds.getPassword())); + } + } + } + } + return null; + } + + public void clear() { + internal.clear(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultHttpClient.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultHttpClient.java new file mode 100644 index 000000000..0806537ed --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/SystemDefaultHttpClient.java @@ -0,0 +1,149 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.net.ProxySelector; + +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.NoConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.conn.PoolingClientConnectionManager; +import ch.boye.httpclientandroidlib.impl.conn.ProxySelectorRoutePlanner; +import ch.boye.httpclientandroidlib.impl.conn.SchemeRegistryFactory; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * An extension of {@link DefaultHttpClient} pre-configured using system properties. + *

    + * The following system properties are taken into account by this class: + *

      + *
    • ssl.TrustManagerFactory.algorithm
    • + *
    • javax.net.ssl.trustStoreType
    • + *
    • javax.net.ssl.trustStore
    • + *
    • javax.net.ssl.trustStoreProvider
    • + *
    • javax.net.ssl.trustStorePassword
    • + *
    • java.home
    • + *
    • ssl.KeyManagerFactory.algorithm
    • + *
    • javax.net.ssl.keyStoreType
    • + *
    • javax.net.ssl.keyStore
    • + *
    • javax.net.ssl.keyStoreProvider
    • + *
    • javax.net.ssl.keyStorePassword
    • + *
    • http.proxyHost
    • + *
    • http.proxyPort
    • + *
    • http.nonProxyHosts
    • + *
    • http.keepAlive
    • + *
    • http.maxConnections
    • + *
    + *

    + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#PROTOCOL_VERSION}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USE_EXPECT_CONTINUE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USER_AGENT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#STALE_CONNECTION_CHECK}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY}
    • + *
    • {@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#DATE_PATTERNS}
    • + *
    • {@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#SINGLE_COOKIE_HEADER}
    • + *
    • {@link ch.boye.httpclientandroidlib.auth.params.AuthPNames#CREDENTIAL_CHARSET}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#COOKIE_POLICY}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_AUTHENTICATION}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#MAX_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#ALLOW_CIRCULAR_REDIRECTS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#VIRTUAL_HOST}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HOST}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HEADERS}
    • + *
    • {@link ch.boye.httpclientandroidlib.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}
    • + *
    + *

    + * + * @since 4.2 + * + * @deprecated (4.3) use {@link HttpClientBuilder} + */ +@ThreadSafe +@Deprecated +public class SystemDefaultHttpClient extends DefaultHttpClient { + + public SystemDefaultHttpClient(final HttpParams params) { + super(null, params); + } + + public SystemDefaultHttpClient() { + super(null, null); + } + + @Override + protected ClientConnectionManager createClientConnectionManager() { + final PoolingClientConnectionManager connmgr = new PoolingClientConnectionManager( + SchemeRegistryFactory.createSystemDefault()); + String s = System.getProperty("http.keepAlive", "true"); + if ("true".equalsIgnoreCase(s)) { + s = System.getProperty("http.maxConnections", "5"); + final int max = Integer.parseInt(s); + connmgr.setDefaultMaxPerRoute(max); + connmgr.setMaxTotal(2 * max); + } + return connmgr; + } + + @Override + protected HttpRoutePlanner createHttpRoutePlanner() { + return new ProxySelectorRoutePlanner(getConnectionManager().getSchemeRegistry(), + ProxySelector.getDefault()); + } + + @Override + protected ConnectionReuseStrategy createConnectionReuseStrategy() { + final String s = System.getProperty("http.keepAlive", "true"); + if ("true".equalsIgnoreCase(s)) { + return new DefaultConnectionReuseStrategy(); + } else { + return new NoConnectionReuseStrategy(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TargetAuthenticationStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TargetAuthenticationStrategy.java new file mode 100644 index 000000000..486167dc1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TargetAuthenticationStrategy.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; + +/** + * Default {@link ch.boye.httpclientandroidlib.client.AuthenticationStrategy} implementation + * for proxy host authentication. + * + * @since 4.2 + */ +@Immutable +public class TargetAuthenticationStrategy extends AuthenticationStrategyImpl { + + public static final TargetAuthenticationStrategy INSTANCE = new TargetAuthenticationStrategy(); + + public TargetAuthenticationStrategy() { + super(HttpStatus.SC_UNAUTHORIZED, AUTH.WWW_AUTH); + } + + @Override + Collection getPreferredAuthSchemes(final RequestConfig config) { + return config.getTargetPreferredAuthSchemes(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TunnelRefusedException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TunnelRefusedException.java new file mode 100644 index 000000000..97edac1f0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/TunnelRefusedException.java @@ -0,0 +1,58 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that the tunnel request was rejected by the proxy host. + * + * @since 4.0 + * + * @deprecated (4.3) reserved for internal use. + */ +@Deprecated +@Immutable +public class TunnelRefusedException extends HttpException { + + private static final long serialVersionUID = -8646722842745617323L; + + private final HttpResponse response; + + public TunnelRefusedException(final String message, final HttpResponse response) { + super(message); + this.response = response; + } + + public HttpResponse getResponse() { + return this.response; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidationRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidationRequest.java new file mode 100644 index 000000000..60ad6ca1f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidationRequest.java @@ -0,0 +1,178 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; + +/** + * Class used to represent an asynchronous revalidation event, such as with + * "stale-while-revalidate" + */ +class AsynchronousValidationRequest implements Runnable { + private final AsynchronousValidator parent; + private final CachingExec cachingExec; + private final HttpRoute route; + private final HttpRequestWrapper request; + private final HttpClientContext context; + private final HttpExecutionAware execAware; + private final HttpCacheEntry cacheEntry; + private final String identifier; + private final int consecutiveFailedAttempts; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** + * Used internally by {@link AsynchronousValidator} to schedule a + * revalidation. + * @param request + * @param context + * @param cacheEntry + * @param identifier + * @param consecutiveFailedAttempts + */ + AsynchronousValidationRequest( + final AsynchronousValidator parent, + final CachingExec cachingExec, + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final HttpCacheEntry cacheEntry, + final String identifier, + final int consecutiveFailedAttempts) { + this.parent = parent; + this.cachingExec = cachingExec; + this.route = route; + this.request = request; + this.context = context; + this.execAware = execAware; + this.cacheEntry = cacheEntry; + this.identifier = identifier; + this.consecutiveFailedAttempts = consecutiveFailedAttempts; + } + + public void run() { + try { + if (revalidateCacheEntry()) { + parent.jobSuccessful(identifier); + } else { + parent.jobFailed(identifier); + } + } finally { + parent.markComplete(identifier); + } + } + + /** + * Revalidate the cache entry and return if the operation was successful. + * Success means a connection to the server was established and replay did + * not indicate a server error. + * @return true if the cache entry was successfully validated; + * otherwise false + */ + protected boolean revalidateCacheEntry() { + try { + final CloseableHttpResponse httpResponse = cachingExec.revalidateCacheEntry(route, request, context, execAware, cacheEntry); + try { + final int statusCode = httpResponse.getStatusLine().getStatusCode(); + return isNotServerError(statusCode) && isNotStale(httpResponse); + } finally { + httpResponse.close(); + } + } catch (final IOException ioe) { + log.debug("Asynchronous revalidation failed due to I/O error", ioe); + return false; + } catch (final HttpException pe) { + log.error("HTTP protocol exception during asynchronous revalidation", pe); + return false; + } catch (final RuntimeException re) { + log.error("RuntimeException thrown during asynchronous revalidation: " + re); + return false; + } + } + + /** + * Return whether the status code indicates a server error or not. + * @param statusCode the status code to be checked + * @return if the status code indicates a server error or not + */ + private boolean isNotServerError(final int statusCode) { + return statusCode < 500; + } + + /** + * Try to detect if the returned response is generated from a stale cache entry. + * @param httpResponse the response to be checked + * @return whether the response is stale or not + */ + private boolean isNotStale(final HttpResponse httpResponse) { + final Header[] warnings = httpResponse.getHeaders(HeaderConstants.WARNING); + if (warnings != null) + { + for (final Header warning : warnings) + { + /** + * warn-codes + * 110 = Response is stale + * 111 = Revalidation failed + */ + final String warningValue = warning.getValue(); + if (warningValue.startsWith("110") || warningValue.startsWith("111")) + { + return false; + } + } + } + return true; + } + + String getIdentifier() { + return identifier; + } + + /** + * The number of consecutively failed revalidation attempts. + * @return the number of consecutively failed revalidation attempts. + */ + public int getConsecutiveFailedAttempts() { + return consecutiveFailedAttempts; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidator.java new file mode 100644 index 000000000..05765a236 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidator.java @@ -0,0 +1,150 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; + +/** + * Class used for asynchronous revalidations to be used when the "stale- + * while-revalidate" directive is present + */ +class AsynchronousValidator implements Closeable { + private final SchedulingStrategy schedulingStrategy; + private final Set queued; + private final CacheKeyGenerator cacheKeyGenerator; + private final FailureCache failureCache; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** + * Create AsynchronousValidator which will make revalidation requests + * using an {@link ImmediateSchedulingStrategy}. Its thread + * pool will be configured according to the given {@link CacheConfig}. + * @param config specifies thread pool settings. See + * {@link CacheConfig#getAsynchronousWorkersMax()}, + * {@link CacheConfig#getAsynchronousWorkersCore()}, + * {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()}, + * and {@link CacheConfig#getRevalidationQueueSize()}. + */ + public AsynchronousValidator(final CacheConfig config) { + this(new ImmediateSchedulingStrategy(config)); + } + + /** + * Create AsynchronousValidator which will make revalidation requests + * using the supplied {@link SchedulingStrategy}. Closing the validator + * will also close the given schedulingStrategy. + * @param schedulingStrategy used to maintain a pool of worker threads and + * schedules when requests are executed + */ + AsynchronousValidator(final SchedulingStrategy schedulingStrategy) { + this.schedulingStrategy = schedulingStrategy; + this.queued = new HashSet(); + this.cacheKeyGenerator = new CacheKeyGenerator(); + this.failureCache = new DefaultFailureCache(); + } + + public void close() throws IOException { + schedulingStrategy.close(); + } + + /** + * Schedules an asynchronous revalidation + */ + public synchronized void revalidateCacheEntry( + final CachingExec cachingExec, + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final HttpCacheEntry entry) { + // getVariantURI will fall back on getURI if no variants exist + final String uri = cacheKeyGenerator.getVariantURI(context.getTargetHost(), request, entry); + + if (!queued.contains(uri)) { + final int consecutiveFailedAttempts = failureCache.getErrorCount(uri); + final AsynchronousValidationRequest revalidationRequest = + new AsynchronousValidationRequest( + this, cachingExec, route, request, context, execAware, entry, uri, consecutiveFailedAttempts); + + try { + schedulingStrategy.schedule(revalidationRequest); + queued.add(uri); + } catch (final RejectedExecutionException ree) { + log.debug("Revalidation for [" + uri + "] not scheduled: " + ree); + } + } + } + + /** + * Removes an identifier from the internal list of revalidation jobs in + * progress. This is meant to be called by + * {@link AsynchronousValidationRequest#run()} once the revalidation is + * complete, using the identifier passed in during constructions. + * @param identifier + */ + synchronized void markComplete(final String identifier) { + queued.remove(identifier); + } + + /** + * The revalidation job was successful thus the number of consecutive + * failed attempts will be reset to zero. Should be called by + * {@link AsynchronousValidationRequest#run()}. + * @param identifier the revalidation job's unique identifier + */ + void jobSuccessful(final String identifier) { + failureCache.resetErrorCount(identifier); + } + + /** + * The revalidation job did fail and thus the number of consecutive failed + * attempts will be increased. Should be called by + * {@link AsynchronousValidationRequest#run()}. + * @param identifier the revalidation job's unique identifier + */ + void jobFailed(final String identifier) { + failureCache.increaseErrorCount(identifier); + } + + Set getScheduledIdentifiers() { + return Collections.unmodifiableSet(queued); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCache.java new file mode 100644 index 000000000..4347e173d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCache.java @@ -0,0 +1,376 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheInvalidator; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateCallback; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateException; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.client.cache.ResourceFactory; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.entity.ByteArrayEntity; +import ch.boye.httpclientandroidlib.message.BasicHttpResponse; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +class BasicHttpCache implements HttpCache { + private static final Set safeRequestMethods = new HashSet( + Arrays.asList(HeaderConstants.HEAD_METHOD, + HeaderConstants.GET_METHOD, HeaderConstants.OPTIONS_METHOD, + HeaderConstants.TRACE_METHOD)); + + private final CacheKeyGenerator uriExtractor; + private final ResourceFactory resourceFactory; + private final long maxObjectSizeBytes; + private final CacheEntryUpdater cacheEntryUpdater; + private final CachedHttpResponseGenerator responseGenerator; + private final HttpCacheInvalidator cacheInvalidator; + private final HttpCacheStorage storage; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public BasicHttpCache( + final ResourceFactory resourceFactory, + final HttpCacheStorage storage, + final CacheConfig config, + final CacheKeyGenerator uriExtractor, + final HttpCacheInvalidator cacheInvalidator) { + this.resourceFactory = resourceFactory; + this.uriExtractor = uriExtractor; + this.cacheEntryUpdater = new CacheEntryUpdater(resourceFactory); + this.maxObjectSizeBytes = config.getMaxObjectSize(); + this.responseGenerator = new CachedHttpResponseGenerator(); + this.storage = storage; + this.cacheInvalidator = cacheInvalidator; + } + + public BasicHttpCache( + final ResourceFactory resourceFactory, + final HttpCacheStorage storage, + final CacheConfig config, + final CacheKeyGenerator uriExtractor) { + this( resourceFactory, storage, config, uriExtractor, + new CacheInvalidator(uriExtractor, storage)); + } + + public BasicHttpCache( + final ResourceFactory resourceFactory, + final HttpCacheStorage storage, + final CacheConfig config) { + this( resourceFactory, storage, config, new CacheKeyGenerator()); + } + + public BasicHttpCache(final CacheConfig config) { + this(new HeapResourceFactory(), new BasicHttpCacheStorage(config), config); + } + + public BasicHttpCache() { + this(CacheConfig.DEFAULT); + } + + public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) + throws IOException { + if (!safeRequestMethods.contains(request.getRequestLine().getMethod())) { + final String uri = uriExtractor.getURI(host, request); + storage.removeEntry(uri); + } + } + + public void flushInvalidatedCacheEntriesFor(final HttpHost host, final HttpRequest request, final HttpResponse response) { + if (!safeRequestMethods.contains(request.getRequestLine().getMethod())) { + cacheInvalidator.flushInvalidatedCacheEntries(host, request, response); + } + } + + void storeInCache( + final HttpHost target, final HttpRequest request, final HttpCacheEntry entry) throws IOException { + if (entry.hasVariants()) { + storeVariantEntry(target, request, entry); + } else { + storeNonVariantEntry(target, request, entry); + } + } + + void storeNonVariantEntry( + final HttpHost target, final HttpRequest req, final HttpCacheEntry entry) throws IOException { + final String uri = uriExtractor.getURI(target, req); + storage.putEntry(uri, entry); + } + + void storeVariantEntry( + final HttpHost target, + final HttpRequest req, + final HttpCacheEntry entry) throws IOException { + final String parentURI = uriExtractor.getURI(target, req); + final String variantURI = uriExtractor.getVariantURI(target, req, entry); + storage.putEntry(variantURI, entry); + + final HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() { + + public HttpCacheEntry update(final HttpCacheEntry existing) throws IOException { + return doGetUpdatedParentEntry( + req.getRequestLine().getUri(), existing, entry, + uriExtractor.getVariantKey(req, entry), + variantURI); + } + + }; + + try { + storage.updateEntry(parentURI, callback); + } catch (final HttpCacheUpdateException e) { + log.warn("Could not update key [" + parentURI + "]", e); + } + } + + public void reuseVariantEntryFor(final HttpHost target, final HttpRequest req, + final Variant variant) throws IOException { + final String parentCacheKey = uriExtractor.getURI(target, req); + final HttpCacheEntry entry = variant.getEntry(); + final String variantKey = uriExtractor.getVariantKey(req, entry); + final String variantCacheKey = variant.getCacheKey(); + + final HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() { + public HttpCacheEntry update(final HttpCacheEntry existing) + throws IOException { + return doGetUpdatedParentEntry(req.getRequestLine().getUri(), + existing, entry, variantKey, variantCacheKey); + } + }; + + try { + storage.updateEntry(parentCacheKey, callback); + } catch (final HttpCacheUpdateException e) { + log.warn("Could not update key [" + parentCacheKey + "]", e); + } + } + + boolean isIncompleteResponse(final HttpResponse resp, final Resource resource) { + final int status = resp.getStatusLine().getStatusCode(); + if (status != HttpStatus.SC_OK + && status != HttpStatus.SC_PARTIAL_CONTENT) { + return false; + } + final Header hdr = resp.getFirstHeader(HTTP.CONTENT_LEN); + if (hdr == null) { + return false; + } + final int contentLength; + try { + contentLength = Integer.parseInt(hdr.getValue()); + } catch (final NumberFormatException nfe) { + return false; + } + return (resource.length() < contentLength); + } + + CloseableHttpResponse generateIncompleteResponseError( + final HttpResponse response, final Resource resource) { + final int contentLength = Integer.parseInt(response.getFirstHeader(HTTP.CONTENT_LEN).getValue()); + final HttpResponse error = + new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_GATEWAY, "Bad Gateway"); + error.setHeader("Content-Type","text/plain;charset=UTF-8"); + final String msg = String.format("Received incomplete response " + + "with Content-Length %d but actual body length %d", + contentLength, resource.length()); + final byte[] msgBytes = msg.getBytes(); + error.setHeader("Content-Length", Integer.toString(msgBytes.length)); + error.setEntity(new ByteArrayEntity(msgBytes)); + return Proxies.enhanceResponse(error); + } + + HttpCacheEntry doGetUpdatedParentEntry( + final String requestId, + final HttpCacheEntry existing, + final HttpCacheEntry entry, + final String variantKey, + final String variantCacheKey) throws IOException { + HttpCacheEntry src = existing; + if (src == null) { + src = entry; + } + + Resource resource = null; + if (src.getResource() != null) { + resource = resourceFactory.copy(requestId, src.getResource()); + } + final Map variantMap = new HashMap(src.getVariantMap()); + variantMap.put(variantKey, variantCacheKey); + return new HttpCacheEntry( + src.getRequestDate(), + src.getResponseDate(), + src.getStatusLine(), + src.getAllHeaders(), + resource, + variantMap); + } + + public HttpCacheEntry updateCacheEntry(final HttpHost target, final HttpRequest request, + final HttpCacheEntry stale, final HttpResponse originResponse, + final Date requestSent, final Date responseReceived) throws IOException { + final HttpCacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry( + request.getRequestLine().getUri(), + stale, + requestSent, + responseReceived, + originResponse); + storeInCache(target, request, updatedEntry); + return updatedEntry; + } + + public HttpCacheEntry updateVariantCacheEntry(final HttpHost target, final HttpRequest request, + final HttpCacheEntry stale, final HttpResponse originResponse, + final Date requestSent, final Date responseReceived, final String cacheKey) throws IOException { + final HttpCacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry( + request.getRequestLine().getUri(), + stale, + requestSent, + responseReceived, + originResponse); + storage.putEntry(cacheKey, updatedEntry); + return updatedEntry; + } + + public HttpResponse cacheAndReturnResponse(final HttpHost host, final HttpRequest request, + final HttpResponse originResponse, final Date requestSent, final Date responseReceived) + throws IOException { + return cacheAndReturnResponse(host, request, + Proxies.enhanceResponse(originResponse), requestSent, + responseReceived); + } + + public CloseableHttpResponse cacheAndReturnResponse( + final HttpHost host, + final HttpRequest request, + final CloseableHttpResponse originResponse, + final Date requestSent, + final Date responseReceived) throws IOException { + + boolean closeOriginResponse = true; + final SizeLimitedResponseReader responseReader = getResponseReader(request, originResponse); + try { + responseReader.readResponse(); + + if (responseReader.isLimitReached()) { + closeOriginResponse = false; + return responseReader.getReconstructedResponse(); + } + + final Resource resource = responseReader.getResource(); + if (isIncompleteResponse(originResponse, resource)) { + return generateIncompleteResponseError(originResponse, resource); + } + + final HttpCacheEntry entry = new HttpCacheEntry( + requestSent, + responseReceived, + originResponse.getStatusLine(), + originResponse.getAllHeaders(), + resource); + storeInCache(host, request, entry); + return responseGenerator.generateResponse(entry); + } finally { + if (closeOriginResponse) { + originResponse.close(); + } + } + } + + SizeLimitedResponseReader getResponseReader(final HttpRequest request, + final CloseableHttpResponse backEndResponse) { + return new SizeLimitedResponseReader( + resourceFactory, maxObjectSizeBytes, request, backEndResponse); + } + + public HttpCacheEntry getCacheEntry(final HttpHost host, final HttpRequest request) throws IOException { + final HttpCacheEntry root = storage.getEntry(uriExtractor.getURI(host, request)); + if (root == null) { + return null; + } + if (!root.hasVariants()) { + return root; + } + final String variantCacheKey = root.getVariantMap().get(uriExtractor.getVariantKey(request, root)); + if (variantCacheKey == null) { + return null; + } + return storage.getEntry(variantCacheKey); + } + + public void flushInvalidatedCacheEntriesFor(final HttpHost host, + final HttpRequest request) throws IOException { + cacheInvalidator.flushInvalidatedCacheEntries(host, request); + } + + public Map getVariantCacheEntriesWithEtags(final HttpHost host, final HttpRequest request) + throws IOException { + final Map variants = new HashMap(); + final HttpCacheEntry root = storage.getEntry(uriExtractor.getURI(host, request)); + if (root == null || !root.hasVariants()) { + return variants; + } + for(final Map.Entry variant : root.getVariantMap().entrySet()) { + final String variantKey = variant.getKey(); + final String variantCacheKey = variant.getValue(); + addVariantWithEtag(variantKey, variantCacheKey, variants); + } + return variants; + } + + private void addVariantWithEtag(final String variantKey, + final String variantCacheKey, final Map variants) + throws IOException { + final HttpCacheEntry entry = storage.getEntry(variantCacheKey); + if (entry == null) { + return; + } + final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); + if (etagHeader == null) { + return; + } + variants.put(etagHeader.getValue(), new Variant(variantKey, variantCacheKey, entry)); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCacheStorage.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCacheStorage.java new file mode 100644 index 000000000..225730858 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicHttpCacheStorage.java @@ -0,0 +1,96 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateCallback; + +/** + * Basic {@link HttpCacheStorage} implementation backed by an instance of + * {@link java.util.LinkedHashMap}. In other words, cache entries and + * the cached response bodies are held in-memory. This cache does NOT + * deallocate resources associated with the cache entries; it is intended + * for use with {@link HeapResource} and similar. This is the default cache + * storage backend used by {@link CachingHttpClients}. + * + * @since 4.1 + */ +@ThreadSafe +public class BasicHttpCacheStorage implements HttpCacheStorage { + + private final CacheMap entries; + + public BasicHttpCacheStorage(final CacheConfig config) { + super(); + this.entries = new CacheMap(config.getMaxCacheEntries()); + } + + /** + * Places a HttpCacheEntry in the cache + * + * @param url + * Url to use as the cache key + * @param entry + * HttpCacheEntry to place in the cache + */ + public synchronized void putEntry(final String url, final HttpCacheEntry entry) throws IOException { + entries.put(url, entry); + } + + /** + * Gets an entry from the cache, if it exists + * + * @param url + * Url that is the cache key + * @return HttpCacheEntry if one exists, or null for cache miss + */ + public synchronized HttpCacheEntry getEntry(final String url) throws IOException { + return entries.get(url); + } + + /** + * Removes a HttpCacheEntry from the cache + * + * @param url + * Url that is the cache key + */ + public synchronized void removeEntry(final String url) throws IOException { + entries.remove(url); + } + + public synchronized void updateEntry( + final String url, + final HttpCacheUpdateCallback callback) throws IOException { + final HttpCacheEntry existingEntry = entries.get(url); + entries.put(url, callback.update(existingEntry)); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicIdGenerator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicIdGenerator.java new file mode 100644 index 000000000..defdc3999 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/BasicIdGenerator.java @@ -0,0 +1,86 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Formatter; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +/** + * Should produce reasonably unique tokens. + */ +@ThreadSafe +class BasicIdGenerator { + + private final String hostname; + private final SecureRandom rnd; + + @GuardedBy("this") + private long count; + + public BasicIdGenerator() { + super(); + String hostname; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (final UnknownHostException ex) { + hostname = "localhost"; + } + this.hostname = hostname; + try { + this.rnd = SecureRandom.getInstance("SHA1PRNG"); + } catch (final NoSuchAlgorithmException ex) { + throw new Error(ex); + } + this.rnd.setSeed(System.currentTimeMillis()); + } + + public synchronized void generate(final StringBuilder buffer) { + this.count++; + final int rndnum = this.rnd.nextInt(); + buffer.append(System.currentTimeMillis()); + buffer.append('.'); + final Formatter formatter = new Formatter(buffer, Locale.US); + formatter.format("%1$016x-%2$08x", this.count, rndnum); + formatter.close(); + buffer.append('.'); + buffer.append(this.hostname); + } + + public String generate() { + final StringBuilder buffer = new StringBuilder(); + generate(buffer); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheConfig.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheConfig.java new file mode 100644 index 000000000..964ea2719 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheConfig.java @@ -0,0 +1,764 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + *

    Java Beans-style configuration for a {@link CachingHttpClient}. Any class + * in the caching module that has configuration options should take a + * {@link CacheConfig} argument in one of its constructors. A + * {@code CacheConfig} instance has sane and conservative defaults, so the + * easiest way to specify options is to get an instance and then set just + * the options you want to modify from their defaults.

    + * + *

    N.B. This class is only for caching-specific configuration; to + * configure the behavior of the rest of the client, configure the + * {@link ch.boye.httpclientandroidlib.client.HttpClient} used as the "backend" + * for the {@code CachingHttpClient}.

    + * + *

    Cache configuration can be grouped into the following categories:

    + * + *

    Cache size. If the backend storage supports these limits, you + * can specify the {@link CacheConfig#getMaxCacheEntries maximum number of + * cache entries} as well as the {@link CacheConfig#getMaxObjectSizeBytes + * maximum cacheable response body size}.

    + * + *

    Public/private caching. By default, the caching module considers + * itself to be a shared (public) cache, and will not, for example, cache + * responses to requests with {@code Authorization} headers or responses + * marked with {@code Cache-Control: private}. If, however, the cache + * is only going to be used by one logical "user" (behaving similarly to a + * browser cache), then you will want to {@link + * CacheConfig#setSharedCache(boolean) turn off the shared cache setting}.

    + * + *

    303 caching. RFC2616 explicitly disallows caching 303 responses; + * however, the HTTPbis working group says they can be cached + * if explicitly indicated in the response headers and permitted by the request method. + * (They also indicate that disallowing 303 caching is actually an unintended + * spec error in RFC2616). + * This behavior is off by default, to err on the side of a conservative + * adherence to the existing standard, but you may want to + * {@link Builder#setAllow303Caching(boolean) enable it}. + * + *

    Weak ETags on PUT/DELETE If-Match requests. RFC2616 explicitly + * prohibits the use of weak validators in non-GET requests, however, the + * HTTPbis working group says while the limitation for weak validators on ranged + * requests makes sense, weak ETag validation is useful on full non-GET + * requests; e.g., PUT with If-Match. This behavior is off by default, to err on + * the side of a conservative adherence to the existing standard, but you may + * want to {@link Builder#setWeakETagOnPutDeleteAllowed(boolean) enable it}. + * + *

    Heuristic caching. Per RFC2616, a cache may cache certain cache + * entries even if no explicit cache control headers are set by the origin. + * This behavior is off by default, but you may want to turn this on if you + * are working with an origin that doesn't set proper headers but where you + * still want to cache the responses. You will want to {@link + * CacheConfig#setHeuristicCachingEnabled(boolean) enable heuristic caching}, + * then specify either a {@link CacheConfig#getHeuristicDefaultLifetime() + * default freshness lifetime} and/or a {@link + * CacheConfig#setHeuristicCoefficient(float) fraction of the time since + * the resource was last modified}. See Sections + * + * 13.2.2 and + * 13.2.4 of the HTTP/1.1 RFC for more details on heuristic caching.

    + * + *

    Background validation. The cache module supports the + * {@code stale-while-revalidate} directive of + * RFC5861, which allows + * certain cache entry revalidations to happen in the background. You may + * want to tweak the settings for the {@link + * CacheConfig#getAsynchronousWorkersCore() minimum} and {@link + * CacheConfig#getAsynchronousWorkersMax() maximum} number of background + * worker threads, as well as the {@link + * CacheConfig#getAsynchronousWorkerIdleLifetimeSecs() maximum time they + * can be idle before being reclaimed}. You can also control the {@link + * CacheConfig#getRevalidationQueueSize() size of the queue} used for + * revalidations when there aren't enough workers to keep up with demand. + */ +public class CacheConfig implements Cloneable { + + /** Default setting for the maximum object size that will be + * cached, in bytes. + */ + public final static int DEFAULT_MAX_OBJECT_SIZE_BYTES = 8192; + + /** Default setting for the maximum number of cache entries + * that will be retained. + */ + public final static int DEFAULT_MAX_CACHE_ENTRIES = 1000; + + /** Default setting for the number of retries on a failed + * cache update + */ + public final static int DEFAULT_MAX_UPDATE_RETRIES = 1; + + /** Default setting for 303 caching + */ + public final static boolean DEFAULT_303_CACHING_ENABLED = false; + + /** Default setting to allow weak tags on PUT/DELETE methods + */ + public final static boolean DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED = false; + + /** Default setting for heuristic caching + */ + public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false; + + /** Default coefficient used to heuristically determine freshness + * lifetime from the Last-Modified time of a cache entry. + */ + public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f; + + /** Default lifetime in seconds to be assumed when we cannot calculate + * freshness heuristically. + */ + public final static long DEFAULT_HEURISTIC_LIFETIME = 0; + + /** Default number of worker threads to allow for background revalidations + * resulting from the stale-while-revalidate directive. + */ + public static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1; + + /** Default minimum number of worker threads to allow for background + * revalidations resulting from the stale-while-revalidate directive. + */ + public static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1; + + /** Default maximum idle lifetime for a background revalidation thread + * before it gets reclaimed. + */ + public static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60; + + /** Default maximum queue length for background revalidation requests. + */ + public static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100; + + public static final CacheConfig DEFAULT = new Builder().build(); + + // TODO: make final + private long maxObjectSize; + private int maxCacheEntries; + private int maxUpdateRetries; + private boolean allow303Caching; + private boolean weakETagOnPutDeleteAllowed; + private boolean heuristicCachingEnabled; + private float heuristicCoefficient; + private long heuristicDefaultLifetime; + private boolean isSharedCache; + private int asynchronousWorkersMax; + private int asynchronousWorkersCore; + private int asynchronousWorkerIdleLifetimeSecs; + private int revalidationQueueSize; + private boolean neverCacheHTTP10ResponsesWithQuery; + + /** + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public CacheConfig() { + super(); + this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES; + this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES; + this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES; + this.allow303Caching = DEFAULT_303_CACHING_ENABLED; + this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED; + this.heuristicCachingEnabled = DEFAULT_HEURISTIC_CACHING_ENABLED; + this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT; + this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME; + this.isSharedCache = true; + this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX; + this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE; + this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS; + this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE; + } + + CacheConfig( + final long maxObjectSize, + final int maxCacheEntries, + final int maxUpdateRetries, + final boolean allow303Caching, + final boolean weakETagOnPutDeleteAllowed, + final boolean heuristicCachingEnabled, + final float heuristicCoefficient, + final long heuristicDefaultLifetime, + final boolean isSharedCache, + final int asynchronousWorkersMax, + final int asynchronousWorkersCore, + final int asynchronousWorkerIdleLifetimeSecs, + final int revalidationQueueSize, + final boolean neverCacheHTTP10ResponsesWithQuery) { + super(); + this.maxObjectSize = maxObjectSize; + this.maxCacheEntries = maxCacheEntries; + this.maxUpdateRetries = maxUpdateRetries; + this.allow303Caching = allow303Caching; + this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed; + this.heuristicCachingEnabled = heuristicCachingEnabled; + this.heuristicCoefficient = heuristicCoefficient; + this.heuristicDefaultLifetime = heuristicDefaultLifetime; + this.isSharedCache = isSharedCache; + this.asynchronousWorkersMax = asynchronousWorkersMax; + this.asynchronousWorkersCore = asynchronousWorkersCore; + this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs; + this.revalidationQueueSize = revalidationQueueSize; + } + + /** + * Returns the current maximum response body size that will be cached. + * @return size in bytes + * + * @deprecated (4.2) use {@link #getMaxObjectSize()} + */ + @Deprecated + public int getMaxObjectSizeBytes() { + return maxObjectSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxObjectSize; + } + + /** + * Specifies the maximum response body size that will be eligible for caching. + * @param maxObjectSizeBytes size in bytes + * + * @deprecated (4.2) use {@link Builder}. + */ + @Deprecated + public void setMaxObjectSizeBytes(final int maxObjectSizeBytes) { + if (maxObjectSizeBytes > Integer.MAX_VALUE) { + this.maxObjectSize = Integer.MAX_VALUE; + } else { + this.maxObjectSize = maxObjectSizeBytes; + } + } + + /** + * Returns the current maximum response body size that will be cached. + * @return size in bytes + * + * @since 4.2 + */ + public long getMaxObjectSize() { + return maxObjectSize; + } + + /** + * Specifies the maximum response body size that will be eligible for caching. + * @param maxObjectSize size in bytes + * + * @since 4.2 + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setMaxObjectSize(final long maxObjectSize) { + this.maxObjectSize = maxObjectSize; + } + + /** + * Returns whether the cache will never cache HTTP 1.0 responses with a query string or not. + * @return {@code true} to not cache query string responses, {@code false} to cache if explicit cache headers are + * found + */ + public boolean isNeverCacheHTTP10ResponsesWithQuery() { + return neverCacheHTTP10ResponsesWithQuery; + } + + /** + * Returns the maximum number of cache entries the cache will retain. + */ + public int getMaxCacheEntries() { + return maxCacheEntries; + } + + /** + * Sets the maximum number of cache entries the cache will retain. + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setMaxCacheEntries(final int maxCacheEntries) { + this.maxCacheEntries = maxCacheEntries; + } + + /** + * Returns the number of times to retry a cache update on failure + */ + public int getMaxUpdateRetries(){ + return maxUpdateRetries; + } + + /** + * Sets the number of times to retry a cache update on failure + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setMaxUpdateRetries(final int maxUpdateRetries){ + this.maxUpdateRetries = maxUpdateRetries; + } + + /** + * Returns whether 303 caching is enabled. + * @return {@code true} if it is enabled. + */ + public boolean is303CachingEnabled() { + return allow303Caching; + } + + /** + * Returns whether weak etags is allowed with PUT/DELETE methods. + * @return {@code true} if it is allowed. + */ + public boolean isWeakETagOnPutDeleteAllowed() { + return weakETagOnPutDeleteAllowed; + } + + /** + * Returns whether heuristic caching is enabled. + * @return {@code true} if it is enabled. + */ + public boolean isHeuristicCachingEnabled() { + return heuristicCachingEnabled; + } + + /** + * Enables or disables heuristic caching. + * @param heuristicCachingEnabled should be {@code true} to + * permit heuristic caching, {@code false} to disable it. + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) { + this.heuristicCachingEnabled = heuristicCachingEnabled; + } + + /** + * Returns lifetime coefficient used in heuristic freshness caching. + */ + public float getHeuristicCoefficient() { + return heuristicCoefficient; + } + + /** + * Sets coefficient to be used in heuristic freshness caching. This is + * interpreted as the fraction of the time between the {@code Last-Modified} + * and {@code Date} headers of a cached response during which the cached + * response will be considered heuristically fresh. + * @param heuristicCoefficient should be between {@code 0.0} and + * {@code 1.0}. + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setHeuristicCoefficient(final float heuristicCoefficient) { + this.heuristicCoefficient = heuristicCoefficient; + } + + /** + * Get the default lifetime to be used if heuristic freshness calculation is + * not possible. + */ + public long getHeuristicDefaultLifetime() { + return heuristicDefaultLifetime; + } + + /** + * Sets default lifetime in seconds to be used if heuristic freshness + * calculation is not possible. Explicit cache control directives on + * either the request or origin response will override this, as will + * the heuristic {@code Last-Modified} freshness calculation if it is + * available. + * @param heuristicDefaultLifetimeSecs is the number of seconds to + * consider a cache-eligible response fresh in the absence of other + * information. Set this to {@code 0} to disable this style of + * heuristic caching. + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setHeuristicDefaultLifetime(final long heuristicDefaultLifetimeSecs) { + this.heuristicDefaultLifetime = heuristicDefaultLifetimeSecs; + } + + /** + * Returns whether the cache will behave as a shared cache or not. + * @return {@code true} for a shared cache, {@code false} for a non- + * shared (private) cache + */ + public boolean isSharedCache() { + return isSharedCache; + } + + /** + * Sets whether the cache should behave as a shared cache or not. + * @param isSharedCache true to behave as a shared cache, false to + * behave as a non-shared (private) cache. To have the cache + * behave like a browser cache, you want to set this to {@code false}. + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setSharedCache(final boolean isSharedCache) { + this.isSharedCache = isSharedCache; + } + + /** + * Returns the maximum number of threads to allow for background + * revalidations due to the {@code stale-while-revalidate} directive. A + * value of 0 means background revalidations are disabled. + */ + public int getAsynchronousWorkersMax() { + return asynchronousWorkersMax; + } + + /** + * Sets the maximum number of threads to allow for background + * revalidations due to the {@code stale-while-revalidate} directive. + * @param max number of threads; a value of 0 disables background + * revalidations. + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setAsynchronousWorkersMax(final int max) { + this.asynchronousWorkersMax = max; + } + + /** + * Returns the minimum number of threads to keep alive for background + * revalidations due to the {@code stale-while-revalidate} directive. + */ + public int getAsynchronousWorkersCore() { + return asynchronousWorkersCore; + } + + /** + * Sets the minimum number of threads to keep alive for background + * revalidations due to the {@code stale-while-revalidate} directive. + * @param min should be greater than zero and less than or equal + * to getAsynchronousWorkersMax() + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setAsynchronousWorkersCore(final int min) { + this.asynchronousWorkersCore = min; + } + + /** + * Returns the current maximum idle lifetime in seconds for a + * background revalidation worker thread. If a worker thread is idle + * for this long, and there are more than the core number of worker + * threads alive, the worker will be reclaimed. + */ + public int getAsynchronousWorkerIdleLifetimeSecs() { + return asynchronousWorkerIdleLifetimeSecs; + } + + /** + * Sets the current maximum idle lifetime in seconds for a + * background revalidation worker thread. If a worker thread is idle + * for this long, and there are more than the core number of worker + * threads alive, the worker will be reclaimed. + * @param secs idle lifetime in seconds + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setAsynchronousWorkerIdleLifetimeSecs(final int secs) { + this.asynchronousWorkerIdleLifetimeSecs = secs; + } + + /** + * Returns the current maximum queue size for background revalidations. + */ + public int getRevalidationQueueSize() { + return revalidationQueueSize; + } + + /** + * Sets the current maximum queue size for background revalidations. + * + * @deprecated (4.3) use {@link Builder}. + */ + @Deprecated + public void setRevalidationQueueSize(final int size) { + this.revalidationQueueSize = size; + } + + @Override + protected CacheConfig clone() throws CloneNotSupportedException { + return (CacheConfig) super.clone(); + } + + public static Builder custom() { + return new Builder(); + } + + public static Builder copy(final CacheConfig config) { + Args.notNull(config, "Cache config"); + return new Builder() + .setMaxObjectSize(config.getMaxObjectSize()) + .setMaxCacheEntries(config.getMaxCacheEntries()) + .setMaxUpdateRetries(config.getMaxUpdateRetries()) + .setHeuristicCachingEnabled(config.isHeuristicCachingEnabled()) + .setHeuristicCoefficient(config.getHeuristicCoefficient()) + .setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime()) + .setSharedCache(config.isSharedCache()) + .setAsynchronousWorkersMax(config.getAsynchronousWorkersMax()) + .setAsynchronousWorkersCore(config.getAsynchronousWorkersCore()) + .setAsynchronousWorkerIdleLifetimeSecs(config.getAsynchronousWorkerIdleLifetimeSecs()) + .setRevalidationQueueSize(config.getRevalidationQueueSize()) + .setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery()); + } + + + public static class Builder { + + private long maxObjectSize; + private int maxCacheEntries; + private int maxUpdateRetries; + private boolean allow303Caching; + private boolean weakETagOnPutDeleteAllowed; + private boolean heuristicCachingEnabled; + private float heuristicCoefficient; + private long heuristicDefaultLifetime; + private boolean isSharedCache; + private int asynchronousWorkersMax; + private int asynchronousWorkersCore; + private int asynchronousWorkerIdleLifetimeSecs; + private int revalidationQueueSize; + private boolean neverCacheHTTP10ResponsesWithQuery; + + Builder() { + this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES; + this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES; + this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES; + this.allow303Caching = DEFAULT_303_CACHING_ENABLED; + this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED; + this.heuristicCachingEnabled = false; + this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT; + this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME; + this.isSharedCache = true; + this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX; + this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE; + this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS; + this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE; + } + + /** + * Specifies the maximum response body size that will be eligible for caching. + * @param maxObjectSize size in bytes + */ + public Builder setMaxObjectSize(final long maxObjectSize) { + this.maxObjectSize = maxObjectSize; + return this; + } + + /** + * Sets the maximum number of cache entries the cache will retain. + */ + public Builder setMaxCacheEntries(final int maxCacheEntries) { + this.maxCacheEntries = maxCacheEntries; + return this; + } + + /** + * Sets the number of times to retry a cache update on failure + */ + public Builder setMaxUpdateRetries(final int maxUpdateRetries) { + this.maxUpdateRetries = maxUpdateRetries; + return this; + } + + /** + * Enables or disables 303 caching. + * @param allow303Caching should be {@code true} to + * permit 303 caching, {@code false} to disable it. + */ + public Builder setAllow303Caching(final boolean allow303Caching) { + this.allow303Caching = allow303Caching; + return this; + } + + /** + * Allows or disallows weak etags to be used with PUT/DELETE If-Match requests. + * @param weakETagOnPutDeleteAllowed should be {@code true} to + * permit weak etags, {@code false} to reject them. + */ + public Builder setWeakETagOnPutDeleteAllowed(final boolean weakETagOnPutDeleteAllowed) { + this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed; + return this; + } + + /** + * Enables or disables heuristic caching. + * @param heuristicCachingEnabled should be {@code true} to + * permit heuristic caching, {@code false} to enable it. + */ + public Builder setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) { + this.heuristicCachingEnabled = heuristicCachingEnabled; + return this; + } + + /** + * Sets coefficient to be used in heuristic freshness caching. This is + * interpreted as the fraction of the time between the {@code Last-Modified} + * and {@code Date} headers of a cached response during which the cached + * response will be considered heuristically fresh. + * @param heuristicCoefficient should be between {@code 0.0} and + * {@code 1.0}. + */ + public Builder setHeuristicCoefficient(final float heuristicCoefficient) { + this.heuristicCoefficient = heuristicCoefficient; + return this; + } + + /** + * Sets default lifetime in seconds to be used if heuristic freshness + * calculation is not possible. Explicit cache control directives on + * either the request or origin response will override this, as will + * the heuristic {@code Last-Modified} freshness calculation if it is + * available. + * @param heuristicDefaultLifetime is the number of seconds to + * consider a cache-eligible response fresh in the absence of other + * information. Set this to {@code 0} to disable this style of + * heuristic caching. + */ + public Builder setHeuristicDefaultLifetime(final long heuristicDefaultLifetime) { + this.heuristicDefaultLifetime = heuristicDefaultLifetime; + return this; + } + + /** + * Sets whether the cache should behave as a shared cache or not. + * @param isSharedCache true to behave as a shared cache, false to + * behave as a non-shared (private) cache. To have the cache + * behave like a browser cache, you want to set this to {@code false}. + */ + public Builder setSharedCache(final boolean isSharedCache) { + this.isSharedCache = isSharedCache; + return this; + } + + /** + * Sets the maximum number of threads to allow for background + * revalidations due to the {@code stale-while-revalidate} directive. + * @param asynchronousWorkersMax number of threads; a value of 0 disables background + * revalidations. + */ + public Builder setAsynchronousWorkersMax(final int asynchronousWorkersMax) { + this.asynchronousWorkersMax = asynchronousWorkersMax; + return this; + } + + /** + * Sets the minimum number of threads to keep alive for background + * revalidations due to the {@code stale-while-revalidate} directive. + * @param asynchronousWorkersCore should be greater than zero and less than or equal + * to getAsynchronousWorkersMax() + */ + public Builder setAsynchronousWorkersCore(final int asynchronousWorkersCore) { + this.asynchronousWorkersCore = asynchronousWorkersCore; + return this; + } + + /** + * Sets the current maximum idle lifetime in seconds for a + * background revalidation worker thread. If a worker thread is idle + * for this long, and there are more than the core number of worker + * threads alive, the worker will be reclaimed. + * @param asynchronousWorkerIdleLifetimeSecs idle lifetime in seconds + */ + public Builder setAsynchronousWorkerIdleLifetimeSecs(final int asynchronousWorkerIdleLifetimeSecs) { + this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs; + return this; + } + + /** + * Sets the current maximum queue size for background revalidations. + */ + public Builder setRevalidationQueueSize(final int revalidationQueueSize) { + this.revalidationQueueSize = revalidationQueueSize; + return this; + } + + /** + * Sets whether the cache should never cache HTTP 1.0 responses with a query string or not. + * @param neverCacheHTTP10ResponsesWithQuery true to never cache responses with a query + * string, false to cache if explicit cache headers are found. Set this to {@code true} + * to better emulate IE, which also never caches responses, regardless of what caching + * headers may be present. + */ + public Builder setNeverCacheHTTP10ResponsesWithQueryString( + final boolean neverCacheHTTP10ResponsesWithQuery) { + this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery; + return this; + } + + public CacheConfig build() { + return new CacheConfig( + maxObjectSize, + maxCacheEntries, + maxUpdateRetries, + allow303Caching, + weakETagOnPutDeleteAllowed, + heuristicCachingEnabled, + heuristicCoefficient, + heuristicDefaultLifetime, + isSharedCache, + asynchronousWorkersMax, + asynchronousWorkersCore, + asynchronousWorkerIdleLifetimeSecs, + revalidationQueueSize, + neverCacheHTTP10ResponsesWithQuery); + } + + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("[maxObjectSize=").append(this.maxObjectSize) + .append(", maxCacheEntries=").append(this.maxCacheEntries) + .append(", maxUpdateRetries=").append(this.maxUpdateRetries) + .append(", 303CachingEnabled=").append(this.allow303Caching) + .append(", weakETagOnPutDeleteAllowed=").append(this.weakETagOnPutDeleteAllowed) + .append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled) + .append(", heuristicCoefficient=").append(this.heuristicCoefficient) + .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime) + .append(", isSharedCache=").append(this.isSharedCache) + .append(", asynchronousWorkersMax=").append(this.asynchronousWorkersMax) + .append(", asynchronousWorkersCore=").append(this.asynchronousWorkersCore) + .append(", asynchronousWorkerIdleLifetimeSecs=").append(this.asynchronousWorkerIdleLifetimeSecs) + .append(", revalidationQueueSize=").append(this.revalidationQueueSize) + .append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery) + .append("]"); + return builder.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntity.java new file mode 100644 index 000000000..1906166d1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntity.java @@ -0,0 +1,99 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +@Immutable +class CacheEntity implements HttpEntity, Serializable { + + private static final long serialVersionUID = -3467082284120936233L; + + private final HttpCacheEntry cacheEntry; + + public CacheEntity(final HttpCacheEntry cacheEntry) { + super(); + this.cacheEntry = cacheEntry; + } + + public Header getContentType() { + return this.cacheEntry.getFirstHeader(HTTP.CONTENT_TYPE); + } + + public Header getContentEncoding() { + return this.cacheEntry.getFirstHeader(HTTP.CONTENT_ENCODING); + } + + public boolean isChunked() { + return false; + } + + public boolean isRepeatable() { + return true; + } + + public long getContentLength() { + return this.cacheEntry.getResource().length(); + } + + public InputStream getContent() throws IOException { + return this.cacheEntry.getResource().getInputStream(); + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + final InputStream instream = this.cacheEntry.getResource().getInputStream(); + try { + IOUtils.copy(instream, outstream); + } finally { + instream.close(); + } + } + + public boolean isStreaming() { + return false; + } + + public void consumeContent() throws IOException { + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntryUpdater.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntryUpdater.java new file mode 100644 index 000000000..381a7508d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheEntryUpdater.java @@ -0,0 +1,173 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.ListIterator; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.client.cache.ResourceFactory; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Update a {@link HttpCacheEntry} with new or updated information based on the latest + * 304 status response from the Server. Use the {@link HttpResponse} to perform + * the update. + * + * @since 4.1 + */ +@Immutable +class CacheEntryUpdater { + + private final ResourceFactory resourceFactory; + + CacheEntryUpdater() { + this(new HeapResourceFactory()); + } + + CacheEntryUpdater(final ResourceFactory resourceFactory) { + super(); + this.resourceFactory = resourceFactory; + } + + /** + * Update the entry with the new information from the response. Should only be used for + * 304 responses. + * + * @param requestId + * @param entry The cache Entry to be updated + * @param requestDate When the request was performed + * @param responseDate When the response was gotten + * @param response The HttpResponse from the backend server call + * @return HttpCacheEntry an updated version of the cache entry + * @throws java.io.IOException if something bad happens while trying to read the body from the original entry + */ + public HttpCacheEntry updateCacheEntry( + final String requestId, + final HttpCacheEntry entry, + final Date requestDate, + final Date responseDate, + final HttpResponse response) throws IOException { + Args.check(response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED, + "Response must have 304 status code"); + final Header[] mergedHeaders = mergeHeaders(entry, response); + Resource resource = null; + if (entry.getResource() != null) { + resource = resourceFactory.copy(requestId, entry.getResource()); + } + return new HttpCacheEntry( + requestDate, + responseDate, + entry.getStatusLine(), + mergedHeaders, + resource); + } + + protected Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) { + + if (entryAndResponseHaveDateHeader(entry, response) + && entryDateHeaderNewerThenResponse(entry, response)) { + // Don't merge headers, keep the entry's headers as they are newer. + return entry.getAllHeaders(); + } + + final List

    cacheEntryHeaderList = new ArrayList
    (Arrays.asList(entry + .getAllHeaders())); + removeCacheHeadersThatMatchResponse(cacheEntryHeaderList, response); + removeCacheEntry1xxWarnings(cacheEntryHeaderList, entry); + cacheEntryHeaderList.addAll(Arrays.asList(response.getAllHeaders())); + + return cacheEntryHeaderList.toArray(new Header[cacheEntryHeaderList.size()]); + } + + private void removeCacheHeadersThatMatchResponse(final List
    cacheEntryHeaderList, + final HttpResponse response) { + for (final Header responseHeader : response.getAllHeaders()) { + final ListIterator
    cacheEntryHeaderListIter = cacheEntryHeaderList.listIterator(); + + while (cacheEntryHeaderListIter.hasNext()) { + final String cacheEntryHeaderName = cacheEntryHeaderListIter.next().getName(); + + if (cacheEntryHeaderName.equals(responseHeader.getName())) { + cacheEntryHeaderListIter.remove(); + } + } + } + } + + private void removeCacheEntry1xxWarnings(final List
    cacheEntryHeaderList, final HttpCacheEntry entry) { + final ListIterator
    cacheEntryHeaderListIter = cacheEntryHeaderList.listIterator(); + + while (cacheEntryHeaderListIter.hasNext()) { + final String cacheEntryHeaderName = cacheEntryHeaderListIter.next().getName(); + + if (HeaderConstants.WARNING.equals(cacheEntryHeaderName)) { + for (final Header cacheEntryWarning : entry.getHeaders(HeaderConstants.WARNING)) { + if (cacheEntryWarning.getValue().startsWith("1")) { + cacheEntryHeaderListIter.remove(); + } + } + } + } + } + + private boolean entryDateHeaderNewerThenResponse(final HttpCacheEntry entry, final HttpResponse response) { + final Date entryDate = DateUtils.parseDate(entry.getFirstHeader(HTTP.DATE_HEADER) + .getValue()); + final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HTTP.DATE_HEADER) + .getValue()); + if (entryDate == null || responseDate == null) { + return false; + } + if (!entryDate.after(responseDate)) { + return false; + } + return true; + } + + private boolean entryAndResponseHaveDateHeader(final HttpCacheEntry entry, final HttpResponse response) { + if (entry.getFirstHeader(HTTP.DATE_HEADER) != null + && response.getFirstHeader(HTTP.DATE_HEADER) != null) { + return true; + } + + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheInvalidator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheInvalidator.java new file mode 100644 index 000000000..19e62869e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheInvalidator.java @@ -0,0 +1,288 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Date; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheInvalidator; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * Given a particular HttpRequest, flush any cache entries that this request + * would invalidate. + * + * @since 4.1 + */ +@Immutable +class CacheInvalidator implements HttpCacheInvalidator { + + private final HttpCacheStorage storage; + private final CacheKeyGenerator cacheKeyGenerator; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** + * Create a new {@link CacheInvalidator} for a given {@link HttpCache} and + * {@link CacheKeyGenerator}. + * + * @param uriExtractor Provides identifiers for the keys to store cache entries + * @param storage the cache to store items away in + */ + public CacheInvalidator( + final CacheKeyGenerator uriExtractor, + final HttpCacheStorage storage) { + this.cacheKeyGenerator = uriExtractor; + this.storage = storage; + } + + /** + * Remove cache entries from the cache that are no longer fresh or + * have been invalidated in some way. + * + * @param host The backend host we are talking to + * @param req The HttpRequest to that host + */ + public void flushInvalidatedCacheEntries(final HttpHost host, final HttpRequest req) { + if (requestShouldNotBeCached(req)) { + log.debug("Request should not be cached"); + + final String theUri = cacheKeyGenerator.getURI(host, req); + + final HttpCacheEntry parent = getEntry(theUri); + + log.debug("parent entry: " + parent); + + if (parent != null) { + for (final String variantURI : parent.getVariantMap().values()) { + flushEntry(variantURI); + } + flushEntry(theUri); + } + final URL reqURL = getAbsoluteURL(theUri); + if (reqURL == null) { + log.error("Couldn't transform request into valid URL"); + return; + } + final Header clHdr = req.getFirstHeader("Content-Location"); + if (clHdr != null) { + final String contentLocation = clHdr.getValue(); + if (!flushAbsoluteUriFromSameHost(reqURL, contentLocation)) { + flushRelativeUriFromSameHost(reqURL, contentLocation); + } + } + final Header lHdr = req.getFirstHeader("Location"); + if (lHdr != null) { + flushAbsoluteUriFromSameHost(reqURL, lHdr.getValue()); + } + } + } + + private void flushEntry(final String uri) { + try { + storage.removeEntry(uri); + } catch (final IOException ioe) { + log.warn("unable to flush cache entry", ioe); + } + } + + private HttpCacheEntry getEntry(final String theUri) { + try { + return storage.getEntry(theUri); + } catch (final IOException ioe) { + log.warn("could not retrieve entry from storage", ioe); + } + return null; + } + + protected void flushUriIfSameHost(final URL requestURL, final URL targetURL) { + final URL canonicalTarget = getAbsoluteURL(cacheKeyGenerator.canonicalizeUri(targetURL.toString())); + if (canonicalTarget == null) { + return; + } + if (canonicalTarget.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) { + flushEntry(canonicalTarget.toString()); + } + } + + protected void flushRelativeUriFromSameHost(final URL reqURL, final String relUri) { + final URL relURL = getRelativeURL(reqURL, relUri); + if (relURL == null) { + return; + } + flushUriIfSameHost(reqURL, relURL); + } + + + protected boolean flushAbsoluteUriFromSameHost(final URL reqURL, final String uri) { + final URL absURL = getAbsoluteURL(uri); + if (absURL == null) { + return false; + } + flushUriIfSameHost(reqURL,absURL); + return true; + } + + private URL getAbsoluteURL(final String uri) { + URL absURL = null; + try { + absURL = new URL(uri); + } catch (final MalformedURLException mue) { + // nop + } + return absURL; + } + + private URL getRelativeURL(final URL reqURL, final String relUri) { + URL relURL = null; + try { + relURL = new URL(reqURL,relUri); + } catch (final MalformedURLException e) { + // nop + } + return relURL; + } + + protected boolean requestShouldNotBeCached(final HttpRequest req) { + final String method = req.getRequestLine().getMethod(); + return notGetOrHeadRequest(method); + } + + private boolean notGetOrHeadRequest(final String method) { + return !(HeaderConstants.GET_METHOD.equals(method) || HeaderConstants.HEAD_METHOD + .equals(method)); + } + + /** Flushes entries that were invalidated by the given response + * received for the given host/request pair. + */ + public void flushInvalidatedCacheEntries(final HttpHost host, + final HttpRequest request, final HttpResponse response) { + final int status = response.getStatusLine().getStatusCode(); + if (status < 200 || status > 299) { + return; + } + final URL reqURL = getAbsoluteURL(cacheKeyGenerator.getURI(host, request)); + if (reqURL == null) { + return; + } + final URL contentLocation = getContentLocationURL(reqURL, response); + if (contentLocation != null) { + flushLocationCacheEntry(reqURL, response, contentLocation); + } + final URL location = getLocationURL(reqURL, response); + if (location != null) { + flushLocationCacheEntry(reqURL, response, location); + } + } + + private void flushLocationCacheEntry(final URL reqURL, + final HttpResponse response, final URL location) { + final String cacheKey = cacheKeyGenerator.canonicalizeUri(location.toString()); + final HttpCacheEntry entry = getEntry(cacheKey); + if (entry == null) { + return; + } + + // do not invalidate if response is strictly older than entry + // or if the etags match + + if (responseDateOlderThanEntryDate(response, entry)) { + return; + } + if (!responseAndEntryEtagsDiffer(response, entry)) { + return; + } + + flushUriIfSameHost(reqURL, location); + } + + private URL getContentLocationURL(final URL reqURL, final HttpResponse response) { + final Header clHeader = response.getFirstHeader("Content-Location"); + if (clHeader == null) { + return null; + } + final String contentLocation = clHeader.getValue(); + final URL canonURL = getAbsoluteURL(contentLocation); + if (canonURL != null) { + return canonURL; + } + return getRelativeURL(reqURL, contentLocation); + } + + private URL getLocationURL(final URL reqURL, final HttpResponse response) { + final Header clHeader = response.getFirstHeader("Location"); + if (clHeader == null) { + return null; + } + final String location = clHeader.getValue(); + final URL canonURL = getAbsoluteURL(location); + if (canonURL != null) { + return canonURL; + } + return getRelativeURL(reqURL, location); + } + + private boolean responseAndEntryEtagsDiffer(final HttpResponse response, + final HttpCacheEntry entry) { + final Header entryEtag = entry.getFirstHeader(HeaderConstants.ETAG); + final Header responseEtag = response.getFirstHeader(HeaderConstants.ETAG); + if (entryEtag == null || responseEtag == null) { + return false; + } + return (!entryEtag.getValue().equals(responseEtag.getValue())); + } + + private boolean responseDateOlderThanEntryDate(final HttpResponse response, + final HttpCacheEntry entry) { + final Header entryDateHeader = entry.getFirstHeader(HTTP.DATE_HEADER); + final Header responseDateHeader = response.getFirstHeader(HTTP.DATE_HEADER); + if (entryDateHeader == null || responseDateHeader == null) { + /* be conservative; should probably flush */ + return false; + } + final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue()); + final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue()); + if (entryDate == null || responseDate == null) { + return false; + } + return responseDate.before(entryDate); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheKeyGenerator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheKeyGenerator.java new file mode 100644 index 000000000..b0628041f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheKeyGenerator.java @@ -0,0 +1,178 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; + +/** + * @since 4.1 + */ +@Immutable +class CacheKeyGenerator { + + private static final URI BASE_URI = URI.create("http://example.com/"); + + /** + * For a given {@link HttpHost} and {@link HttpRequest} get a URI from the + * pair that I can use as an identifier KEY into my HttpCache + * + * @param host The host for this request + * @param req the {@link HttpRequest} + * @return String the extracted URI + */ + public String getURI(final HttpHost host, final HttpRequest req) { + if (isRelativeRequest(req)) { + return canonicalizeUri(String.format("%s%s", host.toString(), req.getRequestLine().getUri())); + } + return canonicalizeUri(req.getRequestLine().getUri()); + } + + public String canonicalizeUri(final String uri) { + try { + final URI normalized = URIUtils.resolve(BASE_URI, uri); + final URL u = new URL(normalized.toASCIIString()); + final String protocol = u.getProtocol(); + final String hostname = u.getHost(); + final int port = canonicalizePort(u.getPort(), protocol); + final String path = u.getPath(); + final String query = u.getQuery(); + final String file = (query != null) ? (path + "?" + query) : path; + final URL out = new URL(protocol, hostname, port, file); + return out.toString(); + } catch (final IllegalArgumentException e) { + return uri; + } catch (final MalformedURLException e) { + return uri; + } + } + + private int canonicalizePort(final int port, final String protocol) { + if (port == -1 && "http".equalsIgnoreCase(protocol)) { + return 80; + } else if (port == -1 && "https".equalsIgnoreCase(protocol)) { + return 443; + } + return port; + } + + private boolean isRelativeRequest(final HttpRequest req) { + final String requestUri = req.getRequestLine().getUri(); + return ("*".equals(requestUri) || requestUri.startsWith("/")); + } + + protected String getFullHeaderValue(final Header[] headers) { + if (headers == null) { + return ""; + } + + final StringBuilder buf = new StringBuilder(""); + boolean first = true; + for (final Header hdr : headers) { + if (!first) { + buf.append(", "); + } + buf.append(hdr.getValue().trim()); + first = false; + + } + return buf.toString(); + } + + /** + * For a given {@link HttpHost} and {@link HttpRequest} if the request has a + * VARY header - I need to get an additional URI from the pair of host and + * request so that I can also store the variant into my HttpCache. + * + * @param host The host for this request + * @param req the {@link HttpRequest} + * @param entry the parent entry used to track the variants + * @return String the extracted variant URI + */ + public String getVariantURI(final HttpHost host, final HttpRequest req, final HttpCacheEntry entry) { + if (!entry.hasVariants()) { + return getURI(host, req); + } + return getVariantKey(req, entry) + getURI(host, req); + } + + /** + * Compute a "variant key" from the headers of a given request that are + * covered by the Vary header of a given cache entry. Any request whose + * varying headers match those of this request should have the same + * variant key. + * @param req originating request + * @param entry cache entry in question that has variants + * @return a String variant key + */ + public String getVariantKey(final HttpRequest req, final HttpCacheEntry entry) { + final List variantHeaderNames = new ArrayList(); + for (final Header varyHdr : entry.getHeaders(HeaderConstants.VARY)) { + for (final HeaderElement elt : varyHdr.getElements()) { + variantHeaderNames.add(elt.getName()); + } + } + Collections.sort(variantHeaderNames); + + StringBuilder buf; + try { + buf = new StringBuilder("{"); + boolean first = true; + for (final String headerName : variantHeaderNames) { + if (!first) { + buf.append("&"); + } + buf.append(URLEncoder.encode(headerName, Consts.UTF_8.name())); + buf.append("="); + buf.append(URLEncoder.encode(getFullHeaderValue(req.getHeaders(headerName)), + Consts.UTF_8.name())); + first = false; + } + buf.append("}"); + } catch (final UnsupportedEncodingException uee) { + throw new RuntimeException("couldn't encode to UTF-8", uee); + } + return buf.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheMap.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheMap.java new file mode 100644 index 000000000..0ed80920c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheMap.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.LinkedHashMap; +import java.util.Map; + +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; + +final class CacheMap extends LinkedHashMap { + + private static final long serialVersionUID = -7750025207539768511L; + + private final int maxEntries; + + CacheMap(final int maxEntries) { + super(20, 0.75f, true); + this.maxEntries = maxEntries; + } + + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return size() > this.maxEntries; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheValidityPolicy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheValidityPolicy.java new file mode 100644 index 000000000..333dfbbfb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheValidityPolicy.java @@ -0,0 +1,320 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.Date; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * @since 4.1 + */ +@Immutable +class CacheValidityPolicy { + + public static final long MAX_AGE = 2147483648L; + + CacheValidityPolicy() { + super(); + } + + public long getCurrentAgeSecs(final HttpCacheEntry entry, final Date now) { + return getCorrectedInitialAgeSecs(entry) + getResidentTimeSecs(entry, now); + } + + public long getFreshnessLifetimeSecs(final HttpCacheEntry entry) { + final long maxage = getMaxAge(entry); + if (maxage > -1) { + return maxage; + } + + final Date dateValue = entry.getDate(); + if (dateValue == null) { + return 0L; + } + + final Date expiry = getExpirationDate(entry); + if (expiry == null) { + return 0; + } + final long diff = expiry.getTime() - dateValue.getTime(); + return (diff / 1000); + } + + public boolean isResponseFresh(final HttpCacheEntry entry, final Date now) { + return (getCurrentAgeSecs(entry, now) < getFreshnessLifetimeSecs(entry)); + } + + /** + * Decides if this response is fresh enough based Last-Modified and Date, if available. + * This entry is meant to be used when isResponseFresh returns false. The algorithm is as follows: + * + * if last-modified and date are defined, freshness lifetime is coefficient*(date-lastModified), + * else freshness lifetime is defaultLifetime + * + * @param entry the cache entry + * @param now what time is it currently (When is right NOW) + * @param coefficient Part of the heuristic for cache entry freshness + * @param defaultLifetime How long can I assume a cache entry is default TTL + * @return {@code true} if the response is fresh + */ + public boolean isResponseHeuristicallyFresh(final HttpCacheEntry entry, + final Date now, final float coefficient, final long defaultLifetime) { + return (getCurrentAgeSecs(entry, now) < getHeuristicFreshnessLifetimeSecs(entry, coefficient, defaultLifetime)); + } + + public long getHeuristicFreshnessLifetimeSecs(final HttpCacheEntry entry, + final float coefficient, final long defaultLifetime) { + final Date dateValue = entry.getDate(); + final Date lastModifiedValue = getLastModifiedValue(entry); + + if (dateValue != null && lastModifiedValue != null) { + final long diff = dateValue.getTime() - lastModifiedValue.getTime(); + if (diff < 0) { + return 0; + } + return (long)(coefficient * (diff / 1000)); + } + + return defaultLifetime; + } + + public boolean isRevalidatable(final HttpCacheEntry entry) { + return entry.getFirstHeader(HeaderConstants.ETAG) != null + || entry.getFirstHeader(HeaderConstants.LAST_MODIFIED) != null; + } + + public boolean mustRevalidate(final HttpCacheEntry entry) { + return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE); + } + + public boolean proxyRevalidate(final HttpCacheEntry entry) { + return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE); + } + + public boolean mayReturnStaleWhileRevalidating(final HttpCacheEntry entry, final Date now) { + for (final Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for(final HeaderElement elt : h.getElements()) { + if (HeaderConstants.STALE_WHILE_REVALIDATE.equalsIgnoreCase(elt.getName())) { + try { + final int allowedStalenessLifetime = Integer.parseInt(elt.getValue()); + if (getStalenessSecs(entry, now) <= allowedStalenessLifetime) { + return true; + } + } catch (final NumberFormatException nfe) { + // skip malformed directive + } + } + } + } + + return false; + } + + public boolean mayReturnStaleIfError(final HttpRequest request, + final HttpCacheEntry entry, final Date now) { + final long stalenessSecs = getStalenessSecs(entry, now); + return mayReturnStaleIfError(request.getHeaders(HeaderConstants.CACHE_CONTROL), + stalenessSecs) + || mayReturnStaleIfError(entry.getHeaders(HeaderConstants.CACHE_CONTROL), + stalenessSecs); + } + + private boolean mayReturnStaleIfError(final Header[] headers, final long stalenessSecs) { + boolean result = false; + for(final Header h : headers) { + for(final HeaderElement elt : h.getElements()) { + if (HeaderConstants.STALE_IF_ERROR.equals(elt.getName())) { + try { + final int staleIfErrorSecs = Integer.parseInt(elt.getValue()); + if (stalenessSecs <= staleIfErrorSecs) { + result = true; + break; + } + } catch (final NumberFormatException nfe) { + // skip malformed directive + } + } + } + } + return result; + } + + /** + * @deprecated (4.3) use {@link HttpCacheEntry#getDate()}. + * @param entry + * @return the Date of the entry + */ + @Deprecated + protected Date getDateValue(final HttpCacheEntry entry) { + return entry.getDate(); + } + + protected Date getLastModifiedValue(final HttpCacheEntry entry) { + final Header dateHdr = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED); + if (dateHdr == null) { + return null; + } + return DateUtils.parseDate(dateHdr.getValue()); + } + + protected long getContentLengthValue(final HttpCacheEntry entry) { + final Header cl = entry.getFirstHeader(HTTP.CONTENT_LEN); + if (cl == null) { + return -1; + } + + try { + return Long.parseLong(cl.getValue()); + } catch (final NumberFormatException ex) { + return -1; + } + } + + protected boolean hasContentLengthHeader(final HttpCacheEntry entry) { + return null != entry.getFirstHeader(HTTP.CONTENT_LEN); + } + + /** + * This matters for deciding whether the cache entry is valid to serve as a + * response. If these values do not match, we might have a partial response + * + * @param entry The cache entry we are currently working with + * @return boolean indicating whether actual length matches Content-Length + */ + protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) { + return !hasContentLengthHeader(entry) || getContentLengthValue(entry) == entry.getResource().length(); + } + + protected long getApparentAgeSecs(final HttpCacheEntry entry) { + final Date dateValue = entry.getDate(); + if (dateValue == null) { + return MAX_AGE; + } + final long diff = entry.getResponseDate().getTime() - dateValue.getTime(); + if (diff < 0L) { + return 0; + } + return (diff / 1000); + } + + protected long getAgeValue(final HttpCacheEntry entry) { + long ageValue = 0; + for (final Header hdr : entry.getHeaders(HeaderConstants.AGE)) { + long hdrAge; + try { + hdrAge = Long.parseLong(hdr.getValue()); + if (hdrAge < 0) { + hdrAge = MAX_AGE; + } + } catch (final NumberFormatException nfe) { + hdrAge = MAX_AGE; + } + ageValue = (hdrAge > ageValue) ? hdrAge : ageValue; + } + return ageValue; + } + + protected long getCorrectedReceivedAgeSecs(final HttpCacheEntry entry) { + final long apparentAge = getApparentAgeSecs(entry); + final long ageValue = getAgeValue(entry); + return (apparentAge > ageValue) ? apparentAge : ageValue; + } + + protected long getResponseDelaySecs(final HttpCacheEntry entry) { + final long diff = entry.getResponseDate().getTime() - entry.getRequestDate().getTime(); + return (diff / 1000L); + } + + protected long getCorrectedInitialAgeSecs(final HttpCacheEntry entry) { + return getCorrectedReceivedAgeSecs(entry) + getResponseDelaySecs(entry); + } + + protected long getResidentTimeSecs(final HttpCacheEntry entry, final Date now) { + final long diff = now.getTime() - entry.getResponseDate().getTime(); + return (diff / 1000L); + } + + protected long getMaxAge(final HttpCacheEntry entry) { + long maxage = -1; + for (final Header hdr : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for (final HeaderElement elt : hdr.getElements()) { + if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName()) + || "s-maxage".equals(elt.getName())) { + try { + final long currMaxAge = Long.parseLong(elt.getValue()); + if (maxage == -1 || currMaxAge < maxage) { + maxage = currMaxAge; + } + } catch (final NumberFormatException nfe) { + // be conservative if can't parse + maxage = 0; + } + } + } + } + return maxage; + } + + protected Date getExpirationDate(final HttpCacheEntry entry) { + final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES); + if (expiresHeader == null) { + return null; + } + return DateUtils.parseDate(expiresHeader.getValue()); + } + + public boolean hasCacheControlDirective(final HttpCacheEntry entry, + final String directive) { + for (final Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for(final HeaderElement elt : h.getElements()) { + if (directive.equalsIgnoreCase(elt.getName())) { + return true; + } + } + } + return false; + } + + public long getStalenessSecs(final HttpCacheEntry entry, final Date now) { + final long age = getCurrentAgeSecs(entry, now); + final long freshness = getFreshnessLifetimeSecs(entry); + if (age <= freshness) { + return 0L; + } + return (age - freshness); + } + + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheableRequestPolicy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheableRequestPolicy.java new file mode 100644 index 000000000..500da01dc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheableRequestPolicy.java @@ -0,0 +1,96 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; + +/** + * Determines if an HttpRequest is allowed to be served from the cache. + * + * @since 4.1 + */ +@Immutable +class CacheableRequestPolicy { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** + * Determines if an HttpRequest can be served from the cache. + * + * @param request + * an HttpRequest + * @return boolean Is it possible to serve this request from cache + */ + public boolean isServableFromCache(final HttpRequest request) { + final String method = request.getRequestLine().getMethod(); + + final ProtocolVersion pv = request.getRequestLine().getProtocolVersion(); + if (HttpVersion.HTTP_1_1.compareToVersion(pv) != 0) { + log.trace("non-HTTP/1.1 request was not serveable from cache"); + return false; + } + + if (!method.equals(HeaderConstants.GET_METHOD)) { + log.trace("non-GET request was not serveable from cache"); + return false; + } + + if (request.getHeaders(HeaderConstants.PRAGMA).length > 0) { + log.trace("request with Pragma header was not serveable from cache"); + return false; + } + + final Header[] cacheControlHeaders = request.getHeaders(HeaderConstants.CACHE_CONTROL); + for (final Header cacheControl : cacheControlHeaders) { + for (final HeaderElement cacheControlElement : cacheControl.getElements()) { + if (HeaderConstants.CACHE_CONTROL_NO_STORE.equalsIgnoreCase(cacheControlElement + .getName())) { + log.trace("Request with no-store was not serveable from cache"); + return false; + } + + if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(cacheControlElement + .getName())) { + log.trace("Request with no-cache was not serveable from cache"); + return false; + } + } + } + + log.trace("Request was serveable from cache"); + return true; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedHttpResponseGenerator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedHttpResponseGenerator.java new file mode 100644 index 000000000..f42529e57 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedHttpResponseGenerator.java @@ -0,0 +1,166 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.Date; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.message.BasicHeader; +import ch.boye.httpclientandroidlib.message.BasicHttpResponse; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * Rebuilds an {@link HttpResponse} from a {@link net.sf.ehcache.CacheEntry} + * + * @since 4.1 + */ +@Immutable +class CachedHttpResponseGenerator { + + private final CacheValidityPolicy validityStrategy; + + CachedHttpResponseGenerator(final CacheValidityPolicy validityStrategy) { + super(); + this.validityStrategy = validityStrategy; + } + + CachedHttpResponseGenerator() { + this(new CacheValidityPolicy()); + } + + /** + * If I was able to use a {@link CacheEntity} to response to the {@link ch.boye.httpclientandroidlib.HttpRequest} then + * generate an {@link HttpResponse} based on the cache entry. + * @param entry + * {@link CacheEntity} to transform into an {@link HttpResponse} + * @return {@link HttpResponse} that was constructed + */ + CloseableHttpResponse generateResponse(final HttpCacheEntry entry) { + + final Date now = new Date(); + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, entry + .getStatusCode(), entry.getReasonPhrase()); + + response.setHeaders(entry.getAllHeaders()); + + if (entry.getResource() != null) { + final HttpEntity entity = new CacheEntity(entry); + addMissingContentLengthHeader(response, entity); + response.setEntity(entity); + } + + final long age = this.validityStrategy.getCurrentAgeSecs(entry, now); + if (age > 0) { + if (age >= Integer.MAX_VALUE) { + response.setHeader(HeaderConstants.AGE, "2147483648"); + } else { + response.setHeader(HeaderConstants.AGE, "" + ((int) age)); + } + } + + return Proxies.enhanceResponse(response); + } + + /** + * Generate a 304 - Not Modified response from a {@link CacheEntity}. This should be + * used to respond to conditional requests, when the entry exists or has been re-validated. + */ + CloseableHttpResponse generateNotModifiedResponse(final HttpCacheEntry entry) { + + final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, + HttpStatus.SC_NOT_MODIFIED, "Not Modified"); + + // The response MUST include the following headers + // (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) + + // - Date, unless its omission is required by section 14.8.1 + Header dateHeader = entry.getFirstHeader(HTTP.DATE_HEADER); + if (dateHeader == null) { + dateHeader = new BasicHeader(HTTP.DATE_HEADER, DateUtils.formatDate(new Date())); + } + response.addHeader(dateHeader); + + // - ETag and/or Content-Location, if the header would have been sent + // in a 200 response to the same request + final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); + if (etagHeader != null) { + response.addHeader(etagHeader); + } + + final Header contentLocationHeader = entry.getFirstHeader("Content-Location"); + if (contentLocationHeader != null) { + response.addHeader(contentLocationHeader); + } + + // - Expires, Cache-Control, and/or Vary, if the field-value might + // differ from that sent in any previous response for the same + // variant + final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES); + if (expiresHeader != null) { + response.addHeader(expiresHeader); + } + + final Header cacheControlHeader = entry.getFirstHeader(HeaderConstants.CACHE_CONTROL); + if (cacheControlHeader != null) { + response.addHeader(cacheControlHeader); + } + + final Header varyHeader = entry.getFirstHeader(HeaderConstants.VARY); + if (varyHeader != null) { + response.addHeader(varyHeader); + } + + return Proxies.enhanceResponse(response); + } + + private void addMissingContentLengthHeader(final HttpResponse response, final HttpEntity entity) { + if (transferEncodingIsPresent(response)) { + return; + } + + Header contentLength = response.getFirstHeader(HTTP.CONTENT_LEN); + if (contentLength == null) { + contentLength = new BasicHeader(HTTP.CONTENT_LEN, Long.toString(entity + .getContentLength())); + response.setHeader(contentLength); + } + } + + private boolean transferEncodingIsPresent(final HttpResponse response) { + final Header hdr = response.getFirstHeader(HTTP.TRANSFER_ENCODING); + return hdr != null; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedResponseSuitabilityChecker.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedResponseSuitabilityChecker.java new file mode 100644 index 000000000..a289729d3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachedResponseSuitabilityChecker.java @@ -0,0 +1,346 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.Date; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; + +/** + * Determines whether a given {@link HttpCacheEntry} is suitable to be + * used as a response for a given {@link HttpRequest}. + * + * @since 4.1 + */ +@Immutable +class CachedResponseSuitabilityChecker { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final boolean sharedCache; + private final boolean useHeuristicCaching; + private final float heuristicCoefficient; + private final long heuristicDefaultLifetime; + private final CacheValidityPolicy validityStrategy; + + CachedResponseSuitabilityChecker(final CacheValidityPolicy validityStrategy, + final CacheConfig config) { + super(); + this.validityStrategy = validityStrategy; + this.sharedCache = config.isSharedCache(); + this.useHeuristicCaching = config.isHeuristicCachingEnabled(); + this.heuristicCoefficient = config.getHeuristicCoefficient(); + this.heuristicDefaultLifetime = config.getHeuristicDefaultLifetime(); + } + + CachedResponseSuitabilityChecker(final CacheConfig config) { + this(new CacheValidityPolicy(), config); + } + + private boolean isFreshEnough(final HttpCacheEntry entry, final HttpRequest request, final Date now) { + if (validityStrategy.isResponseFresh(entry, now)) { + return true; + } + if (useHeuristicCaching && + validityStrategy.isResponseHeuristicallyFresh(entry, now, heuristicCoefficient, heuristicDefaultLifetime)) { + return true; + } + if (originInsistsOnFreshness(entry)) { + return false; + } + final long maxstale = getMaxStale(request); + if (maxstale == -1) { + return false; + } + return (maxstale > validityStrategy.getStalenessSecs(entry, now)); + } + + private boolean originInsistsOnFreshness(final HttpCacheEntry entry) { + if (validityStrategy.mustRevalidate(entry)) { + return true; + } + if (!sharedCache) { + return false; + } + return validityStrategy.proxyRevalidate(entry) || + validityStrategy.hasCacheControlDirective(entry, "s-maxage"); + } + + private long getMaxStale(final HttpRequest request) { + long maxstale = -1; + for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for(final HeaderElement elt : h.getElements()) { + if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) { + if ((elt.getValue() == null || "".equals(elt.getValue().trim())) + && maxstale == -1) { + maxstale = Long.MAX_VALUE; + } else { + try { + long val = Long.parseLong(elt.getValue()); + if (val < 0) { + val = 0; + } + if (maxstale == -1 || val < maxstale) { + maxstale = val; + } + } catch (final NumberFormatException nfe) { + // err on the side of preserving semantic transparency + maxstale = 0; + } + } + } + } + } + return maxstale; + } + + /** + * Determine if I can utilize a {@link HttpCacheEntry} to respond to the given + * {@link HttpRequest} + * + * @param host + * {@link HttpHost} + * @param request + * {@link HttpRequest} + * @param entry + * {@link HttpCacheEntry} + * @param now + * Right now in time + * @return boolean yes/no answer + */ + public boolean canCachedResponseBeUsed(final HttpHost host, final HttpRequest request, final HttpCacheEntry entry, final Date now) { + + if (!isFreshEnough(entry, request, now)) { + log.trace("Cache entry was not fresh enough"); + return false; + } + + if (!validityStrategy.contentLengthHeaderMatchesActualLength(entry)) { + log.debug("Cache entry Content-Length and header information do not match"); + return false; + } + + if (hasUnsupportedConditionalHeaders(request)) { + log.debug("Request contained conditional headers we don't handle"); + return false; + } + + if (!isConditional(request) && entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { + return false; + } + + if (isConditional(request) && !allConditionalsMatch(request, entry, now)) { + return false; + } + + for (final Header ccHdr : request.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for (final HeaderElement elt : ccHdr.getElements()) { + if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) { + log.trace("Response contained NO CACHE directive, cache was not suitable"); + return false; + } + + if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elt.getName())) { + log.trace("Response contained NO STORE directive, cache was not suitable"); + return false; + } + + if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) { + try { + final int maxage = Integer.parseInt(elt.getValue()); + if (validityStrategy.getCurrentAgeSecs(entry, now) > maxage) { + log.trace("Response from cache was NOT suitable due to max age"); + return false; + } + } catch (final NumberFormatException ex) { + // err conservatively + log.debug("Response from cache was malformed" + ex.getMessage()); + return false; + } + } + + if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) { + try { + final int maxstale = Integer.parseInt(elt.getValue()); + if (validityStrategy.getFreshnessLifetimeSecs(entry) > maxstale) { + log.trace("Response from cache was not suitable due to Max stale freshness"); + return false; + } + } catch (final NumberFormatException ex) { + // err conservatively + log.debug("Response from cache was malformed: " + ex.getMessage()); + return false; + } + } + + if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())) { + try { + final long minfresh = Long.parseLong(elt.getValue()); + if (minfresh < 0L) { + return false; + } + final long age = validityStrategy.getCurrentAgeSecs(entry, now); + final long freshness = validityStrategy.getFreshnessLifetimeSecs(entry); + if (freshness - age < minfresh) { + log.trace("Response from cache was not suitable due to min fresh " + + "freshness requirement"); + return false; + } + } catch (final NumberFormatException ex) { + // err conservatively + log.debug("Response from cache was malformed: " + ex.getMessage()); + return false; + } + } + } + } + + log.trace("Response from cache was suitable"); + return true; + } + + /** + * Is this request the type of conditional request we support? + * @param request The current httpRequest being made + * @return {@code true} if the request is supported + */ + public boolean isConditional(final HttpRequest request) { + return hasSupportedEtagValidator(request) || hasSupportedLastModifiedValidator(request); + } + + /** + * Check that conditionals that are part of this request match + * @param request The current httpRequest being made + * @param entry the cache entry + * @param now right NOW in time + * @return {@code true} if the request matches all conditionals + */ + public boolean allConditionalsMatch(final HttpRequest request, final HttpCacheEntry entry, final Date now) { + final boolean hasEtagValidator = hasSupportedEtagValidator(request); + final boolean hasLastModifiedValidator = hasSupportedLastModifiedValidator(request); + + final boolean etagValidatorMatches = (hasEtagValidator) && etagValidatorMatches(request, entry); + final boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry, now); + + if ((hasEtagValidator && hasLastModifiedValidator) + && !(etagValidatorMatches && lastModifiedValidatorMatches)) { + return false; + } else if (hasEtagValidator && !etagValidatorMatches) { + return false; + } + + if (hasLastModifiedValidator && !lastModifiedValidatorMatches) { + return false; + } + return true; + } + + private boolean hasUnsupportedConditionalHeaders(final HttpRequest request) { + return (request.getFirstHeader(HeaderConstants.IF_RANGE) != null + || request.getFirstHeader(HeaderConstants.IF_MATCH) != null + || hasValidDateField(request, HeaderConstants.IF_UNMODIFIED_SINCE)); + } + + private boolean hasSupportedEtagValidator(final HttpRequest request) { + return request.containsHeader(HeaderConstants.IF_NONE_MATCH); + } + + private boolean hasSupportedLastModifiedValidator(final HttpRequest request) { + return hasValidDateField(request, HeaderConstants.IF_MODIFIED_SINCE); + } + + /** + * Check entry against If-None-Match + * @param request The current httpRequest being made + * @param entry the cache entry + * @return boolean does the etag validator match + */ + private boolean etagValidatorMatches(final HttpRequest request, final HttpCacheEntry entry) { + final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); + final String etag = (etagHeader != null) ? etagHeader.getValue() : null; + final Header[] ifNoneMatch = request.getHeaders(HeaderConstants.IF_NONE_MATCH); + if (ifNoneMatch != null) { + for (final Header h : ifNoneMatch) { + for (final HeaderElement elt : h.getElements()) { + final String reqEtag = elt.toString(); + if (("*".equals(reqEtag) && etag != null) + || reqEtag.equals(etag)) { + return true; + } + } + } + } + return false; + } + + /** + * Check entry against If-Modified-Since, if If-Modified-Since is in the future it is invalid as per + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + * @param request The current httpRequest being made + * @param entry the cache entry + * @param now right NOW in time + * @return boolean Does the last modified header match + */ + private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Date now) { + final Header lastModifiedHeader = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED); + Date lastModified = null; + if (lastModifiedHeader != null) { + lastModified = DateUtils.parseDate(lastModifiedHeader.getValue()); + } + if (lastModified == null) { + return false; + } + + for (final Header h : request.getHeaders(HeaderConstants.IF_MODIFIED_SINCE)) { + final Date ifModifiedSince = DateUtils.parseDate(h.getValue()); + if (ifModifiedSince != null) { + if (ifModifiedSince.after(now) || lastModified.after(ifModifiedSince)) { + return false; + } + } + } + return true; + } + + private boolean hasValidDateField(final HttpRequest request, final String headerName) { + for(final Header h : request.getHeaders(headerName)) { + final Date date = DateUtils.parseDate(h.getValue()); + return date != null; + } + return false; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingExec.java new file mode 100644 index 000000000..cf9b8154a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingExec.java @@ -0,0 +1,870 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.cache.CacheResponseStatus; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheContext; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage; +import ch.boye.httpclientandroidlib.client.cache.ResourceFactory; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain; +import ch.boye.httpclientandroidlib.message.BasicHttpResponse; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.VersionInfo; + +/** + * Request executor in the request execution chain that is responsible for + * transparent client-side caching. The current implementation is conditionally + * compliant with HTTP/1.1 (meaning all the MUST and MUST NOTs are obeyed), + * although quite a lot, though not all, of the SHOULDs and SHOULD NOTs + * are obeyed too. + *

    + * Folks that would like to experiment with alternative storage backends + * should look at the {@link HttpCacheStorage} interface and the related + * package documentation there. You may also be interested in the provided + * {@link ch.boye.httpclientandroidlib.impl.client.cache.ehcache.EhcacheHttpCacheStorage + * EhCache} and {@link + * ch.boye.httpclientandroidlib.impl.client.cache.memcached.MemcachedHttpCacheStorage + * memcached} storage backends. + *

    + * Further responsibilities such as communication with the opposite + * endpoint is delegated to the next executor in the request execution + * chain. + * + * @since 4.3 + */ +@ThreadSafe // So long as the responseCache implementation is threadsafe +public class CachingExec implements ClientExecChain { + + private final static boolean SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS = false; + + private final AtomicLong cacheHits = new AtomicLong(); + private final AtomicLong cacheMisses = new AtomicLong(); + private final AtomicLong cacheUpdates = new AtomicLong(); + + private final Map viaHeaders = new HashMap(4); + + private final CacheConfig cacheConfig; + private final ClientExecChain backend; + private final HttpCache responseCache; + private final CacheValidityPolicy validityPolicy; + private final CachedHttpResponseGenerator responseGenerator; + private final CacheableRequestPolicy cacheableRequestPolicy; + private final CachedResponseSuitabilityChecker suitabilityChecker; + private final ConditionalRequestBuilder conditionalRequestBuilder; + private final ResponseProtocolCompliance responseCompliance; + private final RequestProtocolCompliance requestCompliance; + private final ResponseCachingPolicy responseCachingPolicy; + + private final AsynchronousValidator asynchRevalidator; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + public CachingExec( + final ClientExecChain backend, + final HttpCache cache, + final CacheConfig config) { + this(backend, cache, config, null); + } + + public CachingExec( + final ClientExecChain backend, + final HttpCache cache, + final CacheConfig config, + final AsynchronousValidator asynchRevalidator) { + super(); + Args.notNull(backend, "HTTP backend"); + Args.notNull(cache, "HttpCache"); + this.cacheConfig = config != null ? config : CacheConfig.DEFAULT; + this.backend = backend; + this.responseCache = cache; + this.validityPolicy = new CacheValidityPolicy(); + this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy); + this.cacheableRequestPolicy = new CacheableRequestPolicy(); + this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, this.cacheConfig); + this.conditionalRequestBuilder = new ConditionalRequestBuilder(); + this.responseCompliance = new ResponseProtocolCompliance(); + this.requestCompliance = new RequestProtocolCompliance(this.cacheConfig.isWeakETagOnPutDeleteAllowed()); + this.responseCachingPolicy = new ResponseCachingPolicy( + this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(), + this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery(), this.cacheConfig.is303CachingEnabled()); + this.asynchRevalidator = asynchRevalidator; + } + + public CachingExec( + final ClientExecChain backend, + final ResourceFactory resourceFactory, + final HttpCacheStorage storage, + final CacheConfig config) { + this(backend, new BasicHttpCache(resourceFactory, storage, config), config); + } + + public CachingExec(final ClientExecChain backend) { + this(backend, new BasicHttpCache(), CacheConfig.DEFAULT); + } + + CachingExec( + final ClientExecChain backend, + final HttpCache responseCache, + final CacheValidityPolicy validityPolicy, + final ResponseCachingPolicy responseCachingPolicy, + final CachedHttpResponseGenerator responseGenerator, + final CacheableRequestPolicy cacheableRequestPolicy, + final CachedResponseSuitabilityChecker suitabilityChecker, + final ConditionalRequestBuilder conditionalRequestBuilder, + final ResponseProtocolCompliance responseCompliance, + final RequestProtocolCompliance requestCompliance, + final CacheConfig config, + final AsynchronousValidator asynchRevalidator) { + this.cacheConfig = config != null ? config : CacheConfig.DEFAULT; + this.backend = backend; + this.responseCache = responseCache; + this.validityPolicy = validityPolicy; + this.responseCachingPolicy = responseCachingPolicy; + this.responseGenerator = responseGenerator; + this.cacheableRequestPolicy = cacheableRequestPolicy; + this.suitabilityChecker = suitabilityChecker; + this.conditionalRequestBuilder = conditionalRequestBuilder; + this.responseCompliance = responseCompliance; + this.requestCompliance = requestCompliance; + this.asynchRevalidator = asynchRevalidator; + } + + /** + * Reports the number of times that the cache successfully responded + * to an {@link HttpRequest} without contacting the origin server. + * @return the number of cache hits + */ + public long getCacheHits() { + return cacheHits.get(); + } + + /** + * Reports the number of times that the cache contacted the origin + * server because it had no appropriate response cached. + * @return the number of cache misses + */ + public long getCacheMisses() { + return cacheMisses.get(); + } + + /** + * Reports the number of times that the cache was able to satisfy + * a response by revalidating an existing but stale cache entry. + * @return the number of cache revalidations + */ + public long getCacheUpdates() { + return cacheUpdates.get(); + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request) throws IOException, HttpException { + return execute(route, request, HttpClientContext.create(), null); + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context) throws IOException, HttpException { + return execute(route, request, context, null); + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + + final HttpHost target = context.getTargetHost(); + final String via = generateViaHeader(request.getOriginal()); + + // default response context + setResponseStatus(context, CacheResponseStatus.CACHE_MISS); + + if (clientRequestsOurOptions(request)) { + setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); + return Proxies.enhanceResponse(new OptionsHttp11Response()); + } + + final HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context); + if (fatalErrorResponse != null) { + return Proxies.enhanceResponse(fatalErrorResponse); + } + + requestCompliance.makeRequestCompliant(request); + request.addHeader("Via",via); + + flushEntriesInvalidatedByRequest(context.getTargetHost(), request); + + if (!cacheableRequestPolicy.isServableFromCache(request)) { + log.debug("Request is not servable from cache"); + return callBackend(route, request, context, execAware); + } + + final HttpCacheEntry entry = satisfyFromCache(target, request); + if (entry == null) { + log.debug("Cache miss"); + return handleCacheMiss(route, request, context, execAware); + } else { + return handleCacheHit(route, request, context, execAware, entry); + } + } + + private CloseableHttpResponse handleCacheHit( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final HttpCacheEntry entry) throws IOException, HttpException { + final HttpHost target = context.getTargetHost(); + recordCacheHit(target, request); + CloseableHttpResponse out = null; + final Date now = getCurrentDate(); + if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) { + log.debug("Cache hit"); + out = generateCachedResponse(request, context, entry, now); + } else if (!mayCallBackend(request)) { + log.debug("Cache entry not suitable but only-if-cached requested"); + out = generateGatewayTimeout(context); + } else if (!(entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED + && !suitabilityChecker.isConditional(request))) { + log.debug("Revalidating cache entry"); + return revalidateCacheEntry(route, request, context, execAware, entry, now); + } else { + log.debug("Cache entry not usable; calling backend"); + return callBackend(route, request, context, execAware); + } + context.setAttribute(HttpClientContext.HTTP_ROUTE, route); + context.setAttribute(HttpClientContext.HTTP_TARGET_HOST, target); + context.setAttribute(HttpClientContext.HTTP_REQUEST, request); + context.setAttribute(HttpClientContext.HTTP_RESPONSE, out); + context.setAttribute(HttpClientContext.HTTP_REQ_SENT, Boolean.TRUE); + return out; + } + + private CloseableHttpResponse revalidateCacheEntry( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final HttpCacheEntry entry, + final Date now) throws HttpException { + + try { + if (asynchRevalidator != null + && !staleResponseNotAllowed(request, entry, now) + && validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) { + log.trace("Serving stale with asynchronous revalidation"); + final CloseableHttpResponse resp = generateCachedResponse(request, context, entry, now); + asynchRevalidator.revalidateCacheEntry(this, route, request, context, execAware, entry); + return resp; + } + return revalidateCacheEntry(route, request, context, execAware, entry); + } catch (final IOException ioex) { + return handleRevalidationFailure(request, context, entry, now); + } + } + + private CloseableHttpResponse handleCacheMiss( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + final HttpHost target = context.getTargetHost(); + recordCacheMiss(target, request); + + if (!mayCallBackend(request)) { + return Proxies.enhanceResponse( + new BasicHttpResponse( + HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout")); + } + + final Map variants = getExistingCacheVariants(target, request); + if (variants != null && variants.size() > 0) { + return negotiateResponseFromVariants(route, request, context, + execAware, variants); + } + + return callBackend(route, request, context, execAware); + } + + private HttpCacheEntry satisfyFromCache( + final HttpHost target, final HttpRequestWrapper request) { + HttpCacheEntry entry = null; + try { + entry = responseCache.getCacheEntry(target, request); + } catch (final IOException ioe) { + log.warn("Unable to retrieve entries from cache", ioe); + } + return entry; + } + + private HttpResponse getFatallyNoncompliantResponse( + final HttpRequestWrapper request, + final HttpContext context) { + HttpResponse fatalErrorResponse = null; + final List fatalError = requestCompliance.requestIsFatallyNonCompliant(request); + + for (final RequestProtocolError error : fatalError) { + setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); + fatalErrorResponse = requestCompliance.getErrorForRequest(error); + } + return fatalErrorResponse; + } + + private Map getExistingCacheVariants( + final HttpHost target, + final HttpRequestWrapper request) { + Map variants = null; + try { + variants = responseCache.getVariantCacheEntriesWithEtags(target, request); + } catch (final IOException ioe) { + log.warn("Unable to retrieve variant entries from cache", ioe); + } + return variants; + } + + private void recordCacheMiss(final HttpHost target, final HttpRequestWrapper request) { + cacheMisses.getAndIncrement(); + if (log.isTraceEnabled()) { + final RequestLine rl = request.getRequestLine(); + log.trace("Cache miss [host: " + target + "; uri: " + rl.getUri() + "]"); + } + } + + private void recordCacheHit(final HttpHost target, final HttpRequestWrapper request) { + cacheHits.getAndIncrement(); + if (log.isTraceEnabled()) { + final RequestLine rl = request.getRequestLine(); + log.trace("Cache hit [host: " + target + "; uri: " + rl.getUri() + "]"); + } + } + + private void recordCacheUpdate(final HttpContext context) { + cacheUpdates.getAndIncrement(); + setResponseStatus(context, CacheResponseStatus.VALIDATED); + } + + private void flushEntriesInvalidatedByRequest( + final HttpHost target, + final HttpRequestWrapper request) { + try { + responseCache.flushInvalidatedCacheEntriesFor(target, request); + } catch (final IOException ioe) { + log.warn("Unable to flush invalidated entries from cache", ioe); + } + } + + private CloseableHttpResponse generateCachedResponse(final HttpRequestWrapper request, + final HttpContext context, final HttpCacheEntry entry, final Date now) { + final CloseableHttpResponse cachedResponse; + if (request.containsHeader(HeaderConstants.IF_NONE_MATCH) + || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) { + cachedResponse = responseGenerator.generateNotModifiedResponse(entry); + } else { + cachedResponse = responseGenerator.generateResponse(entry); + } + setResponseStatus(context, CacheResponseStatus.CACHE_HIT); + if (validityPolicy.getStalenessSecs(entry, now) > 0L) { + cachedResponse.addHeader(HeaderConstants.WARNING,"110 localhost \"Response is stale\""); + } + return cachedResponse; + } + + private CloseableHttpResponse handleRevalidationFailure( + final HttpRequestWrapper request, + final HttpContext context, + final HttpCacheEntry entry, + final Date now) { + if (staleResponseNotAllowed(request, entry, now)) { + return generateGatewayTimeout(context); + } else { + return unvalidatedCacheHit(context, entry); + } + } + + private CloseableHttpResponse generateGatewayTimeout( + final HttpContext context) { + setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); + return Proxies.enhanceResponse(new BasicHttpResponse( + HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, + "Gateway Timeout")); + } + + private CloseableHttpResponse unvalidatedCacheHit( + final HttpContext context, final HttpCacheEntry entry) { + final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(entry); + setResponseStatus(context, CacheResponseStatus.CACHE_HIT); + cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\""); + return cachedResponse; + } + + private boolean staleResponseNotAllowed( + final HttpRequestWrapper request, + final HttpCacheEntry entry, + final Date now) { + return validityPolicy.mustRevalidate(entry) + || (cacheConfig.isSharedCache() && validityPolicy.proxyRevalidate(entry)) + || explicitFreshnessRequest(request, entry, now); + } + + private boolean mayCallBackend(final HttpRequestWrapper request) { + for (final Header h: request.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for (final HeaderElement elt : h.getElements()) { + if ("only-if-cached".equals(elt.getName())) { + log.trace("Request marked only-if-cached"); + return false; + } + } + } + return true; + } + + private boolean explicitFreshnessRequest( + final HttpRequestWrapper request, + final HttpCacheEntry entry, + final Date now) { + for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for(final HeaderElement elt : h.getElements()) { + if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) { + try { + final int maxstale = Integer.parseInt(elt.getValue()); + final long age = validityPolicy.getCurrentAgeSecs(entry, now); + final long lifetime = validityPolicy.getFreshnessLifetimeSecs(entry); + if (age - lifetime > maxstale) { + return true; + } + } catch (final NumberFormatException nfe) { + return true; + } + } else if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName()) + || HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) { + return true; + } + } + } + return false; + } + + private String generateViaHeader(final HttpMessage msg) { + + final ProtocolVersion pv = msg.getProtocolVersion(); + final String existingEntry = viaHeaders.get(pv); + if (existingEntry != null) { + return existingEntry; + } + + final VersionInfo vi = VersionInfo.loadVersionInfo("ch.boye.httpclientandroidlib.client", getClass().getClassLoader()); + final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; + + String value; + if ("http".equalsIgnoreCase(pv.getProtocol())) { + value = String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getMajor(), pv.getMinor(), + release); + } else { + value = String.format("%s/%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getProtocol(), pv.getMajor(), + pv.getMinor(), release); + } + viaHeaders.put(pv, value); + + return value; + } + + private void setResponseStatus(final HttpContext context, final CacheResponseStatus value) { + if (context != null) { + context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, value); + } + } + + /** + * Reports whether this {@code CachingHttpClient} implementation + * supports byte-range requests as specified by the {@code Range} + * and {@code Content-Range} headers. + * @return {@code true} if byte-range requests are supported + */ + public boolean supportsRangeAndContentRangeHeaders() { + return SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS; + } + + Date getCurrentDate() { + return new Date(); + } + + boolean clientRequestsOurOptions(final HttpRequest request) { + final RequestLine line = request.getRequestLine(); + + if (!HeaderConstants.OPTIONS_METHOD.equals(line.getMethod())) { + return false; + } + + if (!"*".equals(line.getUri())) { + return false; + } + + if (!"0".equals(request.getFirstHeader(HeaderConstants.MAX_FORWARDS).getValue())) { + return false; + } + + return true; + } + + CloseableHttpResponse callBackend( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + + final Date requestDate = getCurrentDate(); + + log.trace("Calling the backend"); + final CloseableHttpResponse backendResponse = backend.execute(route, request, context, execAware); + try { + backendResponse.addHeader("Via", generateViaHeader(backendResponse)); + return handleBackendResponse(route, request, context, execAware, + requestDate, getCurrentDate(), backendResponse); + } catch (final IOException ex) { + backendResponse.close(); + throw ex; + } catch (final RuntimeException ex) { + backendResponse.close(); + throw ex; + } + } + + private boolean revalidationResponseIsTooOld(final HttpResponse backendResponse, + final HttpCacheEntry cacheEntry) { + final Header entryDateHeader = cacheEntry.getFirstHeader(HTTP.DATE_HEADER); + final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER); + if (entryDateHeader != null && responseDateHeader != null) { + final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue()); + final Date respDate = DateUtils.parseDate(responseDateHeader.getValue()); + if (entryDate == null || respDate == null) { + // either backend response or cached entry did not have a valid + // Date header, so we can't tell if they are out of order + // according to the origin clock; thus we can skip the + // unconditional retry recommended in 13.2.6 of RFC 2616. + return false; + } + if (respDate.before(entryDate)) { + return true; + } + } + return false; + } + + CloseableHttpResponse negotiateResponseFromVariants( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final Map variants) throws IOException, HttpException { + final HttpRequestWrapper conditionalRequest = conditionalRequestBuilder + .buildConditionalRequestFromVariants(request, variants); + + final Date requestDate = getCurrentDate(); + final CloseableHttpResponse backendResponse = backend.execute( + route, conditionalRequest, context, execAware); + try { + final Date responseDate = getCurrentDate(); + + backendResponse.addHeader("Via", generateViaHeader(backendResponse)); + + if (backendResponse.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_MODIFIED) { + return handleBackendResponse( + route, request, context, execAware, + requestDate, responseDate, backendResponse); + } + + final Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG); + if (resultEtagHeader == null) { + log.warn("304 response did not contain ETag"); + IOUtils.consume(backendResponse.getEntity()); + backendResponse.close(); + return callBackend(route, request, context, execAware); + } + + final String resultEtag = resultEtagHeader.getValue(); + final Variant matchingVariant = variants.get(resultEtag); + if (matchingVariant == null) { + log.debug("304 response did not contain ETag matching one sent in If-None-Match"); + IOUtils.consume(backendResponse.getEntity()); + backendResponse.close(); + return callBackend(route, request, context, execAware); + } + + final HttpCacheEntry matchedEntry = matchingVariant.getEntry(); + + if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) { + IOUtils.consume(backendResponse.getEntity()); + backendResponse.close(); + return retryRequestUnconditionally(route, request, context, execAware, matchedEntry); + } + + recordCacheUpdate(context); + + final HttpCacheEntry responseEntry = getUpdatedVariantEntry( + context.getTargetHost(), conditionalRequest, requestDate, responseDate, + backendResponse, matchingVariant, matchedEntry); + backendResponse.close(); + + final CloseableHttpResponse resp = responseGenerator.generateResponse(responseEntry); + tryToUpdateVariantMap(context.getTargetHost(), request, matchingVariant); + + if (shouldSendNotModifiedResponse(request, responseEntry)) { + return responseGenerator.generateNotModifiedResponse(responseEntry); + } + return resp; + } catch (final IOException ex) { + backendResponse.close(); + throw ex; + } catch (final RuntimeException ex) { + backendResponse.close(); + throw ex; + } + } + + private CloseableHttpResponse retryRequestUnconditionally( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final HttpCacheEntry matchedEntry) throws IOException, HttpException { + final HttpRequestWrapper unconditional = conditionalRequestBuilder + .buildUnconditionalRequest(request, matchedEntry); + return callBackend(route, unconditional, context, execAware); + } + + private HttpCacheEntry getUpdatedVariantEntry( + final HttpHost target, + final HttpRequestWrapper conditionalRequest, + final Date requestDate, + final Date responseDate, + final CloseableHttpResponse backendResponse, + final Variant matchingVariant, + final HttpCacheEntry matchedEntry) throws IOException { + HttpCacheEntry responseEntry = matchedEntry; + try { + responseEntry = responseCache.updateVariantCacheEntry(target, conditionalRequest, + matchedEntry, backendResponse, requestDate, responseDate, matchingVariant.getCacheKey()); + } catch (final IOException ioe) { + log.warn("Could not update cache entry", ioe); + } finally { + backendResponse.close(); + } + return responseEntry; + } + + private void tryToUpdateVariantMap( + final HttpHost target, + final HttpRequestWrapper request, + final Variant matchingVariant) { + try { + responseCache.reuseVariantEntryFor(target, request, matchingVariant); + } catch (final IOException ioe) { + log.warn("Could not update cache entry to reuse variant", ioe); + } + } + + private boolean shouldSendNotModifiedResponse( + final HttpRequestWrapper request, + final HttpCacheEntry responseEntry) { + return (suitabilityChecker.isConditional(request) + && suitabilityChecker.allConditionalsMatch(request, responseEntry, new Date())); + } + + CloseableHttpResponse revalidateCacheEntry( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final HttpCacheEntry cacheEntry) throws IOException, HttpException { + + final HttpRequestWrapper conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(request, cacheEntry); + + Date requestDate = getCurrentDate(); + CloseableHttpResponse backendResponse = backend.execute( + route, conditionalRequest, context, execAware); + Date responseDate = getCurrentDate(); + + if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) { + backendResponse.close(); + final HttpRequestWrapper unconditional = conditionalRequestBuilder + .buildUnconditionalRequest(request, cacheEntry); + requestDate = getCurrentDate(); + backendResponse = backend.execute(route, unconditional, context, execAware); + responseDate = getCurrentDate(); + } + + backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse)); + + final int statusCode = backendResponse.getStatusLine().getStatusCode(); + if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) { + recordCacheUpdate(context); + } + + if (statusCode == HttpStatus.SC_NOT_MODIFIED) { + final HttpCacheEntry updatedEntry = responseCache.updateCacheEntry( + context.getTargetHost(), request, cacheEntry, + backendResponse, requestDate, responseDate); + if (suitabilityChecker.isConditional(request) + && suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) { + return responseGenerator + .generateNotModifiedResponse(updatedEntry); + } + return responseGenerator.generateResponse(updatedEntry); + } + + if (staleIfErrorAppliesTo(statusCode) + && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate()) + && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) { + try { + final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry); + cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\""); + return cachedResponse; + } finally { + backendResponse.close(); + } + } + return handleBackendResponse( + route, conditionalRequest, context, execAware, + requestDate, responseDate, backendResponse); + } + + private boolean staleIfErrorAppliesTo(final int statusCode) { + return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR + || statusCode == HttpStatus.SC_BAD_GATEWAY + || statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE + || statusCode == HttpStatus.SC_GATEWAY_TIMEOUT; + } + + CloseableHttpResponse handleBackendResponse( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware, + final Date requestDate, + final Date responseDate, + final CloseableHttpResponse backendResponse) throws IOException { + + log.trace("Handling Backend response"); + responseCompliance.ensureProtocolCompliance(request, backendResponse); + + final HttpHost target = context.getTargetHost(); + final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse); + responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse); + if (cacheable && !alreadyHaveNewerCacheEntry(target, request, backendResponse)) { + storeRequestIfModifiedSinceFor304Response(request, backendResponse); + return responseCache.cacheAndReturnResponse(target, request, + backendResponse, requestDate, responseDate); + } + if (!cacheable) { + try { + responseCache.flushCacheEntriesFor(target, request); + } catch (final IOException ioe) { + log.warn("Unable to flush invalid cache entries", ioe); + } + } + return backendResponse; + } + + /** + * For 304 Not modified responses, adds a "Last-Modified" header with the + * value of the "If-Modified-Since" header passed in the request. This + * header is required to be able to reuse match the cache entry for + * subsequent requests but as defined in http specifications it is not + * included in 304 responses by backend servers. This header will not be + * included in the resulting response. + */ + private void storeRequestIfModifiedSinceFor304Response( + final HttpRequest request, final HttpResponse backendResponse) { + if (backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { + final Header h = request.getFirstHeader("If-Modified-Since"); + if (h != null) { + backendResponse.addHeader("Last-Modified", h.getValue()); + } + } + } + + private boolean alreadyHaveNewerCacheEntry(final HttpHost target, final HttpRequestWrapper request, + final HttpResponse backendResponse) { + HttpCacheEntry existing = null; + try { + existing = responseCache.getCacheEntry(target, request); + } catch (final IOException ioe) { + // nop + } + if (existing == null) { + return false; + } + final Header entryDateHeader = existing.getFirstHeader(HTTP.DATE_HEADER); + if (entryDateHeader == null) { + return false; + } + final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER); + if (responseDateHeader == null) { + return false; + } + final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue()); + final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue()); + if (entryDate == null || responseDate == null) { + return false; + } + return responseDate.before(entryDate); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClientBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClientBuilder.java new file mode 100644 index 000000000..385324f32 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClientBuilder.java @@ -0,0 +1,149 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.File; + +import ch.boye.httpclientandroidlib.client.cache.HttpCacheInvalidator; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage; +import ch.boye.httpclientandroidlib.client.cache.ResourceFactory; +import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder; +import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain; + +/** + * Builder for {@link ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient} + * instances capable of client-side caching. + * + * @since 4.3 + */ +public class CachingHttpClientBuilder extends HttpClientBuilder { + + private ResourceFactory resourceFactory; + private HttpCacheStorage storage; + private File cacheDir; + private CacheConfig cacheConfig; + private SchedulingStrategy schedulingStrategy; + private HttpCacheInvalidator httpCacheInvalidator; + + public static CachingHttpClientBuilder create() { + return new CachingHttpClientBuilder(); + } + + protected CachingHttpClientBuilder() { + super(); + } + + public final CachingHttpClientBuilder setResourceFactory( + final ResourceFactory resourceFactory) { + this.resourceFactory = resourceFactory; + return this; + } + + public final CachingHttpClientBuilder setHttpCacheStorage( + final HttpCacheStorage storage) { + this.storage = storage; + return this; + } + + public final CachingHttpClientBuilder setCacheDir( + final File cacheDir) { + this.cacheDir = cacheDir; + return this; + } + + public final CachingHttpClientBuilder setCacheConfig( + final CacheConfig cacheConfig) { + this.cacheConfig = cacheConfig; + return this; + } + + public final CachingHttpClientBuilder setSchedulingStrategy( + final SchedulingStrategy schedulingStrategy) { + this.schedulingStrategy = schedulingStrategy; + return this; + } + + public final CachingHttpClientBuilder setHttpCacheInvalidator( + final HttpCacheInvalidator cacheInvalidator) { + this.httpCacheInvalidator = cacheInvalidator; + return this; + } + + @Override + protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) { + final CacheConfig config = this.cacheConfig != null ? this.cacheConfig : CacheConfig.DEFAULT; + ResourceFactory resourceFactory = this.resourceFactory; + if (resourceFactory == null) { + if (this.cacheDir == null) { + resourceFactory = new HeapResourceFactory(); + } else { + resourceFactory = new FileResourceFactory(cacheDir); + } + } + HttpCacheStorage storage = this.storage; + if (storage == null) { + if (this.cacheDir == null) { + storage = new BasicHttpCacheStorage(config); + } else { + final ManagedHttpCacheStorage managedStorage = new ManagedHttpCacheStorage(config); + addCloseable(managedStorage); + storage = managedStorage; + } + } + final AsynchronousValidator revalidator = createAsynchronousRevalidator(config); + final CacheKeyGenerator uriExtractor = new CacheKeyGenerator(); + + HttpCacheInvalidator cacheInvalidator = this.httpCacheInvalidator; + if (cacheInvalidator == null) { + cacheInvalidator = new CacheInvalidator(uriExtractor, storage); + } + + return new CachingExec(mainExec, + new BasicHttpCache( + resourceFactory, + storage, config, + uriExtractor, + cacheInvalidator), config, revalidator); + } + + private AsynchronousValidator createAsynchronousRevalidator(final CacheConfig config) { + if (config.getAsynchronousWorkersMax() > 0) { + final SchedulingStrategy configuredSchedulingStrategy = createSchedulingStrategy(config); + final AsynchronousValidator revalidator = new AsynchronousValidator( + configuredSchedulingStrategy); + addCloseable(revalidator); + return revalidator; + } + return null; + } + + @SuppressWarnings("resource") + private SchedulingStrategy createSchedulingStrategy(final CacheConfig config) { + return schedulingStrategy != null ? schedulingStrategy : new ImmediateSchedulingStrategy(config); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClients.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClients.java new file mode 100644 index 000000000..328c147f3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CachingHttpClients.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.File; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; + +/** + * Factory methods for {@link CloseableHttpClient} instances + * capable of client-side caching. + * + * @since 4.3 + */ +@Immutable +public class CachingHttpClients { + + private CachingHttpClients() { + super(); + } + + /** + * Creates builder object for construction of custom + * {@link CloseableHttpClient} instances. + */ + public static CachingHttpClientBuilder custom() { + return CachingHttpClientBuilder.create(); + } + + /** + * Creates {@link CloseableHttpClient} instance that uses a memory bound + * response cache. + */ + public static CloseableHttpClient createMemoryBound() { + return CachingHttpClientBuilder.create().build(); + } + + /** + * Creates {@link CloseableHttpClient} instance that uses a file system + * bound response cache. + * + * @param cacheDir location of response cache. + */ + public static CloseableHttpClient createFileBound(final File cacheDir) { + return CachingHttpClientBuilder.create().setCacheDir(cacheDir).build(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CombinedEntity.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CombinedEntity.java new file mode 100644 index 000000000..62bfaff3e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CombinedEntity.java @@ -0,0 +1,104 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.SequenceInputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.entity.AbstractHttpEntity; +import ch.boye.httpclientandroidlib.util.Args; + +@NotThreadSafe +class CombinedEntity extends AbstractHttpEntity { + + private final Resource resource; + private final InputStream combinedStream; + + CombinedEntity(final Resource resource, final InputStream instream) throws IOException { + super(); + this.resource = resource; + this.combinedStream = new SequenceInputStream( + new ResourceStream(resource.getInputStream()), instream); + } + + public long getContentLength() { + return -1; + } + + public boolean isRepeatable() { + return false; + } + + public boolean isStreaming() { + return true; + } + + public InputStream getContent() throws IOException, IllegalStateException { + return this.combinedStream; + } + + public void writeTo(final OutputStream outstream) throws IOException { + Args.notNull(outstream, "Output stream"); + final InputStream instream = getContent(); + try { + int l; + final byte[] tmp = new byte[2048]; + while ((l = instream.read(tmp)) != -1) { + outstream.write(tmp, 0, l); + } + } finally { + instream.close(); + } + } + + private void dispose() { + this.resource.dispose(); + } + + class ResourceStream extends FilterInputStream { + + protected ResourceStream(final InputStream in) { + super(in); + } + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + dispose(); + } + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ConditionalRequestBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ConditionalRequestBuilder.java new file mode 100644 index 000000000..0edc4fb85 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ConditionalRequestBuilder.java @@ -0,0 +1,140 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.Map; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; + +/** + * @since 4.1 + */ +@Immutable +class ConditionalRequestBuilder { + + /** + * When a {@link HttpCacheEntry} is stale but 'might' be used as a response + * to an {@link ch.boye.httpclientandroidlib.HttpRequest} we will attempt to revalidate + * the entry with the origin. Build the origin {@link ch.boye.httpclientandroidlib.HttpRequest} + * here and return it. + * + * @param request the original request from the caller + * @param cacheEntry the entry that needs to be re-validated + * @return the wrapped request + * @throws ProtocolException when I am unable to build a new origin request. + */ + public HttpRequestWrapper buildConditionalRequest(final HttpRequestWrapper request, final HttpCacheEntry cacheEntry) + throws ProtocolException { + final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(request.getOriginal()); + newRequest.setHeaders(request.getAllHeaders()); + final Header eTag = cacheEntry.getFirstHeader(HeaderConstants.ETAG); + if (eTag != null) { + newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, eTag.getValue()); + } + final Header lastModified = cacheEntry.getFirstHeader(HeaderConstants.LAST_MODIFIED); + if (lastModified != null) { + newRequest.setHeader(HeaderConstants.IF_MODIFIED_SINCE, lastModified.getValue()); + } + boolean mustRevalidate = false; + for(final Header h : cacheEntry.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for(final HeaderElement elt : h.getElements()) { + if (HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE.equalsIgnoreCase(elt.getName()) + || HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE.equalsIgnoreCase(elt.getName())) { + mustRevalidate = true; + break; + } + } + } + if (mustRevalidate) { + newRequest.addHeader(HeaderConstants.CACHE_CONTROL, HeaderConstants.CACHE_CONTROL_MAX_AGE + "=0"); + } + return newRequest; + + } + + /** + * When a {@link HttpCacheEntry} does not exist for a specific + * {@link ch.boye.httpclientandroidlib.HttpRequest} we attempt to see if an existing + * {@link HttpCacheEntry} is appropriate by building a conditional + * {@link ch.boye.httpclientandroidlib.HttpRequest} using the variants' ETag values. + * If no such values exist, the request is unmodified + * + * @param request the original request from the caller + * @param variants + * @return the wrapped request + */ + public HttpRequestWrapper buildConditionalRequestFromVariants(final HttpRequestWrapper request, + final Map variants) { + final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(request.getOriginal()); + newRequest.setHeaders(request.getAllHeaders()); + + // we do not support partial content so all etags are used + final StringBuilder etags = new StringBuilder(); + boolean first = true; + for(final String etag : variants.keySet()) { + if (!first) { + etags.append(","); + } + first = false; + etags.append(etag); + } + + newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, etags.toString()); + return newRequest; + } + + /** + * Returns a request to unconditionally validate a cache entry with + * the origin. In certain cases (due to multiple intervening caches) + * our cache may actually receive a response to a normal conditional + * validation where the Date header is actually older than that of + * our current cache entry. In this case, the protocol recommendation + * is to retry the validation and force syncup with the origin. + * @param request client request we are trying to satisfy + * @param entry existing cache entry we are trying to validate + * @return an unconditional validation request + */ + public HttpRequestWrapper buildUnconditionalRequest(final HttpRequestWrapper request, final HttpCacheEntry entry) { + final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(request.getOriginal()); + newRequest.setHeaders(request.getAllHeaders()); + newRequest.addHeader(HeaderConstants.CACHE_CONTROL,HeaderConstants.CACHE_CONTROL_NO_CACHE); + newRequest.addHeader(HeaderConstants.PRAGMA,HeaderConstants.CACHE_CONTROL_NO_CACHE); + newRequest.removeHeaders(HeaderConstants.IF_RANGE); + newRequest.removeHeaders(HeaderConstants.IF_MATCH); + newRequest.removeHeaders(HeaderConstants.IF_NONE_MATCH); + newRequest.removeHeaders(HeaderConstants.IF_UNMODIFIED_SINCE); + newRequest.removeHeaders(HeaderConstants.IF_MODIFIED_SINCE); + return newRequest; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultFailureCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultFailureCache.java new file mode 100644 index 000000000..3646c3c32 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultFailureCache.java @@ -0,0 +1,143 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Implements a bounded failure cache. The oldest entries are discarded when + * the maximum size is exceeded. + * + * @since 4.3 + */ +@ThreadSafe +public class DefaultFailureCache implements FailureCache { + + static final int DEFAULT_MAX_SIZE = 1000; + static final int MAX_UPDATE_TRIES = 10; + + private final int maxSize; + private final ConcurrentMap storage; + + /** + * Create a new failure cache with the maximum size of + * {@link #DEFAULT_MAX_SIZE}. + */ + public DefaultFailureCache() { + this(DEFAULT_MAX_SIZE); + } + + /** + * Creates a new failure cache with the specified maximum size. + * @param maxSize the maximum number of entries the cache should store + */ + public DefaultFailureCache(final int maxSize) { + this.maxSize = maxSize; + this.storage = new ConcurrentHashMap(); + } + + public int getErrorCount(final String identifier) { + if (identifier == null) { + throw new IllegalArgumentException("identifier may not be null"); + } + final FailureCacheValue storedErrorCode = storage.get(identifier); + return storedErrorCode != null ? storedErrorCode.getErrorCount() : 0; + } + + public void resetErrorCount(final String identifier) { + if (identifier == null) { + throw new IllegalArgumentException("identifier may not be null"); + } + storage.remove(identifier); + } + + public void increaseErrorCount(final String identifier) { + if (identifier == null) { + throw new IllegalArgumentException("identifier may not be null"); + } + updateValue(identifier); + removeOldestEntryIfMapSizeExceeded(); + } + + private void updateValue(final String identifier) { + /** + * Due to concurrency it is possible that someone else is modifying an + * entry before we could write back our updated value. So we keep + * trying until it is our turn. + * + * In case there is a lot of contention on that identifier, a thread + * might starve. Thus it gives up after a certain number of failed + * update tries. + */ + for (int i = 0; i < MAX_UPDATE_TRIES; i++) { + final FailureCacheValue oldValue = storage.get(identifier); + if (oldValue == null) { + final FailureCacheValue newValue = new FailureCacheValue(identifier, 1); + if (storage.putIfAbsent(identifier, newValue) == null) { + return; + } + } + else { + final int errorCount = oldValue.getErrorCount(); + if (errorCount == Integer.MAX_VALUE) { + return; + } + final FailureCacheValue newValue = new FailureCacheValue(identifier, errorCount + 1); + if (storage.replace(identifier, oldValue, newValue)) { + return; + } + } + } + } + + private void removeOldestEntryIfMapSizeExceeded() { + if (storage.size() > maxSize) { + final FailureCacheValue valueWithOldestTimestamp = findValueWithOldestTimestamp(); + if (valueWithOldestTimestamp != null) { + storage.remove(valueWithOldestTimestamp.getKey(), valueWithOldestTimestamp); + } + } + } + + private FailureCacheValue findValueWithOldestTimestamp() { + long oldestTimestamp = Long.MAX_VALUE; + FailureCacheValue oldestValue = null; + for (final Map.Entry storageEntry : storage.entrySet()) { + final FailureCacheValue value = storageEntry.getValue(); + final long creationTimeInNanos = value.getCreationTimeInNanos(); + if (creationTimeInNanos < oldestTimestamp) { + oldestTimestamp = creationTimeInNanos; + oldestValue = storageEntry.getValue(); + } + } + return oldestValue; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultHttpCacheEntrySerializer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultHttpCacheEntrySerializer.java new file mode 100644 index 000000000..0d78a2eb1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/DefaultHttpCacheEntrySerializer.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntrySerializationException; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntrySerializer; + +/** + * {@link HttpCacheEntrySerializer} implementation that uses the default (native) + * serialization. + * + * @see java.io.Serializable + * + * @since 4.1 + */ +@Immutable +public class DefaultHttpCacheEntrySerializer implements HttpCacheEntrySerializer { + + public void writeTo(final HttpCacheEntry cacheEntry, final OutputStream os) throws IOException { + final ObjectOutputStream oos = new ObjectOutputStream(os); + try { + oos.writeObject(cacheEntry); + } finally { + oos.close(); + } + } + + public HttpCacheEntry readFrom(final InputStream is) throws IOException { + final ObjectInputStream ois = new ObjectInputStream(is); + try { + return (HttpCacheEntry) ois.readObject(); + } catch (final ClassNotFoundException ex) { + throw new HttpCacheEntrySerializationException("Class not found: " + ex.getMessage(), ex); + } finally { + ois.close(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ExponentialBackOffSchedulingStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ExponentialBackOffSchedulingStrategy.java new file mode 100644 index 000000000..286205583 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ExponentialBackOffSchedulingStrategy.java @@ -0,0 +1,174 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * An implementation that backs off exponentially based on the number of + * consecutive failed attempts stored in the + * {@link AsynchronousValidationRequest}. It uses the following defaults: + *

    + *         no delay in case it was never tried or didn't fail so far
    + *     6 secs delay for one failed attempt (= {@link #getInitialExpiryInMillis()})
    + *    60 secs delay for two failed attempts
    + *    10 mins delay for three failed attempts
    + *   100 mins delay for four failed attempts
    + *  ~16 hours delay for five failed attempts
    + *   24 hours delay for six or more failed attempts (= {@link #getMaxExpiryInMillis()})
    + * 
    + * + * The following equation is used to calculate the delay for a specific revalidation request: + *
    + *     delay = {@link #getInitialExpiryInMillis()} * Math.pow({@link #getBackOffRate()}, {@link AsynchronousValidationRequest#getConsecutiveFailedAttempts()} - 1))
    + * 
    + * The resulting delay won't exceed {@link #getMaxExpiryInMillis()}. + * + * @since 4.3 + */ +@ThreadSafe +public class ExponentialBackOffSchedulingStrategy implements SchedulingStrategy { + + public static final long DEFAULT_BACK_OFF_RATE = 10; + public static final long DEFAULT_INITIAL_EXPIRY_IN_MILLIS = TimeUnit.SECONDS.toMillis(6); + public static final long DEFAULT_MAX_EXPIRY_IN_MILLIS = TimeUnit.SECONDS.toMillis(86400); + + private final long backOffRate; + private final long initialExpiryInMillis; + private final long maxExpiryInMillis; + + private final ScheduledExecutorService executor; + + /** + * Create a new scheduling strategy using a fixed pool of worker threads. + * @param cacheConfig the thread pool configuration to be used; not null + * @see ch.boye.httpclientandroidlib.impl.client.cache.CacheConfig#getAsynchronousWorkersMax() + * @see #DEFAULT_BACK_OFF_RATE + * @see #DEFAULT_INITIAL_EXPIRY_IN_MILLIS + * @see #DEFAULT_MAX_EXPIRY_IN_MILLIS + */ + public ExponentialBackOffSchedulingStrategy(final CacheConfig cacheConfig) { + this(cacheConfig, + DEFAULT_BACK_OFF_RATE, + DEFAULT_INITIAL_EXPIRY_IN_MILLIS, + DEFAULT_MAX_EXPIRY_IN_MILLIS); + } + + /** + * Create a new scheduling strategy by using a fixed pool of worker threads and the + * given parameters to calculated the delay. + * + * @param cacheConfig the thread pool configuration to be used; not null + * @param backOffRate the back off rate to be used; not negative + * @param initialExpiryInMillis the initial expiry in milli seconds; not negative + * @param maxExpiryInMillis the upper limit of the delay in milli seconds; not negative + * @see ch.boye.httpclientandroidlib.impl.client.cache.CacheConfig#getAsynchronousWorkersMax() + * @see ExponentialBackOffSchedulingStrategy + */ + public ExponentialBackOffSchedulingStrategy( + final CacheConfig cacheConfig, + final long backOffRate, + final long initialExpiryInMillis, + final long maxExpiryInMillis) { + this(createThreadPoolFromCacheConfig(cacheConfig), + backOffRate, + initialExpiryInMillis, + maxExpiryInMillis); + } + + private static ScheduledThreadPoolExecutor createThreadPoolFromCacheConfig( + final CacheConfig cacheConfig) { + final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor( + cacheConfig.getAsynchronousWorkersMax()); + scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + return scheduledThreadPoolExecutor; + } + + ExponentialBackOffSchedulingStrategy( + final ScheduledExecutorService executor, + final long backOffRate, + final long initialExpiryInMillis, + final long maxExpiryInMillis) { + this.executor = checkNotNull("executor", executor); + this.backOffRate = checkNotNegative("backOffRate", backOffRate); + this.initialExpiryInMillis = checkNotNegative("initialExpiryInMillis", initialExpiryInMillis); + this.maxExpiryInMillis = checkNotNegative("maxExpiryInMillis", maxExpiryInMillis); + } + + public void schedule( + final AsynchronousValidationRequest revalidationRequest) { + checkNotNull("revalidationRequest", revalidationRequest); + final int consecutiveFailedAttempts = revalidationRequest.getConsecutiveFailedAttempts(); + final long delayInMillis = calculateDelayInMillis(consecutiveFailedAttempts); + executor.schedule(revalidationRequest, delayInMillis, TimeUnit.MILLISECONDS); + } + + public void close() { + executor.shutdown(); + } + + public long getBackOffRate() { + return backOffRate; + } + + public long getInitialExpiryInMillis() { + return initialExpiryInMillis; + } + + public long getMaxExpiryInMillis() { + return maxExpiryInMillis; + } + + protected long calculateDelayInMillis(final int consecutiveFailedAttempts) { + if (consecutiveFailedAttempts > 0) { + final long delayInSeconds = (long) (initialExpiryInMillis * + Math.pow(backOffRate, consecutiveFailedAttempts - 1)); + return Math.min(delayInSeconds, maxExpiryInMillis); + } + else { + return 0; + } + } + + protected static T checkNotNull(final String parameterName, final T value) { + if (value == null) { + throw new IllegalArgumentException(parameterName + " may not be null"); + } + return value; + } + + protected static long checkNotNegative(final String parameterName, final long value) { + if (value < 0) { + throw new IllegalArgumentException(parameterName + " may not be negative"); + } + return value; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCache.java new file mode 100644 index 000000000..a30fc54d9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCache.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +/** + * Increase and reset the number of errors associated with a specific + * identifier. + * + * @since 4.3 + */ +public interface FailureCache { + + /** + * Get the current error count. + * @param identifier the identifier for which the error count is requested + * @return the currently known error count or zero if there is no record + */ + int getErrorCount(String identifier); + + /** + * Reset the error count back to zero. + * @param identifier the identifier for which the error count should be + * reset + */ + void resetErrorCount(String identifier); + + /** + * Increases the error count by one. + * @param identifier the identifier for which the error count should be + * increased + */ + void increaseErrorCount(String identifier); +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCacheValue.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCacheValue.java new file mode 100644 index 000000000..48bbf55fc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FailureCacheValue.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * The error count with a creation timestamp and its associated key. + * + * @since 4.3 + */ +@Immutable +public class FailureCacheValue { + + private final long creationTimeInNanos; + private final String key; + private final int errorCount; + + public FailureCacheValue(final String key, final int errorCount) { + this.creationTimeInNanos = System.nanoTime(); + this.key = key; + this.errorCount = errorCount; + } + + public long getCreationTimeInNanos() { + return creationTimeInNanos; + } + + public String getKey() + { + return key; + } + + public int getErrorCount() { + return errorCount; + } + + @Override + public String toString() { + return "[entry creationTimeInNanos=" + creationTimeInNanos + "; " + + "key=" + key + "; errorCount=" + errorCount + ']'; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResource.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResource.java new file mode 100644 index 000000000..934ecd13d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResource.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.cache.Resource; + +/** + * Cache resource backed by a file. + * + * @since 4.1 + */ +@ThreadSafe +public class FileResource implements Resource { + + private static final long serialVersionUID = 4132244415919043397L; + + private final File file; + + private volatile boolean disposed; + + public FileResource(final File file) { + super(); + this.file = file; + this.disposed = false; + } + + synchronized File getFile() { + return this.file; + } + + public synchronized InputStream getInputStream() throws IOException { + return new FileInputStream(this.file); + } + + public synchronized long length() { + return this.file.length(); + } + + public synchronized void dispose() { + if (this.disposed) { + return; + } + this.disposed = true; + this.file.delete(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResourceFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResourceFactory.java new file mode 100644 index 000000000..8522c54b5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/FileResourceFactory.java @@ -0,0 +1,111 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.InputLimit; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.client.cache.ResourceFactory; + +/** + * Generates {@link Resource} instances whose body is stored in a temporary file. + * + * @since 4.1 + */ +@Immutable +public class FileResourceFactory implements ResourceFactory { + + private final File cacheDir; + private final BasicIdGenerator idgen; + + public FileResourceFactory(final File cacheDir) { + super(); + this.cacheDir = cacheDir; + this.idgen = new BasicIdGenerator(); + } + + private File generateUniqueCacheFile(final String requestId) { + final StringBuilder buffer = new StringBuilder(); + this.idgen.generate(buffer); + buffer.append('.'); + final int len = Math.min(requestId.length(), 100); + for (int i = 0; i < len; i++) { + final char ch = requestId.charAt(i); + if (Character.isLetterOrDigit(ch) || ch == '.') { + buffer.append(ch); + } else { + buffer.append('-'); + } + } + return new File(this.cacheDir, buffer.toString()); + } + + public Resource generate( + final String requestId, + final InputStream instream, + final InputLimit limit) throws IOException { + final File file = generateUniqueCacheFile(requestId); + final FileOutputStream outstream = new FileOutputStream(file); + try { + final byte[] buf = new byte[2048]; + long total = 0; + int l; + while ((l = instream.read(buf)) != -1) { + outstream.write(buf, 0, l); + total += l; + if (limit != null && total > limit.getValue()) { + limit.reached(); + break; + } + } + } finally { + outstream.close(); + } + return new FileResource(file); + } + + public Resource copy( + final String requestId, + final Resource resource) throws IOException { + final File file = generateUniqueCacheFile(requestId); + + if (resource instanceof FileResource) { + final File src = ((FileResource) resource).getFile(); + IOUtils.copyFile(src, file); + } else { + final FileOutputStream out = new FileOutputStream(file); + IOUtils.copyAndClose(resource.getInputStream(), out); + } + return new FileResource(file); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResource.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResource.java new file mode 100644 index 000000000..e4b353a3c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResource.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.Resource; + +/** + * Cache resource backed by a byte array on the heap. + * + * @since 4.1 + */ +@Immutable +public class HeapResource implements Resource { + + private static final long serialVersionUID = -2078599905620463394L; + + private final byte[] b; + + public HeapResource(final byte[] b) { + super(); + this.b = b; + } + + byte[] getByteArray() { + return this.b; + } + + public InputStream getInputStream() { + return new ByteArrayInputStream(this.b); + } + + public long length() { + return this.b.length; + } + + public void dispose() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResourceFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResourceFactory.java new file mode 100644 index 000000000..f45cc0965 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HeapResourceFactory.java @@ -0,0 +1,83 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.InputLimit; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.client.cache.ResourceFactory; + +/** + * Generates {@link Resource} instances stored entirely in heap. + * + * @since 4.1 + */ +@Immutable +public class HeapResourceFactory implements ResourceFactory { + + public Resource generate( + final String requestId, + final InputStream instream, + final InputLimit limit) throws IOException { + final ByteArrayOutputStream outstream = new ByteArrayOutputStream(); + final byte[] buf = new byte[2048]; + long total = 0; + int l; + while ((l = instream.read(buf)) != -1) { + outstream.write(buf, 0, l); + total += l; + if (limit != null && total > limit.getValue()) { + limit.reached(); + break; + } + } + return createResource(outstream.toByteArray()); + } + + public Resource copy( + final String requestId, + final Resource resource) throws IOException { + byte[] body; + if (resource instanceof HeapResource) { + body = ((HeapResource) resource).getByteArray(); + } else { + final ByteArrayOutputStream outstream = new ByteArrayOutputStream(); + IOUtils.copyAndClose(resource.getInputStream(), outstream); + body = outstream.toByteArray(); + } + return createResource(body); + } + + Resource createResource(final byte[] buf) { + return new HeapResource(buf); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HttpCache.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HttpCache.java new file mode 100644 index 000000000..48037b5f5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/HttpCache.java @@ -0,0 +1,166 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.util.Date; +import java.util.Map; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; + +/** + * @since 4.1 + */ +interface HttpCache { + + /** + * Clear all matching {@link HttpCacheEntry}s. + * @param host + * @param request + * @throws IOException + */ + void flushCacheEntriesFor(HttpHost host, HttpRequest request) + throws IOException; + + /** + * Clear invalidated matching {@link HttpCacheEntry}s + * @param host + * @param request + * @throws IOException + */ + void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request) + throws IOException; + + /** Clear any entries that may be invalidated by the given response to + * a particular request. + * @param host + * @param request + * @param response + */ + void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request, + HttpResponse response); + + /** + * Retrieve matching {@link HttpCacheEntry} from the cache if it exists + * @param host + * @param request + * @return the matching {@link HttpCacheEntry} or {@code null} + * @throws IOException + */ + HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request) + throws IOException; + + /** + * Retrieve all variants from the cache, if there are no variants then an empty + * {@link Map} is returned + * @param host + * @param request + * @return a Map mapping Etags to variant cache entries + * @throws IOException + */ + Map getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request) + throws IOException; + + /** + * Store a {@link HttpResponse} in the cache if possible, and return + * @param host + * @param request + * @param originResponse + * @param requestSent + * @param responseReceived + * @return the {@link HttpResponse} + * @throws IOException + */ + HttpResponse cacheAndReturnResponse( + HttpHost host, HttpRequest request, HttpResponse originResponse, + Date requestSent, Date responseReceived) + throws IOException; + + /** + * Store a {@link HttpResponse} in the cache if possible, and return + * @param host + * @param request + * @param originResponse + * @param requestSent + * @param responseReceived + * @return the {@link HttpResponse} + * @throws IOException + */ + CloseableHttpResponse cacheAndReturnResponse(HttpHost host, + HttpRequest request, CloseableHttpResponse originResponse, + Date requestSent, Date responseReceived) + throws IOException; + + /** + * Update a {@link HttpCacheEntry} using a 304 {@link HttpResponse}. + * @param target + * @param request + * @param stale + * @param originResponse + * @param requestSent + * @param responseReceived + * @return the updated {@link HttpCacheEntry} + * @throws IOException + */ + HttpCacheEntry updateCacheEntry( + HttpHost target, HttpRequest request, HttpCacheEntry stale, HttpResponse originResponse, + Date requestSent, Date responseReceived) + throws IOException; + + /** + * Update a specific {@link HttpCacheEntry} representing a cached variant + * using a 304 {@link HttpResponse}. + * @param target host for client request + * @param request actual request from upstream client + * @param stale current variant cache entry + * @param originResponse 304 response received from origin + * @param requestSent when the validating request was sent + * @param responseReceived when the validating response was received + * @param cacheKey where in the cache this entry is currently stored + * @return the updated {@link HttpCacheEntry} + * @throws IOException + */ + HttpCacheEntry updateVariantCacheEntry(HttpHost target, HttpRequest request, + HttpCacheEntry stale, HttpResponse originResponse, Date requestSent, + Date responseReceived, String cacheKey) + throws IOException; + + /** + * Specifies cache should reuse the given cached variant to satisfy + * requests whose varying headers match those of the given client request. + * @param target host of the upstream client request + * @param req request sent by upstream client + * @param variant variant cache entry to reuse + * @throws IOException may be thrown during cache update + */ + void reuseVariantEntryFor(HttpHost target, final HttpRequest req, + final Variant variant) throws IOException; +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/IOUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/IOUtils.java new file mode 100644 index 000000000..aaf41b2c9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/IOUtils.java @@ -0,0 +1,109 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +@Immutable +class IOUtils { + + static void consume(final HttpEntity entity) throws IOException { + if (entity == null) { + return; + } + if (entity.isStreaming()) { + final InputStream instream = entity.getContent(); + if (instream != null) { + instream.close(); + } + } + } + + static void copy(final InputStream in, final OutputStream out) throws IOException { + final byte[] buf = new byte[2048]; + int len; + while ((len = in.read(buf)) != -1) { + out.write(buf, 0, len); + } + } + + static void closeSilently(final Closeable closable) { + try { + closable.close(); + } catch (final IOException ignore) { + } + } + + static void copyAndClose(final InputStream in, final OutputStream out) throws IOException { + try { + copy(in, out); + in.close(); + out.close(); + } catch (final IOException ex) { + closeSilently(in); + closeSilently(out); + // Propagate the original exception + throw ex; + } + } + + static void copyFile(final File in, final File out) throws IOException { + final RandomAccessFile f1 = new RandomAccessFile(in, "r"); + final RandomAccessFile f2 = new RandomAccessFile(out, "rw"); + try { + final FileChannel c1 = f1.getChannel(); + final FileChannel c2 = f2.getChannel(); + try { + c1.transferTo(0, f1.length(), c2); + c1.close(); + c2.close(); + } catch (final IOException ex) { + closeSilently(c1); + closeSilently(c2); + // Propagate the original exception + throw ex; + } + f1.close(); + f2.close(); + } catch (final IOException ex) { + closeSilently(f1); + closeSilently(f2); + // Propagate the original exception + throw ex; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ImmediateSchedulingStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ImmediateSchedulingStrategy.java new file mode 100644 index 000000000..71d633825 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ImmediateSchedulingStrategy.java @@ -0,0 +1,88 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Immediately schedules any incoming validation request. Relies on + * {@link CacheConfig} to configure the used {@link java.util.concurrent.ThreadPoolExecutor}. + * + * @since 4.3 + */ +@ThreadSafe +public class ImmediateSchedulingStrategy implements SchedulingStrategy { + + private final ExecutorService executor; + + /** + * Uses a {@link java.util.concurrent.ThreadPoolExecutor} which is configured according to the + * given {@link CacheConfig}. + * @param cacheConfig specifies thread pool settings. See + * {@link CacheConfig#getAsynchronousWorkersMax()}, + * {@link CacheConfig#getAsynchronousWorkersCore()}, + * {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()}, + * and {@link CacheConfig#getRevalidationQueueSize()}. + */ + public ImmediateSchedulingStrategy(final CacheConfig cacheConfig) { + this(new ThreadPoolExecutor( + cacheConfig.getAsynchronousWorkersCore(), + cacheConfig.getAsynchronousWorkersMax(), + cacheConfig.getAsynchronousWorkerIdleLifetimeSecs(), + TimeUnit.SECONDS, + new ArrayBlockingQueue(cacheConfig.getRevalidationQueueSize())) + ); + } + + ImmediateSchedulingStrategy(final ExecutorService executor) { + this.executor = executor; + } + + public void schedule(final AsynchronousValidationRequest revalidationRequest) { + if (revalidationRequest == null) { + throw new IllegalArgumentException("AsynchronousValidationRequest may not be null"); + } + + executor.execute(revalidationRequest); + } + + public void close() { + executor.shutdown(); + } + + /** + * Visible for testing. + */ + void awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException { + executor.awaitTermination(timeout, unit); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ManagedHttpCacheStorage.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ManagedHttpCacheStorage.java new file mode 100644 index 000000000..3a2589cc3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ManagedHttpCacheStorage.java @@ -0,0 +1,163 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.ref.ReferenceQueue; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateCallback; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * {@link HttpCacheStorage} implementation capable of deallocating resources associated with + * the cache entries. This cache keeps track of cache entries using + * {@link java.lang.ref.PhantomReference} and maintains a collection of all resources that + * are no longer in use. The cache, however, does not automatically deallocates associated + * resources by invoking {@link Resource#dispose()} method. The consumer MUST periodically + * call {@link #cleanResources()} method to trigger resource deallocation. The cache can be + * permanently shut down using {@link #shutdown()} method. All resources associated with + * the entries used by the cache will be deallocated. + * + * This {@link HttpCacheStorage} implementation is intended for use with {@link FileResource} + * and similar. + * + * @since 4.1 + */ +@ThreadSafe +public class ManagedHttpCacheStorage implements HttpCacheStorage, Closeable { + + private final CacheMap entries; + private final ReferenceQueue morque; + private final Set resources; + private final AtomicBoolean active; + + public ManagedHttpCacheStorage(final CacheConfig config) { + super(); + this.entries = new CacheMap(config.getMaxCacheEntries()); + this.morque = new ReferenceQueue(); + this.resources = new HashSet(); + this.active = new AtomicBoolean(true); + } + + private void ensureValidState() throws IllegalStateException { + if (!this.active.get()) { + throw new IllegalStateException("Cache has been shut down"); + } + } + + private void keepResourceReference(final HttpCacheEntry entry) { + final Resource resource = entry.getResource(); + if (resource != null) { + // Must deallocate the resource when the entry is no longer in used + final ResourceReference ref = new ResourceReference(entry, this.morque); + this.resources.add(ref); + } + } + + public void putEntry(final String url, final HttpCacheEntry entry) throws IOException { + Args.notNull(url, "URL"); + Args.notNull(entry, "Cache entry"); + ensureValidState(); + synchronized (this) { + this.entries.put(url, entry); + keepResourceReference(entry); + } + } + + public HttpCacheEntry getEntry(final String url) throws IOException { + Args.notNull(url, "URL"); + ensureValidState(); + synchronized (this) { + return this.entries.get(url); + } + } + + public void removeEntry(final String url) throws IOException { + Args.notNull(url, "URL"); + ensureValidState(); + synchronized (this) { + // Cannot deallocate the associated resources immediately as the + // cache entry may still be in use + this.entries.remove(url); + } + } + + public void updateEntry( + final String url, + final HttpCacheUpdateCallback callback) throws IOException { + Args.notNull(url, "URL"); + Args.notNull(callback, "Callback"); + ensureValidState(); + synchronized (this) { + final HttpCacheEntry existing = this.entries.get(url); + final HttpCacheEntry updated = callback.update(existing); + this.entries.put(url, updated); + if (existing != updated) { + keepResourceReference(updated); + } + } + } + + public void cleanResources() { + if (this.active.get()) { + ResourceReference ref; + while ((ref = (ResourceReference) this.morque.poll()) != null) { + synchronized (this) { + this.resources.remove(ref); + } + ref.getResource().dispose(); + } + } + } + + public void shutdown() { + if (this.active.compareAndSet(true, false)) { + synchronized (this) { + this.entries.clear(); + for (final ResourceReference ref: this.resources) { + ref.getResource().dispose(); + } + this.resources.clear(); + while (this.morque.poll() != null) { + } + } + } + } + + public void close() { + shutdown(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/OptionsHttp11Response.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/OptionsHttp11Response.java new file mode 100644 index 000000000..c2e07b52d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/OptionsHttp11Response.java @@ -0,0 +1,182 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.message.AbstractHttpMessage; +import ch.boye.httpclientandroidlib.message.BasicStatusLine; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * @since 4.1 + */ +@SuppressWarnings("deprecation") +@Immutable +final class OptionsHttp11Response extends AbstractHttpMessage implements HttpResponse { + + private final StatusLine statusLine = new BasicStatusLine(HttpVersion.HTTP_1_1, + HttpStatus.SC_NOT_IMPLEMENTED, ""); + private final ProtocolVersion version = HttpVersion.HTTP_1_1; + + public StatusLine getStatusLine() { + return statusLine; + } + + public void setStatusLine(final StatusLine statusline) { + // No-op on purpose, this class is not going to be doing any work. + } + + public void setStatusLine(final ProtocolVersion ver, final int code) { + // No-op on purpose, this class is not going to be doing any work. + } + + public void setStatusLine(final ProtocolVersion ver, final int code, final String reason) { + // No-op on purpose, this class is not going to be doing any work. + } + + public void setStatusCode(final int code) throws IllegalStateException { + // No-op on purpose, this class is not going to be doing any work. + } + + public void setReasonPhrase(final String reason) throws IllegalStateException { + // No-op on purpose, this class is not going to be doing any work. + } + + public HttpEntity getEntity() { + return null; + } + + public void setEntity(final HttpEntity entity) { + // No-op on purpose, this class is not going to be doing any work. + } + + public Locale getLocale() { + return null; + } + + public void setLocale(final Locale loc) { + // No-op on purpose, this class is not going to be doing any work. + } + + public ProtocolVersion getProtocolVersion() { + return version; + } + + @Override + public boolean containsHeader(final String name) { + return this.headergroup.containsHeader(name); + } + + @Override + public Header[] getHeaders(final String name) { + return this.headergroup.getHeaders(name); + } + + @Override + public Header getFirstHeader(final String name) { + return this.headergroup.getFirstHeader(name); + } + + @Override + public Header getLastHeader(final String name) { + return this.headergroup.getLastHeader(name); + } + + @Override + public Header[] getAllHeaders() { + return this.headergroup.getAllHeaders(); + } + + @Override + public void addHeader(final Header header) { + // No-op on purpose, this class is not going to be doing any work. + } + + @Override + public void addHeader(final String name, final String value) { + // No-op on purpose, this class is not going to be doing any work. + } + + @Override + public void setHeader(final Header header) { + // No-op on purpose, this class is not going to be doing any work. + } + + @Override + public void setHeader(final String name, final String value) { + // No-op on purpose, this class is not going to be doing any work. + } + + @Override + public void setHeaders(final Header[] headers) { + // No-op on purpose, this class is not going to be doing any work. + } + + @Override + public void removeHeader(final Header header) { + // No-op on purpose, this class is not going to be doing any work. + } + + @Override + public void removeHeaders(final String name) { + // No-op on purpose, this class is not going to be doing any work. + } + + @Override + public HeaderIterator headerIterator() { + return this.headergroup.iterator(); + } + + @Override + public HeaderIterator headerIterator(final String name) { + return this.headergroup.iterator(name); + } + + @Override + public HttpParams getParams() { + if (this.params == null) { + this.params = new BasicHttpParams(); + } + return this.params; + } + + @Override + public void setParams(final HttpParams params) { + // No-op on purpose, this class is not going to be doing any work. + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Proxies.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Proxies.java new file mode 100644 index 000000000..4499d8288 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Proxies.java @@ -0,0 +1,56 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.lang.reflect.Proxy; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Proxies for HTTP message objects. + * + * @since 4.3 + */ +@NotThreadSafe +class Proxies { + + public static CloseableHttpResponse enhanceResponse(final HttpResponse original) { + Args.notNull(original, "HTTP response"); + if (original instanceof CloseableHttpResponse) { + return (CloseableHttpResponse) original; + } else { + return (CloseableHttpResponse) Proxy.newProxyInstance( + ResponseProxyHandler.class.getClassLoader(), + new Class[] { CloseableHttpResponse.class }, + new ResponseProxyHandler(original)); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolCompliance.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolCompliance.java new file mode 100644 index 000000000..ac7ab439c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolCompliance.java @@ -0,0 +1,376 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.entity.AbstractHttpEntity; +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.message.BasicHeader; +import ch.boye.httpclientandroidlib.message.BasicHttpResponse; +import ch.boye.httpclientandroidlib.message.BasicStatusLine; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * @since 4.1 + */ +@Immutable +class RequestProtocolCompliance { + private final boolean weakETagOnPutDeleteAllowed; + + public RequestProtocolCompliance() { + super(); + this.weakETagOnPutDeleteAllowed = false; + } + + public RequestProtocolCompliance(final boolean weakETagOnPutDeleteAllowed) { + super(); + this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed; + } + + private static final List disallowedWithNoCache = + Arrays.asList(HeaderConstants.CACHE_CONTROL_MIN_FRESH, HeaderConstants.CACHE_CONTROL_MAX_STALE, HeaderConstants.CACHE_CONTROL_MAX_AGE); + + /** + * Test to see if the {@link HttpRequest} is HTTP1.1 compliant or not + * and if not, we can not continue. + * + * @param request the HttpRequest Object + * @return list of {@link RequestProtocolError} + */ + public List requestIsFatallyNonCompliant(final HttpRequest request) { + final List theErrors = new ArrayList(); + + RequestProtocolError anError = requestHasWeakETagAndRange(request); + if (anError != null) { + theErrors.add(anError); + } + + if (!weakETagOnPutDeleteAllowed) { + anError = requestHasWeekETagForPUTOrDELETEIfMatch(request); + if (anError != null) { + theErrors.add(anError); + } + } + + anError = requestContainsNoCacheDirectiveWithFieldName(request); + if (anError != null) { + theErrors.add(anError); + } + + return theErrors; + } + + /** + * If the {@link HttpRequest} is non-compliant but 'fixable' we go ahead and + * fix the request here. + * + * @param request the request to check for compliance + * @throws ClientProtocolException when we have trouble making the request compliant + */ + public void makeRequestCompliant(final HttpRequestWrapper request) + throws ClientProtocolException { + + if (requestMustNotHaveEntity(request)) { + ((HttpEntityEnclosingRequest) request).setEntity(null); + } + + verifyRequestWithExpectContinueFlagHas100continueHeader(request); + verifyOPTIONSRequestWithBodyHasContentType(request); + decrementOPTIONSMaxForwardsIfGreaterThen0(request); + stripOtherFreshnessDirectivesWithNoCache(request); + + if (requestVersionIsTooLow(request) + || requestMinorVersionIsTooHighMajorVersionsMatch(request)) { + request.setProtocolVersion(HttpVersion.HTTP_1_1); + } + } + + private void stripOtherFreshnessDirectivesWithNoCache(final HttpRequest request) { + final List outElts = new ArrayList(); + boolean shouldStrip = false; + for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for(final HeaderElement elt : h.getElements()) { + if (!disallowedWithNoCache.contains(elt.getName())) { + outElts.add(elt); + } + if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) { + shouldStrip = true; + } + } + } + if (!shouldStrip) { + return; + } + request.removeHeaders(HeaderConstants.CACHE_CONTROL); + request.setHeader(HeaderConstants.CACHE_CONTROL, buildHeaderFromElements(outElts)); + } + + private String buildHeaderFromElements(final List outElts) { + final StringBuilder newHdr = new StringBuilder(""); + boolean first = true; + for(final HeaderElement elt : outElts) { + if (!first) { + newHdr.append(","); + } else { + first = false; + } + newHdr.append(elt.toString()); + } + return newHdr.toString(); + } + + private boolean requestMustNotHaveEntity(final HttpRequest request) { + return HeaderConstants.TRACE_METHOD.equals(request.getRequestLine().getMethod()) + && request instanceof HttpEntityEnclosingRequest; + } + + private void decrementOPTIONSMaxForwardsIfGreaterThen0(final HttpRequest request) { + if (!HeaderConstants.OPTIONS_METHOD.equals(request.getRequestLine().getMethod())) { + return; + } + + final Header maxForwards = request.getFirstHeader(HeaderConstants.MAX_FORWARDS); + if (maxForwards == null) { + return; + } + + request.removeHeaders(HeaderConstants.MAX_FORWARDS); + final int currentMaxForwards = Integer.parseInt(maxForwards.getValue()); + + request.setHeader(HeaderConstants.MAX_FORWARDS, Integer.toString(currentMaxForwards - 1)); + } + + private void verifyOPTIONSRequestWithBodyHasContentType(final HttpRequest request) { + if (!HeaderConstants.OPTIONS_METHOD.equals(request.getRequestLine().getMethod())) { + return; + } + + if (!(request instanceof HttpEntityEnclosingRequest)) { + return; + } + + addContentTypeHeaderIfMissing((HttpEntityEnclosingRequest) request); + } + + private void addContentTypeHeaderIfMissing(final HttpEntityEnclosingRequest request) { + if (request.getEntity().getContentType() == null) { + ((AbstractHttpEntity) request.getEntity()).setContentType( + ContentType.APPLICATION_OCTET_STREAM.getMimeType()); + } + } + + private void verifyRequestWithExpectContinueFlagHas100continueHeader(final HttpRequest request) { + if (request instanceof HttpEntityEnclosingRequest) { + + if (((HttpEntityEnclosingRequest) request).expectContinue() + && ((HttpEntityEnclosingRequest) request).getEntity() != null) { + add100ContinueHeaderIfMissing(request); + } else { + remove100ContinueHeaderIfExists(request); + } + } else { + remove100ContinueHeaderIfExists(request); + } + } + + private void remove100ContinueHeaderIfExists(final HttpRequest request) { + boolean hasHeader = false; + + final Header[] expectHeaders = request.getHeaders(HTTP.EXPECT_DIRECTIVE); + List expectElementsThatAreNot100Continue = new ArrayList(); + + for (final Header h : expectHeaders) { + for (final HeaderElement elt : h.getElements()) { + if (!(HTTP.EXPECT_CONTINUE.equalsIgnoreCase(elt.getName()))) { + expectElementsThatAreNot100Continue.add(elt); + } else { + hasHeader = true; + } + } + + if (hasHeader) { + request.removeHeader(h); + for (final HeaderElement elt : expectElementsThatAreNot100Continue) { + final BasicHeader newHeader = new BasicHeader(HTTP.EXPECT_DIRECTIVE, elt.getName()); + request.addHeader(newHeader); + } + return; + } else { + expectElementsThatAreNot100Continue = new ArrayList(); + } + } + } + + private void add100ContinueHeaderIfMissing(final HttpRequest request) { + boolean hasHeader = false; + + for (final Header h : request.getHeaders(HTTP.EXPECT_DIRECTIVE)) { + for (final HeaderElement elt : h.getElements()) { + if (HTTP.EXPECT_CONTINUE.equalsIgnoreCase(elt.getName())) { + hasHeader = true; + } + } + } + + if (!hasHeader) { + request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE); + } + } + + protected boolean requestMinorVersionIsTooHighMajorVersionsMatch(final HttpRequest request) { + final ProtocolVersion requestProtocol = request.getProtocolVersion(); + if (requestProtocol.getMajor() != HttpVersion.HTTP_1_1.getMajor()) { + return false; + } + + if (requestProtocol.getMinor() > HttpVersion.HTTP_1_1.getMinor()) { + return true; + } + + return false; + } + + protected boolean requestVersionIsTooLow(final HttpRequest request) { + return request.getProtocolVersion().compareToVersion(HttpVersion.HTTP_1_1) < 0; + } + + /** + * Extract error information about the {@link HttpRequest} telling the 'caller' + * that a problem occured. + * + * @param errorCheck What type of error should I get + * @return The {@link HttpResponse} that is the error generated + */ + public HttpResponse getErrorForRequest(final RequestProtocolError errorCheck) { + switch (errorCheck) { + case BODY_BUT_NO_LENGTH_ERROR: + return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, + HttpStatus.SC_LENGTH_REQUIRED, "")); + + case WEAK_ETAG_AND_RANGE_ERROR: + return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, + HttpStatus.SC_BAD_REQUEST, "Weak eTag not compatible with byte range")); + + case WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR: + return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, + HttpStatus.SC_BAD_REQUEST, + "Weak eTag not compatible with PUT or DELETE requests")); + + case NO_CACHE_DIRECTIVE_WITH_FIELD_NAME: + return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, + HttpStatus.SC_BAD_REQUEST, + "No-Cache directive MUST NOT include a field name")); + + default: + throw new IllegalStateException( + "The request was compliant, therefore no error can be generated for it."); + + } + } + + private RequestProtocolError requestHasWeakETagAndRange(final HttpRequest request) { + // TODO: Should these be looking at all the headers marked as Range? + final String method = request.getRequestLine().getMethod(); + if (!(HeaderConstants.GET_METHOD.equals(method))) { + return null; + } + + final Header range = request.getFirstHeader(HeaderConstants.RANGE); + if (range == null) { + return null; + } + + final Header ifRange = request.getFirstHeader(HeaderConstants.IF_RANGE); + if (ifRange == null) { + return null; + } + + final String val = ifRange.getValue(); + if (val.startsWith("W/")) { + return RequestProtocolError.WEAK_ETAG_AND_RANGE_ERROR; + } + + return null; + } + + private RequestProtocolError requestHasWeekETagForPUTOrDELETEIfMatch(final HttpRequest request) { + // TODO: Should these be looking at all the headers marked as If-Match/If-None-Match? + + final String method = request.getRequestLine().getMethod(); + if (!(HeaderConstants.PUT_METHOD.equals(method) || HeaderConstants.DELETE_METHOD + .equals(method))) { + return null; + } + + final Header ifMatch = request.getFirstHeader(HeaderConstants.IF_MATCH); + if (ifMatch != null) { + final String val = ifMatch.getValue(); + if (val.startsWith("W/")) { + return RequestProtocolError.WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR; + } + } else { + final Header ifNoneMatch = request.getFirstHeader(HeaderConstants.IF_NONE_MATCH); + if (ifNoneMatch == null) { + return null; + } + + final String val2 = ifNoneMatch.getValue(); + if (val2.startsWith("W/")) { + return RequestProtocolError.WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR; + } + } + + return null; + } + + private RequestProtocolError requestContainsNoCacheDirectiveWithFieldName(final HttpRequest request) { + for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) { + for(final HeaderElement elt : h.getElements()) { + if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(elt.getName()) + && elt.getValue() != null) { + return RequestProtocolError.NO_CACHE_DIRECTIVE_WITH_FIELD_NAME; + } + } + } + return null; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolError.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolError.java new file mode 100644 index 000000000..1cc5668c7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/RequestProtocolError.java @@ -0,0 +1,40 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +/** + * @since 4.1 + */ +enum RequestProtocolError { + + UNKNOWN, + BODY_BUT_NO_LENGTH_ERROR, + WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR, + WEAK_ETAG_AND_RANGE_ERROR, + NO_CACHE_DIRECTIVE_WITH_FIELD_NAME + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResourceReference.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResourceReference.java new file mode 100644 index 000000000..5e3f368f1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResourceReference.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.util.Args; + +@Immutable +class ResourceReference extends PhantomReference { + + private final Resource resource; + + public ResourceReference(final HttpCacheEntry entry, final ReferenceQueue q) { + super(entry, q); + Args.notNull(entry.getResource(), "Resource"); + this.resource = entry.getResource(); + } + + public Resource getResource() { + return this.resource; + } + + @Override + public int hashCode() { + return this.resource.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + return this.resource.equals(obj); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseCachingPolicy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseCachingPolicy.java new file mode 100644 index 000000000..abadb88d4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseCachingPolicy.java @@ -0,0 +1,311 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * Determines if an HttpResponse can be cached. + * + * @since 4.1 + */ +@Immutable +class ResponseCachingPolicy { + + private static final String[] AUTH_CACHEABLE_PARAMS = { + "s-maxage", HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE, HeaderConstants.PUBLIC + }; + private final long maxObjectSizeBytes; + private final boolean sharedCache; + private final boolean neverCache1_0ResponsesWithQueryString; + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + private static final Set cacheableStatuses = + new HashSet(Arrays.asList(HttpStatus.SC_OK, + HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, + HttpStatus.SC_MULTIPLE_CHOICES, + HttpStatus.SC_MOVED_PERMANENTLY, + HttpStatus.SC_GONE)); + private final Set uncacheableStatuses; + /** + * Define a cache policy that limits the size of things that should be stored + * in the cache to a maximum of {@link HttpResponse} bytes in size. + * + * @param maxObjectSizeBytes the size to limit items into the cache + * @param sharedCache whether to behave as a shared cache (true) or a + * non-shared/private cache (false) + * @param neverCache1_0ResponsesWithQueryString true to never cache HTTP 1.0 responses with a query string, false + * to cache if explicit cache headers are found. + * @param allow303Caching if this policy is permitted to cache 303 response + */ + public ResponseCachingPolicy(final long maxObjectSizeBytes, + final boolean sharedCache, + final boolean neverCache1_0ResponsesWithQueryString, + final boolean allow303Caching) { + this.maxObjectSizeBytes = maxObjectSizeBytes; + this.sharedCache = sharedCache; + this.neverCache1_0ResponsesWithQueryString = neverCache1_0ResponsesWithQueryString; + if (allow303Caching) { + uncacheableStatuses = new HashSet( + Arrays.asList(HttpStatus.SC_PARTIAL_CONTENT)); + } else { + uncacheableStatuses = new HashSet(Arrays.asList( + HttpStatus.SC_PARTIAL_CONTENT, HttpStatus.SC_SEE_OTHER)); + } + } + + /** + * Determines if an HttpResponse can be cached. + * + * @param httpMethod What type of request was this, a GET, PUT, other? + * @param response The origin response + * @return true if response is cacheable + */ + public boolean isResponseCacheable(final String httpMethod, final HttpResponse response) { + boolean cacheable = false; + + if (!HeaderConstants.GET_METHOD.equals(httpMethod)) { + log.debug("Response was not cacheable."); + return false; + } + + final int status = response.getStatusLine().getStatusCode(); + if (cacheableStatuses.contains(status)) { + // these response codes MAY be cached + cacheable = true; + } else if (uncacheableStatuses.contains(status)) { + return false; + } else if (unknownStatusCode(status)) { + // a response with an unknown status code MUST NOT be + // cached + return false; + } + + final Header contentLength = response.getFirstHeader(HTTP.CONTENT_LEN); + if (contentLength != null) { + final int contentLengthValue = Integer.parseInt(contentLength.getValue()); + if (contentLengthValue > this.maxObjectSizeBytes) { + return false; + } + } + + final Header[] ageHeaders = response.getHeaders(HeaderConstants.AGE); + + if (ageHeaders.length > 1) { + return false; + } + + final Header[] expiresHeaders = response.getHeaders(HeaderConstants.EXPIRES); + + if (expiresHeaders.length > 1) { + return false; + } + + final Header[] dateHeaders = response.getHeaders(HTTP.DATE_HEADER); + + if (dateHeaders.length != 1) { + return false; + } + + final Date date = DateUtils.parseDate(dateHeaders[0].getValue()); + if (date == null) { + return false; + } + + for (final Header varyHdr : response.getHeaders(HeaderConstants.VARY)) { + for (final HeaderElement elem : varyHdr.getElements()) { + if ("*".equals(elem.getName())) { + return false; + } + } + } + + if (isExplicitlyNonCacheable(response)) { + return false; + } + + return (cacheable || isExplicitlyCacheable(response)); + } + + private boolean unknownStatusCode(final int status) { + if (status >= 100 && status <= 101) { + return false; + } + if (status >= 200 && status <= 206) { + return false; + } + if (status >= 300 && status <= 307) { + return false; + } + if (status >= 400 && status <= 417) { + return false; + } + if (status >= 500 && status <= 505) { + return false; + } + return true; + } + + protected boolean isExplicitlyNonCacheable(final HttpResponse response) { + final Header[] cacheControlHeaders = response.getHeaders(HeaderConstants.CACHE_CONTROL); + for (final Header header : cacheControlHeaders) { + for (final HeaderElement elem : header.getElements()) { + if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elem.getName()) + || HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elem.getName()) + || (sharedCache && HeaderConstants.PRIVATE.equals(elem.getName()))) { + return true; + } + } + } + return false; + } + + protected boolean hasCacheControlParameterFrom(final HttpMessage msg, final String[] params) { + final Header[] cacheControlHeaders = msg.getHeaders(HeaderConstants.CACHE_CONTROL); + for (final Header header : cacheControlHeaders) { + for (final HeaderElement elem : header.getElements()) { + for (final String param : params) { + if (param.equalsIgnoreCase(elem.getName())) { + return true; + } + } + } + } + return false; + } + + protected boolean isExplicitlyCacheable(final HttpResponse response) { + if (response.getFirstHeader(HeaderConstants.EXPIRES) != null) { + return true; + } + final String[] cacheableParams = { HeaderConstants.CACHE_CONTROL_MAX_AGE, "s-maxage", + HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE, + HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE, + HeaderConstants.PUBLIC + }; + return hasCacheControlParameterFrom(response, cacheableParams); + } + + /** + * Determine if the {@link HttpResponse} gotten from the origin is a + * cacheable response. + * + * @param request the {@link HttpRequest} that generated an origin hit + * @param response the {@link HttpResponse} from the origin + * @return true if response is cacheable + */ + public boolean isResponseCacheable(final HttpRequest request, final HttpResponse response) { + if (requestProtocolGreaterThanAccepted(request)) { + log.debug("Response was not cacheable."); + return false; + } + + final String[] uncacheableRequestDirectives = { HeaderConstants.CACHE_CONTROL_NO_STORE }; + if (hasCacheControlParameterFrom(request,uncacheableRequestDirectives)) { + return false; + } + + if (request.getRequestLine().getUri().contains("?")) { + if (neverCache1_0ResponsesWithQueryString && from1_0Origin(response)) { + log.debug("Response was not cacheable as it had a query string."); + return false; + } else if (!isExplicitlyCacheable(response)) { + log.debug("Response was not cacheable as it is missing explicit caching headers."); + return false; + } + } + + if (expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(response)) { + return false; + } + + if (sharedCache) { + final Header[] authNHeaders = request.getHeaders(HeaderConstants.AUTHORIZATION); + if (authNHeaders != null && authNHeaders.length > 0 + && !hasCacheControlParameterFrom(response, AUTH_CACHEABLE_PARAMS)) { + return false; + } + } + + final String method = request.getRequestLine().getMethod(); + return isResponseCacheable(method, response); + } + + private boolean expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl( + final HttpResponse response) { + if (response.getFirstHeader(HeaderConstants.CACHE_CONTROL) != null) { + return false; + } + final Header expiresHdr = response.getFirstHeader(HeaderConstants.EXPIRES); + final Header dateHdr = response.getFirstHeader(HTTP.DATE_HEADER); + if (expiresHdr == null || dateHdr == null) { + return false; + } + final Date expires = DateUtils.parseDate(expiresHdr.getValue()); + final Date date = DateUtils.parseDate(dateHdr.getValue()); + if (expires == null || date == null) { + return false; + } + return expires.equals(date) || expires.before(date); + } + + private boolean from1_0Origin(final HttpResponse response) { + final Header via = response.getFirstHeader(HeaderConstants.VIA); + if (via != null) { + for(final HeaderElement elt : via.getElements()) { + final String proto = elt.toString().split("\\s")[0]; + if (proto.contains("/")) { + return proto.equals("HTTP/1.0"); + } else { + return proto.equals("1.0"); + } + } + } + return HttpVersion.HTTP_1_0.equals(response.getProtocolVersion()); + } + + private boolean requestProtocolGreaterThanAccepted(final HttpRequest req) { + return req.getProtocolVersion().compareToVersion(HttpVersion.HTTP_1_1) > 0; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProtocolCompliance.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProtocolCompliance.java new file mode 100644 index 000000000..7f5ebb483 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProtocolCompliance.java @@ -0,0 +1,251 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.cache.HeaderConstants; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.message.BasicHeader; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * @since 4.1 + */ +@Immutable +class ResponseProtocolCompliance { + + private static final String UNEXPECTED_100_CONTINUE = "The incoming request did not contain a " + + "100-continue header, but the response was a Status 100, continue."; + private static final String UNEXPECTED_PARTIAL_CONTENT = "partial content was returned for a request that did not ask for it"; + + /** + * When we get a response from a down stream server (Origin Server) + * we attempt to see if it is HTTP 1.1 Compliant and if not, attempt to + * make it so. + * + * @param request The {@link HttpRequest} that generated an origin hit and response + * @param response The {@link HttpResponse} from the origin server + * @throws IOException Bad things happened + */ + public void ensureProtocolCompliance(final HttpRequestWrapper request, final HttpResponse response) + throws IOException { + if (backendResponseMustNotHaveBody(request, response)) { + consumeBody(response); + response.setEntity(null); + } + + requestDidNotExpect100ContinueButResponseIsOne(request, response); + + transferEncodingIsNotReturnedTo1_0Client(request, response); + + ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(request, response); + + ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(request, response); + + ensure206ContainsDateHeader(response); + + ensure304DoesNotContainExtraEntityHeaders(response); + + identityIsNotUsedInContentEncoding(response); + + warningsWithNonMatchingWarnDatesAreRemoved(response); + } + + private void consumeBody(final HttpResponse response) throws IOException { + final HttpEntity body = response.getEntity(); + if (body != null) { + IOUtils.consume(body); + } + } + + private void warningsWithNonMatchingWarnDatesAreRemoved( + final HttpResponse response) { + final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HTTP.DATE_HEADER).getValue()); + if (responseDate == null) { + return; + } + + final Header[] warningHeaders = response.getHeaders(HeaderConstants.WARNING); + + if (warningHeaders == null || warningHeaders.length == 0) { + return; + } + + final List
    newWarningHeaders = new ArrayList
    (); + boolean modified = false; + for(final Header h : warningHeaders) { + for(final WarningValue wv : WarningValue.getWarningValues(h)) { + final Date warnDate = wv.getWarnDate(); + if (warnDate == null || warnDate.equals(responseDate)) { + newWarningHeaders.add(new BasicHeader(HeaderConstants.WARNING,wv.toString())); + } else { + modified = true; + } + } + } + if (modified) { + response.removeHeaders(HeaderConstants.WARNING); + for(final Header h : newWarningHeaders) { + response.addHeader(h); + } + } + } + + private void identityIsNotUsedInContentEncoding(final HttpResponse response) { + final Header[] hdrs = response.getHeaders(HTTP.CONTENT_ENCODING); + if (hdrs == null || hdrs.length == 0) { + return; + } + final List
    newHeaders = new ArrayList
    (); + boolean modified = false; + for (final Header h : hdrs) { + final StringBuilder buf = new StringBuilder(); + boolean first = true; + for (final HeaderElement elt : h.getElements()) { + if ("identity".equalsIgnoreCase(elt.getName())) { + modified = true; + } else { + if (!first) { + buf.append(","); + } + buf.append(elt.toString()); + first = false; + } + } + final String newHeaderValue = buf.toString(); + if (!"".equals(newHeaderValue)) { + newHeaders.add(new BasicHeader(HTTP.CONTENT_ENCODING, newHeaderValue)); + } + } + if (!modified) { + return; + } + response.removeHeaders(HTTP.CONTENT_ENCODING); + for (final Header h : newHeaders) { + response.addHeader(h); + } + } + + private void ensure206ContainsDateHeader(final HttpResponse response) { + if (response.getFirstHeader(HTTP.DATE_HEADER) == null) { + response.addHeader(HTTP.DATE_HEADER, DateUtils.formatDate(new Date())); + } + + } + + private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request, + final HttpResponse response) throws IOException { + if (request.getFirstHeader(HeaderConstants.RANGE) != null + || response.getStatusLine().getStatusCode() != HttpStatus.SC_PARTIAL_CONTENT) { + return; + } + + consumeBody(response); + throw new ClientProtocolException(UNEXPECTED_PARTIAL_CONTENT); + } + + private void ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(final HttpRequest request, + final HttpResponse response) { + if (!request.getRequestLine().getMethod().equalsIgnoreCase(HeaderConstants.OPTIONS_METHOD)) { + return; + } + + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + return; + } + + if (response.getFirstHeader(HTTP.CONTENT_LEN) == null) { + response.addHeader(HTTP.CONTENT_LEN, "0"); + } + } + + private void ensure304DoesNotContainExtraEntityHeaders(final HttpResponse response) { + final String[] disallowedEntityHeaders = { HeaderConstants.ALLOW, HTTP.CONTENT_ENCODING, + "Content-Language", HTTP.CONTENT_LEN, "Content-MD5", + "Content-Range", HTTP.CONTENT_TYPE, HeaderConstants.LAST_MODIFIED + }; + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { + for(final String hdr : disallowedEntityHeaders) { + response.removeHeaders(hdr); + } + } + } + + private boolean backendResponseMustNotHaveBody(final HttpRequest request, final HttpResponse backendResponse) { + return HeaderConstants.HEAD_METHOD.equals(request.getRequestLine().getMethod()) + || backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT + || backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_RESET_CONTENT + || backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED; + } + + private void requestDidNotExpect100ContinueButResponseIsOne(final HttpRequestWrapper request, + final HttpResponse response) throws IOException { + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) { + return; + } + + final HttpRequest originalRequest = request.getOriginal(); + if (originalRequest instanceof HttpEntityEnclosingRequest) { + if (((HttpEntityEnclosingRequest)originalRequest).expectContinue()) { + return; + } + } + consumeBody(response); + throw new ClientProtocolException(UNEXPECTED_100_CONTINUE); + } + + private void transferEncodingIsNotReturnedTo1_0Client(final HttpRequestWrapper request, + final HttpResponse response) { + final HttpRequest originalRequest = request.getOriginal(); + if (originalRequest.getProtocolVersion().compareToVersion(HttpVersion.HTTP_1_1) >= 0) { + return; + } + + removeResponseTransferEncoding(response); + } + + private void removeResponseTransferEncoding(final HttpResponse response) { + response.removeHeaders("TE"); + response.removeHeaders(HTTP.TRANSFER_ENCODING); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProxyHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProxyHandler.java new file mode 100644 index 000000000..a541b2778 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/ResponseProxyHandler.java @@ -0,0 +1,88 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * A proxy class that can enhance an arbitrary {@link HttpResponse} with + * {@link Closeable#close()} method. + * + * @since 4.3 + */ +@NotThreadSafe +class ResponseProxyHandler implements InvocationHandler { + + private static final Method CLOSE_METHOD; + + static { + try { + CLOSE_METHOD = Closeable.class.getMethod("close"); + } catch (final NoSuchMethodException ex) { + throw new Error(ex); + } + } + + private final HttpResponse original; + + ResponseProxyHandler(final HttpResponse original) { + super(); + this.original = original; + } + + public void close() throws IOException { + IOUtils.consume(original.getEntity()); + } + + public Object invoke( + final Object proxy, final Method method, final Object[] args) throws Throwable { + if (method.equals(CLOSE_METHOD)) { + close(); + return null; + } else { + try { + return method.invoke(this.original, args); + } catch (final InvocationTargetException ex) { + final Throwable cause = ex.getCause(); + if (cause != null) { + throw cause; + } else { + throw ex; + } + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SchedulingStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SchedulingStrategy.java new file mode 100644 index 000000000..0940fbc83 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SchedulingStrategy.java @@ -0,0 +1,45 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.Closeable; + +/** + * Specifies when revalidation requests are scheduled. + * + * @since 4.3 + */ +public interface SchedulingStrategy extends Closeable +{ + /** + * Schedule an {@link AsynchronousValidationRequest} to be executed. + * + * @param revalidationRequest the request to be executed; not null + * @throws java.util.concurrent.RejectedExecutionException if the request could not be scheduled for execution + */ + void schedule(AsynchronousValidationRequest revalidationRequest); +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SizeLimitedResponseReader.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SizeLimitedResponseReader.java new file mode 100644 index 000000000..4c36ebb48 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/SizeLimitedResponseReader.java @@ -0,0 +1,150 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Proxy; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.cache.InputLimit; +import ch.boye.httpclientandroidlib.client.cache.Resource; +import ch.boye.httpclientandroidlib.client.cache.ResourceFactory; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.message.BasicHttpResponse; + +/** + * @since 4.1 + */ +@NotThreadSafe +class SizeLimitedResponseReader { + + private final ResourceFactory resourceFactory; + private final long maxResponseSizeBytes; + private final HttpRequest request; + private final CloseableHttpResponse response; + + private InputStream instream; + private InputLimit limit; + private Resource resource; + private boolean consumed; + + /** + * Create an {@link HttpResponse} that is limited in size, this allows for checking + * the size of objects that will be stored in the cache. + */ + public SizeLimitedResponseReader( + final ResourceFactory resourceFactory, + final long maxResponseSizeBytes, + final HttpRequest request, + final CloseableHttpResponse response) { + super(); + this.resourceFactory = resourceFactory; + this.maxResponseSizeBytes = maxResponseSizeBytes; + this.request = request; + this.response = response; + } + + protected void readResponse() throws IOException { + if (!consumed) { + doConsume(); + } + } + + private void ensureNotConsumed() { + if (consumed) { + throw new IllegalStateException("Response has already been consumed"); + } + } + + private void ensureConsumed() { + if (!consumed) { + throw new IllegalStateException("Response has not been consumed"); + } + } + + private void doConsume() throws IOException { + ensureNotConsumed(); + consumed = true; + + limit = new InputLimit(maxResponseSizeBytes); + + final HttpEntity entity = response.getEntity(); + if (entity == null) { + return; + } + final String uri = request.getRequestLine().getUri(); + instream = entity.getContent(); + try { + resource = resourceFactory.generate(uri, instream, limit); + } finally { + if (!limit.isReached()) { + instream.close(); + } + } + } + + boolean isLimitReached() { + ensureConsumed(); + return limit.isReached(); + } + + Resource getResource() { + ensureConsumed(); + return resource; + } + + CloseableHttpResponse getReconstructedResponse() throws IOException { + ensureConsumed(); + final HttpResponse reconstructed = new BasicHttpResponse(response.getStatusLine()); + reconstructed.setHeaders(response.getAllHeaders()); + + final CombinedEntity combinedEntity = new CombinedEntity(resource, instream); + final HttpEntity entity = response.getEntity(); + if (entity != null) { + combinedEntity.setContentType(entity.getContentType()); + combinedEntity.setContentEncoding(entity.getContentEncoding()); + combinedEntity.setChunked(entity.isChunked()); + } + reconstructed.setEntity(combinedEntity); + return (CloseableHttpResponse) Proxy.newProxyInstance( + ResponseProxyHandler.class.getClassLoader(), + new Class[] { CloseableHttpResponse.class }, + new ResponseProxyHandler(reconstructed) { + + @Override + public void close() throws IOException { + response.close(); + } + + }); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Variant.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Variant.java new file mode 100644 index 000000000..f631b0211 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/Variant.java @@ -0,0 +1,55 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry; + +/** Records a set of information describing a cached variant. */ +class Variant { + + private final String variantKey; + private final String cacheKey; + private final HttpCacheEntry entry; + + public Variant(final String variantKey, final String cacheKey, final HttpCacheEntry entry) { + this.variantKey = variantKey; + this.cacheKey = cacheKey; + this.entry = entry; + } + + public String getVariantKey() { + return variantKey; + } + + public String getCacheKey() { + return cacheKey; + } + + public HttpCacheEntry getEntry() { + return entry; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/WarningValue.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/WarningValue.java new file mode 100644 index 000000000..a3c2ce67d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/WarningValue.java @@ -0,0 +1,370 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.client.cache; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; + +/** This class provides for parsing and understanding Warning headers. As + * the Warning header can be multi-valued, but the values can contain + * separators like commas inside quoted strings, we cannot use the regular + * {@link Header#getElements()} call to access the values. + */ +class WarningValue { + + private int offs; + private int init_offs; + private final String src; + private int warnCode; + private String warnAgent; + private String warnText; + private Date warnDate; + + WarningValue(final String s) { + this(s, 0); + } + + WarningValue(final String s, final int offs) { + this.offs = this.init_offs = offs; + this.src = s; + consumeWarnValue(); + } + + /** Returns an array of the parseable warning values contained + * in the given header value, which is assumed to be a + * Warning header. Improperly formatted warning values will be + * skipped, in keeping with the philosophy of "ignore what you + * cannot understand." + * @param h Warning {@link Header} to parse + * @return array of WarnValue objects + */ + public static WarningValue[] getWarningValues(final Header h) { + final List out = new ArrayList(); + final String src = h.getValue(); + int offs = 0; + while(offs < src.length()) { + try { + final WarningValue wv = new WarningValue(src, offs); + out.add(wv); + offs = wv.offs; + } catch (final IllegalArgumentException e) { + final int nextComma = src.indexOf(',', offs); + if (nextComma == -1) { + break; + } + offs = nextComma + 1; + } + } + final WarningValue[] wvs = {}; + return out.toArray(wvs); + } + + /* + * LWS = [CRLF] 1*( SP | HT ) + * CRLF = CR LF + */ + protected void consumeLinearWhitespace() { + while(offs < src.length()) { + switch(src.charAt(offs)) { + case '\r': + if (offs+2 >= src.length() + || src.charAt(offs+1) != '\n' + || (src.charAt(offs+2) != ' ' + && src.charAt(offs+2) != '\t')) { + return; + } + offs += 2; + break; + case ' ': + case '\t': + break; + default: + return; + } + offs++; + } + } + + /* + * CHAR = + */ + private boolean isChar(final char c) { + final int i = c; + return (i >= 0 && i <= 127); + } + + /* + * CTL = + */ + private boolean isControl(final char c) { + final int i = c; + return (i == 127 || (i >=0 && i <= 31)); + } + + /* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ + private boolean isSeparator(final char c) { + return (c == '(' || c == ')' || c == '<' || c == '>' + || c == '@' || c == ',' || c == ';' || c == ':' + || c == '\\' || c == '\"' || c == '/' + || c == '[' || c == ']' || c == '?' || c == '=' + || c == '{' || c == '}' || c == ' ' || c == '\t'); + } + + /* + * token = 1* + */ + protected void consumeToken() { + if (!isTokenChar(src.charAt(offs))) { + parseError(); + } + while(offs < src.length()) { + if (!isTokenChar(src.charAt(offs))) { + break; + } + offs++; + } + } + + private boolean isTokenChar(final char c) { + return (isChar(c) && !isControl(c) && !isSeparator(c)); + } + + private static final String TOPLABEL = "\\p{Alpha}([\\p{Alnum}-]*\\p{Alnum})?"; + private static final String DOMAINLABEL = "\\p{Alnum}([\\p{Alnum}-]*\\p{Alnum})?"; + private static final String HOSTNAME = "(" + DOMAINLABEL + "\\.)*" + TOPLABEL + "\\.?"; + private static final String IPV4ADDRESS = "\\d+\\.\\d+\\.\\d+\\.\\d+"; + private static final String HOST = "(" + HOSTNAME + ")|(" + IPV4ADDRESS + ")"; + private static final String PORT = "\\d*"; + private static final String HOSTPORT = "(" + HOST + ")(\\:" + PORT + ")?"; + private static final Pattern HOSTPORT_PATTERN = Pattern.compile(HOSTPORT); + + protected void consumeHostPort() { + final Matcher m = HOSTPORT_PATTERN.matcher(src.substring(offs)); + if (!m.find()) { + parseError(); + } + if (m.start() != 0) { + parseError(); + } + offs += m.end(); + } + + + /* + * warn-agent = ( host [ ":" port ] ) | pseudonym + * pseudonym = token + */ + protected void consumeWarnAgent() { + final int curr_offs = offs; + try { + consumeHostPort(); + warnAgent = src.substring(curr_offs, offs); + consumeCharacter(' '); + return; + } catch (final IllegalArgumentException e) { + offs = curr_offs; + } + consumeToken(); + warnAgent = src.substring(curr_offs, offs); + consumeCharacter(' '); + } + + /* + * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) + * qdtext = > + */ + protected void consumeQuotedString() { + if (src.charAt(offs) != '\"') { + parseError(); + } + offs++; + boolean foundEnd = false; + while(offs < src.length() && !foundEnd) { + final char c = src.charAt(offs); + if (offs + 1 < src.length() && c == '\\' + && isChar(src.charAt(offs+1))) { + offs += 2; // consume quoted-pair + } else if (c == '\"') { + foundEnd = true; + offs++; + } else if (c != '\"' && !isControl(c)) { + offs++; + } else { + parseError(); + } + } + if (!foundEnd) { + parseError(); + } + } + + /* + * warn-text = quoted-string + */ + protected void consumeWarnText() { + final int curr = offs; + consumeQuotedString(); + warnText = src.substring(curr, offs); + } + + private static final String MONTH = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"; + private static final String WEEKDAY = "Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday"; + private static final String WKDAY = "Mon|Tue|Wed|Thu|Fri|Sat|Sun"; + private static final String TIME = "\\d{2}:\\d{2}:\\d{2}"; + private static final String DATE3 = "(" + MONTH + ") ( |\\d)\\d"; + private static final String DATE2 = "\\d{2}-(" + MONTH + ")-\\d{2}"; + private static final String DATE1 = "\\d{2} (" + MONTH + ") \\d{4}"; + private static final String ASCTIME_DATE = "(" + WKDAY + ") (" + DATE3 + ") (" + TIME + ") \\d{4}"; + private static final String RFC850_DATE = "(" + WEEKDAY + "), (" + DATE2 + ") (" + TIME + ") GMT"; + private static final String RFC1123_DATE = "(" + WKDAY + "), (" + DATE1 + ") (" + TIME + ") GMT"; + private static final String HTTP_DATE = "(" + RFC1123_DATE + ")|(" + RFC850_DATE + ")|(" + ASCTIME_DATE + ")"; + private static final String WARN_DATE = "\"(" + HTTP_DATE + ")\""; + private static final Pattern WARN_DATE_PATTERN = Pattern.compile(WARN_DATE); + + /* + * warn-date = <"> HTTP-date <"> + */ + protected void consumeWarnDate() { + final int curr = offs; + final Matcher m = WARN_DATE_PATTERN.matcher(src.substring(offs)); + if (!m.lookingAt()) { + parseError(); + } + offs += m.end(); + warnDate = DateUtils.parseDate(src.substring(curr+1,offs-1)); + } + + /* + * warning-value = warn-code SP warn-agent SP warn-text [SP warn-date] + */ + protected void consumeWarnValue() { + consumeLinearWhitespace(); + consumeWarnCode(); + consumeWarnAgent(); + consumeWarnText(); + if (offs + 1 < src.length() && src.charAt(offs) == ' ' && src.charAt(offs+1) == '\"') { + consumeCharacter(' '); + consumeWarnDate(); + } + consumeLinearWhitespace(); + if (offs != src.length()) { + consumeCharacter(','); + } + } + + protected void consumeCharacter(final char c) { + if (offs + 1 > src.length() + || c != src.charAt(offs)) { + parseError(); + } + offs++; + } + + /* + * warn-code = 3DIGIT + */ + protected void consumeWarnCode() { + if (offs + 4 > src.length() + || !Character.isDigit(src.charAt(offs)) + || !Character.isDigit(src.charAt(offs + 1)) + || !Character.isDigit(src.charAt(offs + 2)) + || src.charAt(offs + 3) != ' ') { + parseError(); + } + warnCode = Integer.parseInt(src.substring(offs,offs+3)); + offs += 4; + } + + private void parseError() { + final String s = src.substring(init_offs); + throw new IllegalArgumentException("Bad warn code \"" + s + "\""); + } + + /** Returns the 3-digit code associated with this warning. + * @return int + */ + public int getWarnCode() { return warnCode; } + + /** Returns the "warn-agent" string associated with this warning, + * which is either the name or pseudonym of the server that added + * this particular Warning header. + * @return {@link String} + */ + public String getWarnAgent() { return warnAgent; } + + /** Returns the human-readable warning text for this warning. Note + * that the original quoted-string is returned here, including + * escaping for any contained characters. In other words, if the + * header was: + *
    +     *   Warning: 110 fred "Response is stale"
    +     * 
    + * then this method will return "\"Response is stale\"" + * (surrounding quotes included). + * @return {@link String} + */ + public String getWarnText() { return warnText; } + + /** Returns the date and time when this warning was added, or + * null if a warning date was not supplied in the + * header. + * @return {@link Date} + */ + public Date getWarnDate() { return warnDate; } + + /** Formats a WarningValue as a {@link String} + * suitable for including in a header. For example, you can: + *
    +     *   WarningValue wv = ...;
    +     *   HttpResponse resp = ...;
    +     *   resp.addHeader("Warning", wv.toString());
    +     * 
    + * @return {@link String} + */ + @Override + public String toString() { + if (warnDate != null) { + return String.format("%d %s %s \"%s\"", warnCode, + warnAgent, warnText, DateUtils.formatDate(warnDate)); + } else { + return String.format("%d %s %s", warnCode, warnAgent, warnText); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/package.html b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/package.html new file mode 100644 index 000000000..4c208e7a9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/package.html @@ -0,0 +1,42 @@ + + + + + + + +

    +This package contains a cache module that can be used for HTTP/1.1 +client-side caching. The primary classes in this package are the +{@link org.apache.http.impl.client.cache.CachingHttpClient}, +which is a drop-in replacement for +a {@link org.apache.http.impl.client.DefaultHttpClient} that adds +caching, and the {@link org.apache.http.impl.client.cache.CacheConfig} +class that can be used for configuring it. +

    + + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/package-info.java new file mode 100644 index 000000000..1efbf4cec --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/package-info.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default HTTP client implementation. + *

    + * The usual execution flow can be demonstrated by the code snippet below: + *

    + * CloseableHttpClient httpclient = HttpClients.createDefault();
    + * try {
    + *      HttpGet httpGet = new HttpGet("http://targethost/homepage");
    + *      CloseableHttpResponse response = httpclient.execute(httpGet);
    + *      try {
    + *          System.out.println(response.getStatusLine());
    + *          HttpEntity entity = response.getEntity();
    + *          // do something useful with the response body
    + *          // and ensure it is fully consumed
    + *          EntityUtils.consume(entity);
    + *      } finally {
    + *          response.close();
    + *      }
    + * } finally {
    + *      httpclient.close();
    + * }
    + * 
    + */ +package ch.boye.httpclientandroidlib.impl.client; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java new file mode 100644 index 000000000..ea2524f6a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java @@ -0,0 +1,369 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Abstract adapter from {@link OperatedClientConnection operated} to + * {@link ManagedClientConnection managed} client connections. + * Read and write methods are delegated to the wrapped connection. + * Operations affecting the connection state have to be implemented + * by derived classes. Operations for querying the connection state + * are delegated to the wrapped connection if there is one, or + * return a default value if there is none. + *

    + * This adapter tracks the checkpoints for reusable communication states, + * as indicated by {@link #markReusable markReusable} and queried by + * {@link #isMarkedReusable isMarkedReusable}. + * All send and receive operations will automatically clear the mark. + *

    + * Connection release calls are delegated to the connection manager, + * if there is one. {@link #abortConnection abortConnection} will + * clear the reusability mark first. The connection manager is + * expected to tolerate multiple calls to the release method. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +@NotThreadSafe +public abstract class AbstractClientConnAdapter implements ManagedClientConnection, HttpContext { + + /** + * The connection manager. + */ + private final ClientConnectionManager connManager; + + /** The wrapped connection. */ + private volatile OperatedClientConnection wrappedConnection; + + /** The reusability marker. */ + private volatile boolean markedReusable; + + /** True if the connection has been shut down or released. */ + private volatile boolean released; + + /** The duration this is valid for while idle (in ms). */ + private volatile long duration; + + /** + * Creates a new connection adapter. + * The adapter is initially not + * {@link #isMarkedReusable marked} as reusable. + * + * @param mgr the connection manager, or null + * @param conn the connection to wrap, or null + */ + protected AbstractClientConnAdapter(final ClientConnectionManager mgr, + final OperatedClientConnection conn) { + super(); + connManager = mgr; + wrappedConnection = conn; + markedReusable = false; + released = false; + duration = Long.MAX_VALUE; + } + + /** + * Detaches this adapter from the wrapped connection. + * This adapter becomes useless. + */ + protected synchronized void detach() { + wrappedConnection = null; + duration = Long.MAX_VALUE; + } + + protected OperatedClientConnection getWrappedConnection() { + return wrappedConnection; + } + + protected ClientConnectionManager getManager() { + return connManager; + } + + /** + * @deprecated (4.1) use {@link #assertValid(OperatedClientConnection)} + */ + @Deprecated + protected final void assertNotAborted() throws InterruptedIOException { + if (isReleased()) { + throw new InterruptedIOException("Connection has been shut down"); + } + } + + /** + * @since 4.1 + * @return value of released flag + */ + protected boolean isReleased() { + return released; + } + + /** + * Asserts that there is a valid wrapped connection to delegate to. + * + * @throws ConnectionShutdownException if there is no wrapped connection + * or connection has been aborted + */ + protected final void assertValid( + final OperatedClientConnection wrappedConn) throws ConnectionShutdownException { + if (isReleased() || wrappedConn == null) { + throw new ConnectionShutdownException(); + } + } + + public boolean isOpen() { + final OperatedClientConnection conn = getWrappedConnection(); + if (conn == null) { + return false; + } + + return conn.isOpen(); + } + + public boolean isStale() { + if (isReleased()) { + return true; + } + final OperatedClientConnection conn = getWrappedConnection(); + if (conn == null) { + return true; + } + + return conn.isStale(); + } + + public void setSocketTimeout(final int timeout) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + conn.setSocketTimeout(timeout); + } + + public int getSocketTimeout() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getSocketTimeout(); + } + + public HttpConnectionMetrics getMetrics() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getMetrics(); + } + + public void flush() throws IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + conn.flush(); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.isResponseAvailable(timeout); + } + + public void receiveResponseEntity(final HttpResponse response) + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + conn.receiveResponseEntity(response); + } + + public HttpResponse receiveResponseHeader() + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + return conn.receiveResponseHeader(); + } + + public void sendRequestEntity(final HttpEntityEnclosingRequest request) + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + conn.sendRequestEntity(request); + } + + public void sendRequestHeader(final HttpRequest request) + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + conn.sendRequestHeader(request); + } + + public InetAddress getLocalAddress() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getLocalAddress(); + } + + public int getLocalPort() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getLocalPort(); + } + + public InetAddress getRemoteAddress() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getRemoteAddress(); + } + + public int getRemotePort() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getRemotePort(); + } + + public boolean isSecure() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.isSecure(); + } + + public void bind(final Socket socket) throws IOException { + throw new UnsupportedOperationException(); + } + + public Socket getSocket() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (!isOpen()) { + return null; + } + return conn.getSocket(); + } + + public SSLSession getSSLSession() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (!isOpen()) { + return null; + } + + SSLSession result = null; + final Socket sock = conn.getSocket(); + if (sock instanceof SSLSocket) { + result = ((SSLSocket)sock).getSession(); + } + return result; + } + + public void markReusable() { + markedReusable = true; + } + + public void unmarkReusable() { + markedReusable = false; + } + + public boolean isMarkedReusable() { + return markedReusable; + } + + public void setIdleDuration(final long duration, final TimeUnit unit) { + if(duration > 0) { + this.duration = unit.toMillis(duration); + } else { + this.duration = -1; + } + } + + public synchronized void releaseConnection() { + if (released) { + return; + } + released = true; + connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); + } + + public synchronized void abortConnection() { + if (released) { + return; + } + released = true; + unmarkReusable(); + try { + shutdown(); + } catch (final IOException ignore) { + } + connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); + } + + public Object getAttribute(final String id) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).getAttribute(id); + } else { + return null; + } + } + + public Object removeAttribute(final String id) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).removeAttribute(id); + } else { + return null; + } + } + + public void setAttribute(final String id, final Object obj) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (conn instanceof HttpContext) { + ((HttpContext) conn).setAttribute(id, obj); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPoolEntry.java new file mode 100644 index 000000000..cbbe727b5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPoolEntry.java @@ -0,0 +1,262 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A pool entry for use by connection manager implementations. + * Pool entries work in conjunction with an + * {@link AbstractClientConnAdapter adapter}. + * The adapter is handed out to applications that obtain a connection. + * The pool entry stores the underlying connection and tracks the + * {@link HttpRoute route} established. + * The adapter delegates methods for establishing the route to + * its pool entry. + *

    + * If the managed connections is released or revoked, the adapter + * gets disconnected, but the pool entry still contains the + * underlying connection and the established route. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public abstract class AbstractPoolEntry { + + /** The connection operator. */ + protected final ClientConnectionOperator connOperator; + + /** The underlying connection being pooled or used. */ + protected final OperatedClientConnection connection; + + /** The route for which this entry gets allocated. */ + //@@@ currently accessed from connection manager(s) as attribute + //@@@ avoid that, derived classes should decide whether update is allowed + //@@@ SCCM: yes, TSCCM: no + protected volatile HttpRoute route; + + /** Connection state object */ + protected volatile Object state; + + /** The tracked route, or null before tracking starts. */ + protected volatile RouteTracker tracker; + + + /** + * Creates a new pool entry. + * + * @param connOperator the Connection Operator for this entry + * @param route the planned route for the connection, + * or null + */ + protected AbstractPoolEntry(final ClientConnectionOperator connOperator, + final HttpRoute route) { + super(); + Args.notNull(connOperator, "Connection operator"); + this.connOperator = connOperator; + this.connection = connOperator.createConnection(); + this.route = route; + this.tracker = null; + } + + /** + * Returns the state object associated with this pool entry. + * + * @return The state object + */ + public Object getState() { + return state; + } + + /** + * Assigns a state object to this pool entry. + * + * @param state The state object + */ + public void setState(final Object state) { + this.state = state; + } + + /** + * Opens the underlying connection. + * + * @param route the route along which to open the connection + * @param context the context for opening the connection + * @param params the parameters for opening the connection + * + * @throws IOException in case of a problem + */ + public void open(final HttpRoute route, + final HttpContext context, final HttpParams params) + throws IOException { + + Args.notNull(route, "Route"); + Args.notNull(params, "HTTP parameters"); + if (this.tracker != null) { + Asserts.check(!this.tracker.isConnected(), "Connection already open"); + } + // - collect the arguments + // - call the operator + // - update the tracking data + // In this order, we can be sure that only a successful + // opening of the connection will be tracked. + + this.tracker = new RouteTracker(route); + final HttpHost proxy = route.getProxyHost(); + + connOperator.openConnection + (this.connection, + (proxy != null) ? proxy : route.getTargetHost(), + route.getLocalAddress(), + context, params); + + final RouteTracker localTracker = tracker; // capture volatile + + // If this tracker was reset while connecting, + // fail early. + if (localTracker == null) { + throw new InterruptedIOException("Request aborted"); + } + + if (proxy == null) { + localTracker.connectTarget(this.connection.isSecure()); + } else { + localTracker.connectProxy(proxy, this.connection.isSecure()); + } + + } + + /** + * Tracks tunnelling of the connection to the target. + * The tunnel has to be established outside by sending a CONNECT + * request to the (last) proxy. + * + * @param secure true if the tunnel should be + * considered secure, false otherwise + * @param params the parameters for tunnelling the connection + * + * @throws IOException in case of a problem + */ + public void tunnelTarget(final boolean secure, final HttpParams params) + throws IOException { + + Args.notNull(params, "HTTP parameters"); + Asserts.notNull(this.tracker, "Route tracker"); + Asserts.check(this.tracker.isConnected(), "Connection not open"); + Asserts.check(!this.tracker.isTunnelled(), "Connection is already tunnelled"); + + this.connection.update(null, tracker.getTargetHost(), + secure, params); + this.tracker.tunnelTarget(secure); + } + + /** + * Tracks tunnelling of the connection to a chained proxy. + * The tunnel has to be established outside by sending a CONNECT + * request to the previous proxy. + * + * @param next the proxy to which the tunnel was established. + * See {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection#tunnelProxy + * ManagedClientConnection.tunnelProxy} + * for details. + * @param secure true if the tunnel should be + * considered secure, false otherwise + * @param params the parameters for tunnelling the connection + * + * @throws IOException in case of a problem + */ + public void tunnelProxy(final HttpHost next, final boolean secure, final HttpParams params) + throws IOException { + + Args.notNull(next, "Next proxy"); + Args.notNull(params, "Parameters"); + + Asserts.notNull(this.tracker, "Route tracker"); + Asserts.check(this.tracker.isConnected(), "Connection not open"); + + this.connection.update(null, next, secure, params); + this.tracker.tunnelProxy(next, secure); + } + + /** + * Layers a protocol on top of an established tunnel. + * + * @param context the context for layering + * @param params the parameters for layering + * + * @throws IOException in case of a problem + */ + public void layerProtocol(final HttpContext context, final HttpParams params) + throws IOException { + + //@@@ is context allowed to be null? depends on operator? + Args.notNull(params, "HTTP parameters"); + Asserts.notNull(this.tracker, "Route tracker"); + Asserts.check(this.tracker.isConnected(), "Connection not open"); + Asserts.check(this.tracker.isTunnelled(), "Protocol layering without a tunnel not supported"); + Asserts.check(!this.tracker.isLayered(), "Multiple protocol layering not supported"); + // - collect the arguments + // - call the operator + // - update the tracking data + // In this order, we can be sure that only a successful + // layering on top of the connection will be tracked. + + final HttpHost target = tracker.getTargetHost(); + + connOperator.updateSecureConnection(this.connection, target, + context, params); + + this.tracker.layerProtocol(this.connection.isSecure()); + + } + + /** + * Shuts down the entry. + * + * If {@link #open(HttpRoute, HttpContext, HttpParams)} is in progress, + * this will cause that open to possibly throw an {@link IOException}. + */ + protected void shutdownEntry() { + tracker = null; + state = null; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java new file mode 100644 index 000000000..47f0feda5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java @@ -0,0 +1,191 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Abstract adapter from pool {@link AbstractPoolEntry entries} to + * {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection managed} + * client connections. + * The connection in the pool entry is used to initialize the base class. + * In addition, methods to establish a route are delegated to the + * pool entry. {@link #shutdown shutdown} and {@link #close close} + * will clear the tracked route in the pool entry and call the + * respective method of the wrapped connection. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public abstract class AbstractPooledConnAdapter extends AbstractClientConnAdapter { + + /** The wrapped pool entry. */ + protected volatile AbstractPoolEntry poolEntry; + + /** + * Creates a new connection adapter. + * + * @param manager the connection manager + * @param entry the pool entry for the connection being wrapped + */ + protected AbstractPooledConnAdapter(final ClientConnectionManager manager, + final AbstractPoolEntry entry) { + super(manager, entry.connection); + this.poolEntry = entry; + } + + public String getId() { + return null; + } + + /** + * Obtains the pool entry. + * + * @return the pool entry, or null if detached + * + * @deprecated (4.0.1) + */ + @Deprecated + protected AbstractPoolEntry getPoolEntry() { + return this.poolEntry; + } + + /** + * Asserts that there is a valid pool entry. + * + * @throws ConnectionShutdownException if there is no pool entry + * or connection has been aborted + * + * @see #assertValid(OperatedClientConnection) + */ + protected void assertValid(final AbstractPoolEntry entry) { + if (isReleased() || entry == null) { + throw new ConnectionShutdownException(); + } + } + + /** + * @deprecated (4.1) use {@link #assertValid(AbstractPoolEntry)} + */ + @Deprecated + protected final void assertAttached() { + if (poolEntry == null) { + throw new ConnectionShutdownException(); + } + } + + /** + * Detaches this adapter from the wrapped connection. + * This adapter becomes useless. + */ + @Override + protected synchronized void detach() { + poolEntry = null; + super.detach(); + } + + public HttpRoute getRoute() { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + return (entry.tracker == null) ? null : entry.tracker.toRoute(); + } + + public void open(final HttpRoute route, + final HttpContext context, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.open(route, context, params); + } + + public void tunnelTarget(final boolean secure, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.tunnelTarget(secure, params); + } + + public void tunnelProxy(final HttpHost next, final boolean secure, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.tunnelProxy(next, secure, params); + } + + public void layerProtocol(final HttpContext context, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.layerProtocol(context, params); + } + + public void close() throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + if (entry != null) { + entry.shutdownEntry(); + } + + final OperatedClientConnection conn = getWrappedConnection(); + if (conn != null) { + conn.close(); + } + } + + public void shutdown() throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + if (entry != null) { + entry.shutdownEntry(); + } + + final OperatedClientConnection conn = getWrappedConnection(); + if (conn != null) { + conn.shutdown(); + } + } + + public Object getState() { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + return entry.getState(); + } + + public void setState(final Object state) { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.setState(state); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicClientConnectionManager.java new file mode 100644 index 000000000..176e28150 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicClientConnectionManager.java @@ -0,0 +1,276 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A connection manager for a single connection. This connection manager maintains only one active + * connection. Even though this class is fully thread-safe it ought to be used by one execution + * thread only, as only one thread a time can lease the connection at a time. + *

    + * This connection manager will make an effort to reuse the connection for subsequent requests + * with the same {@link HttpRoute route}. It will, however, close the existing connection and + * open it for the given route, if the route of the persistent connection does not match that + * of the connection request. If the connection has been already been allocated + * {@link IllegalStateException} is thrown. + *

    + * This connection manager implementation should be used inside an EJB container instead of + * {@link PoolingClientConnectionManager}. + * + * @since 4.2 + * + * @deprecated (4.3) use {@link BasicHttpClientConnectionManager}. + */ +@ThreadSafe +@Deprecated +public class BasicClientConnectionManager implements ClientConnectionManager { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private static final AtomicLong COUNTER = new AtomicLong(); + + /** The message to be logged on multiple allocation. */ + public final static String MISUSE_MESSAGE = + "Invalid use of BasicClientConnManager: connection still allocated.\n" + + "Make sure to release the connection before allocating another one."; + + /** The schemes supported by this connection manager. */ + private final SchemeRegistry schemeRegistry; + + /** The operator for opening and updating connections. */ + private final ClientConnectionOperator connOperator; + + /** The one and only entry in this pool. */ + @GuardedBy("this") + private HttpPoolEntry poolEntry; + + /** The currently issued managed connection, if any. */ + @GuardedBy("this") + private ManagedClientConnectionImpl conn; + + /** Indicates whether this connection manager is shut down. */ + @GuardedBy("this") + private volatile boolean shutdown; + + /** + * Creates a new simple connection manager. + * + * @param schreg the scheme registry + */ + public BasicClientConnectionManager(final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + this.schemeRegistry = schreg; + this.connOperator = createConnectionOperator(schreg); + } + + public BasicClientConnectionManager() { + this(SchemeRegistryFactory.createDefault()); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { // Make sure we call overridden method even if shutdown barfs + super.finalize(); + } + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) { + return new DefaultClientConnectionOperator(schreg); + } + + public final ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + + return new ClientConnectionRequest() { + + public void abortRequest() { + // Nothing to abort, since requests are immediate. + } + + public ManagedClientConnection getConnection( + final long timeout, final TimeUnit tunit) { + return BasicClientConnectionManager.this.getConnection( + route, state); + } + + }; + } + + private void assertNotShutdown() { + Asserts.check(!this.shutdown, "Connection manager has been shut down"); + } + + ManagedClientConnection getConnection(final HttpRoute route, final Object state) { + Args.notNull(route, "Route"); + synchronized (this) { + assertNotShutdown(); + if (this.log.isDebugEnabled()) { + this.log.debug("Get connection for route " + route); + } + Asserts.check(this.conn == null, MISUSE_MESSAGE); + if (this.poolEntry != null && !this.poolEntry.getPlannedRoute().equals(route)) { + this.poolEntry.close(); + this.poolEntry = null; + } + if (this.poolEntry == null) { + final String id = Long.toString(COUNTER.getAndIncrement()); + final OperatedClientConnection conn = this.connOperator.createConnection(); + this.poolEntry = new HttpPoolEntry(this.log, id, route, conn, 0, TimeUnit.MILLISECONDS); + } + final long now = System.currentTimeMillis(); + if (this.poolEntry.isExpired(now)) { + this.poolEntry.close(); + this.poolEntry.getTracker().reset(); + } + this.conn = new ManagedClientConnectionImpl(this, this.connOperator, this.poolEntry); + return this.conn; + } + } + + private void shutdownConnection(final HttpClientConnection conn) { + try { + conn.shutdown(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception shutting down connection", iox); + } + } + } + + public void releaseConnection(final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) { + Args.check(conn instanceof ManagedClientConnectionImpl, "Connection class mismatch, " + + "connection not obtained from this manager"); + final ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn; + synchronized (managedConn) { + if (this.log.isDebugEnabled()) { + this.log.debug("Releasing connection " + conn); + } + if (managedConn.getPoolEntry() == null) { + return; // already released + } + final ClientConnectionManager manager = managedConn.getManager(); + Asserts.check(manager == this, "Connection not obtained from this manager"); + synchronized (this) { + if (this.shutdown) { + shutdownConnection(managedConn); + return; + } + try { + if (managedConn.isOpen() && !managedConn.isMarkedReusable()) { + shutdownConnection(managedConn); + } + if (managedConn.isMarkedReusable()) { + this.poolEntry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + keepalive + " " + tunit; + } else { + s = "indefinitely"; + } + this.log.debug("Connection can be kept alive " + s); + } + } + } finally { + managedConn.detach(); + this.conn = null; + if (this.poolEntry.isClosed()) { + this.poolEntry = null; + } + } + } + } + } + + public void closeExpiredConnections() { + synchronized (this) { + assertNotShutdown(); + final long now = System.currentTimeMillis(); + if (this.poolEntry != null && this.poolEntry.isExpired(now)) { + this.poolEntry.close(); + this.poolEntry.getTracker().reset(); + } + } + } + + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + synchronized (this) { + assertNotShutdown(); + long time = tunit.toMillis(idletime); + if (time < 0) { + time = 0; + } + final long deadline = System.currentTimeMillis() - time; + if (this.poolEntry != null && this.poolEntry.getUpdated() <= deadline) { + this.poolEntry.close(); + this.poolEntry.getTracker().reset(); + } + } + } + + public void shutdown() { + synchronized (this) { + this.shutdown = true; + try { + if (this.poolEntry != null) { + this.poolEntry.close(); + } + } finally { + this.poolEntry = null; + this.conn = null; + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicHttpClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicHttpClientConnectionManager.java new file mode 100644 index 000000000..1049a4151 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicHttpClientConnectionManager.java @@ -0,0 +1,370 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.config.Registry; +import ch.boye.httpclientandroidlib.config.RegistryBuilder; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.conn.ConnectionRequest; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.SSLConnectionSocketFactory; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * A connection manager for a single connection. This connection manager maintains only one active + * connection. Even though this class is fully thread-safe it ought to be used by one execution + * thread only, as only one thread a time can lease the connection at a time. + *

    + * This connection manager will make an effort to reuse the connection for subsequent requests + * with the same {@link HttpRoute route}. It will, however, close the existing connection and + * open it for the given route, if the route of the persistent connection does not match that + * of the connection request. If the connection has been already been allocated + * {@link IllegalStateException} is thrown. + *

    + * This connection manager implementation should be used inside an EJB container instead of + * {@link PoolingHttpClientConnectionManager}. + * + * @since 4.3 + */ +@ThreadSafe +public class BasicHttpClientConnectionManager implements HttpClientConnectionManager, Closeable { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpClientConnectionOperator connectionOperator; + private final HttpConnectionFactory connFactory; + + @GuardedBy("this") + private ManagedHttpClientConnection conn; + + @GuardedBy("this") + private HttpRoute route; + + @GuardedBy("this") + private Object state; + + @GuardedBy("this") + private long updated; + + @GuardedBy("this") + private long expiry; + + @GuardedBy("this") + private boolean leased; + + @GuardedBy("this") + private SocketConfig socketConfig; + + @GuardedBy("this") + private ConnectionConfig connConfig; + + private final AtomicBoolean isShutdown; + + private static Registry getDefaultRegistry() { + return RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", SSLConnectionSocketFactory.getSocketFactory()) + .build(); + } + + public BasicHttpClientConnectionManager( + final Lookup socketFactoryRegistry, + final HttpConnectionFactory connFactory, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver) { + super(); + this.connectionOperator = new HttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver); + this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; + this.expiry = Long.MAX_VALUE; + this.socketConfig = SocketConfig.DEFAULT; + this.connConfig = ConnectionConfig.DEFAULT; + this.isShutdown = new AtomicBoolean(false); + } + + public BasicHttpClientConnectionManager( + final Lookup socketFactoryRegistry, + final HttpConnectionFactory connFactory) { + this(socketFactoryRegistry, connFactory, null, null); + } + + public BasicHttpClientConnectionManager( + final Lookup socketFactoryRegistry) { + this(socketFactoryRegistry, null, null, null); + } + + public BasicHttpClientConnectionManager() { + this(getDefaultRegistry(), null, null, null); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { // Make sure we call overridden method even if shutdown barfs + super.finalize(); + } + } + + public void close() { + shutdown(); + } + + HttpRoute getRoute() { + return route; + } + + Object getState() { + return state; + } + + public synchronized SocketConfig getSocketConfig() { + return socketConfig; + } + + public synchronized void setSocketConfig(final SocketConfig socketConfig) { + this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; + } + + public synchronized ConnectionConfig getConnectionConfig() { + return connConfig; + } + + public synchronized void setConnectionConfig(final ConnectionConfig connConfig) { + this.connConfig = connConfig != null ? connConfig : ConnectionConfig.DEFAULT; + } + + public final ConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + Args.notNull(route, "Route"); + return new ConnectionRequest() { + + public boolean cancel() { + // Nothing to abort, since requests are immediate. + return false; + } + + public HttpClientConnection get(final long timeout, final TimeUnit tunit) { + return BasicHttpClientConnectionManager.this.getConnection( + route, state); + } + + }; + } + + private void closeConnection() { + if (this.conn != null) { + this.log.debug("Closing connection"); + try { + this.conn.close(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception closing connection", iox); + } + } + this.conn = null; + } + } + + private void shutdownConnection() { + if (this.conn != null) { + this.log.debug("Shutting down connection"); + try { + this.conn.shutdown(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception shutting down connection", iox); + } + } + this.conn = null; + } + } + + private void checkExpiry() { + if (this.conn != null && System.currentTimeMillis() >= this.expiry) { + if (this.log.isDebugEnabled()) { + this.log.debug("Connection expired @ " + new Date(this.expiry)); + } + closeConnection(); + } + } + + synchronized HttpClientConnection getConnection(final HttpRoute route, final Object state) { + Asserts.check(!this.isShutdown.get(), "Connection manager has been shut down"); + if (this.log.isDebugEnabled()) { + this.log.debug("Get connection for route " + route); + } + Asserts.check(!this.leased, "Connection is still allocated"); + if (!LangUtils.equals(this.route, route) || !LangUtils.equals(this.state, state)) { + closeConnection(); + } + this.route = route; + this.state = state; + checkExpiry(); + if (this.conn == null) { + this.conn = this.connFactory.create(route, this.connConfig); + } + this.leased = true; + return this.conn; + } + + public synchronized void releaseConnection( + final HttpClientConnection conn, + final Object state, + final long keepalive, final TimeUnit tunit) { + Args.notNull(conn, "Connection"); + Asserts.check(conn == this.conn, "Connection not obtained from this manager"); + if (this.log.isDebugEnabled()) { + this.log.debug("Releasing connection " + conn); + } + if (this.isShutdown.get()) { + return; + } + try { + this.updated = System.currentTimeMillis(); + if (!this.conn.isOpen()) { + this.conn = null; + this.route = null; + this.conn = null; + this.expiry = Long.MAX_VALUE; + } else { + this.state = state; + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + keepalive + " " + tunit; + } else { + s = "indefinitely"; + } + this.log.debug("Connection can be kept alive " + s); + } + if (keepalive > 0) { + this.expiry = this.updated + tunit.toMillis(keepalive); + } else { + this.expiry = Long.MAX_VALUE; + } + } + } finally { + this.leased = false; + } + } + + public void connect( + final HttpClientConnection conn, + final HttpRoute route, + final int connectTimeout, + final HttpContext context) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(route, "HTTP route"); + Asserts.check(conn == this.conn, "Connection not obtained from this manager"); + final HttpHost host; + if (route.getProxyHost() != null) { + host = route.getProxyHost(); + } else { + host = route.getTargetHost(); + } + final InetSocketAddress localAddress = route.getLocalSocketAddress(); + this.connectionOperator.connect(this.conn, host, localAddress, + connectTimeout, this.socketConfig, context); + } + + public void upgrade( + final HttpClientConnection conn, + final HttpRoute route, + final HttpContext context) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(route, "HTTP route"); + Asserts.check(conn == this.conn, "Connection not obtained from this manager"); + this.connectionOperator.upgrade(this.conn, route.getTargetHost(), context); + } + + public void routeComplete( + final HttpClientConnection conn, + final HttpRoute route, + final HttpContext context) throws IOException { + } + + public synchronized void closeExpiredConnections() { + if (this.isShutdown.get()) { + return; + } + if (!this.leased) { + checkExpiry(); + } + } + + public synchronized void closeIdleConnections(final long idletime, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + if (this.isShutdown.get()) { + return; + } + if (!this.leased) { + long time = tunit.toMillis(idletime); + if (time < 0) { + time = 0; + } + final long deadline = System.currentTimeMillis() - time; + if (this.updated <= deadline) { + closeConnection(); + } + } + } + + public synchronized void shutdown() { + if (this.isShutdown.compareAndSet(false, true)) { + shutdownConnection(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPool.java new file mode 100644 index 000000000..040960c71 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPool.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.pool.AbstractConnPool; +import ch.boye.httpclientandroidlib.pool.ConnFactory; + +/** + * @since 4.3 + */ +@ThreadSafe +class CPool extends AbstractConnPool { + + private static final AtomicLong COUNTER = new AtomicLong(); + + public HttpClientAndroidLog log = new HttpClientAndroidLog(CPool.class); + private final long timeToLive; + private final TimeUnit tunit; + + public CPool( + final ConnFactory connFactory, + final int defaultMaxPerRoute, final int maxTotal, + final long timeToLive, final TimeUnit tunit) { + super(connFactory, defaultMaxPerRoute, maxTotal); + this.timeToLive = timeToLive; + this.tunit = tunit; + } + + @Override + protected CPoolEntry createEntry(final HttpRoute route, final ManagedHttpClientConnection conn) { + final String id = Long.toString(COUNTER.getAndIncrement()); + return new CPoolEntry(this.log, id, route, conn, this.timeToLive, this.tunit); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolEntry.java new file mode 100644 index 000000000..f5ecfdc5e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolEntry.java @@ -0,0 +1,101 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.pool.PoolEntry; + +/** + * @since 4.3 + */ +@ThreadSafe +class CPoolEntry extends PoolEntry { + + public HttpClientAndroidLog log; + private volatile boolean routeComplete; + + public CPoolEntry( + final HttpClientAndroidLog log, + final String id, + final HttpRoute route, + final ManagedHttpClientConnection conn, + final long timeToLive, final TimeUnit tunit) { + super(id, route, conn, timeToLive, tunit); + this.log = log; + } + + public void markRouteComplete() { + this.routeComplete = true; + } + + public boolean isRouteComplete() { + return this.routeComplete; + } + + public void closeConnection() throws IOException { + final HttpClientConnection conn = getConnection(); + conn.close(); + } + + public void shutdownConnection() throws IOException { + final HttpClientConnection conn = getConnection(); + conn.shutdown(); + } + + @Override + public boolean isExpired(final long now) { + final boolean expired = super.isExpired(now); + if (expired && this.log.isDebugEnabled()) { + this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry())); + } + return expired; + } + + @Override + public boolean isClosed() { + final HttpClientConnection conn = getConnection(); + return !conn.isOpen(); + } + + @Override + public void close() { + try { + closeConnection(); + } catch (final IOException ex) { + this.log.debug("I/O error closing connection", ex); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolProxy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolProxy.java new file mode 100644 index 000000000..9ac67a10d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolProxy.java @@ -0,0 +1,245 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +import javax.net.ssl.SSLSession; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * @since 4.3 + */ +@NotThreadSafe +class CPoolProxy implements ManagedHttpClientConnection, HttpContext { + + private volatile CPoolEntry poolEntry; + + CPoolProxy(final CPoolEntry entry) { + super(); + this.poolEntry = entry; + } + + CPoolEntry getPoolEntry() { + return this.poolEntry; + } + + CPoolEntry detach() { + final CPoolEntry local = this.poolEntry; + this.poolEntry = null; + return local; + } + + ManagedHttpClientConnection getConnection() { + final CPoolEntry local = this.poolEntry; + if (local == null) { + return null; + } + return local.getConnection(); + } + + ManagedHttpClientConnection getValidConnection() { + final ManagedHttpClientConnection conn = getConnection(); + if (conn == null) { + throw new ConnectionShutdownException(); + } + return conn; + } + + public void close() throws IOException { + final CPoolEntry local = this.poolEntry; + if (local != null) { + local.closeConnection(); + } + } + + public void shutdown() throws IOException { + final CPoolEntry local = this.poolEntry; + if (local != null) { + local.shutdownConnection(); + } + } + + public boolean isOpen() { + final CPoolEntry local = this.poolEntry; + if (local != null) { + return !local.isClosed(); + } else { + return false; + } + } + + public boolean isStale() { + final HttpClientConnection conn = getConnection(); + if (conn != null) { + return conn.isStale(); + } else { + return true; + } + } + + public void setSocketTimeout(final int timeout) { + getValidConnection().setSocketTimeout(timeout); + } + + public int getSocketTimeout() { + return getValidConnection().getSocketTimeout(); + } + + public String getId() { + return getValidConnection().getId(); + } + + public void bind(final Socket socket) throws IOException { + getValidConnection().bind(socket); + } + + public Socket getSocket() { + return getValidConnection().getSocket(); + } + + public SSLSession getSSLSession() { + return getValidConnection().getSSLSession(); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + return getValidConnection().isResponseAvailable(timeout); + } + + public void sendRequestHeader(final HttpRequest request) throws HttpException, IOException { + getValidConnection().sendRequestHeader(request); + } + + public void sendRequestEntity(final HttpEntityEnclosingRequest request) throws HttpException, IOException { + getValidConnection().sendRequestEntity(request); + } + + public HttpResponse receiveResponseHeader() throws HttpException, IOException { + return getValidConnection().receiveResponseHeader(); + } + + public void receiveResponseEntity(final HttpResponse response) throws HttpException, IOException { + getValidConnection().receiveResponseEntity(response); + } + + public void flush() throws IOException { + getValidConnection().flush(); + } + + public HttpConnectionMetrics getMetrics() { + return getValidConnection().getMetrics(); + } + + public InetAddress getLocalAddress() { + return getValidConnection().getLocalAddress(); + } + + public int getLocalPort() { + return getValidConnection().getLocalPort(); + } + + public InetAddress getRemoteAddress() { + return getValidConnection().getRemoteAddress(); + } + + public int getRemotePort() { + return getValidConnection().getRemotePort(); + } + + public Object getAttribute(final String id) { + final ManagedHttpClientConnection conn = getValidConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).getAttribute(id); + } else { + return null; + } + } + + public void setAttribute(final String id, final Object obj) { + final ManagedHttpClientConnection conn = getValidConnection(); + if (conn instanceof HttpContext) { + ((HttpContext) conn).setAttribute(id, obj); + } + } + + public Object removeAttribute(final String id) { + final ManagedHttpClientConnection conn = getValidConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).removeAttribute(id); + } else { + return null; + } + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("CPoolProxy{"); + final ManagedHttpClientConnection conn = getConnection(); + if (conn != null) { + sb.append(conn); + } else { + sb.append("detached"); + } + sb.append('}'); + return sb.toString(); + } + + public static HttpClientConnection newProxy(final CPoolEntry poolEntry) { + return new CPoolProxy(poolEntry); + } + + private static CPoolProxy getProxy(final HttpClientConnection conn) { + if (!CPoolProxy.class.isInstance(conn)) { + throw new IllegalStateException("Unexpected connection proxy class: " + conn.getClass()); + } + return CPoolProxy.class.cast(conn); + } + + public static CPoolEntry getPoolEntry(final HttpClientConnection proxy) { + final CPoolEntry entry = getProxy(proxy).getPoolEntry(); + if (entry == null) { + throw new ConnectionShutdownException(); + } + return entry; + } + + public static CPoolEntry detach(final HttpClientConnection conn) { + return getProxy(conn).detach(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ConnectionShutdownException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ConnectionShutdownException.java new file mode 100644 index 000000000..03820a38f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ConnectionShutdownException.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that the connection has been shut down or released back to the + * the connection pool + * + * @since 4.1 + */ +@Immutable +public class ConnectionShutdownException extends IllegalStateException { + + private static final long serialVersionUID = 5868657401162844497L; + + /** + * Creates a new ConnectionShutdownException with a null detail message. + */ + public ConnectionShutdownException() { + super(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnection.java new file mode 100644 index 000000000..f9003b144 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnection.java @@ -0,0 +1,292 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.impl.SocketHttpClientConnection; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.params.HttpProtocolParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of an operated client connection. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ManagedHttpClientConnectionFactory}. + */ +@NotThreadSafe // connSecure, targetHost +@Deprecated +public class DefaultClientConnection extends SocketHttpClientConnection + implements OperatedClientConnection, ManagedHttpClientConnection, HttpContext { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + public HttpClientAndroidLog headerLog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.headers"); + public HttpClientAndroidLog wireLog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.wire"); + + /** The unconnected socket */ + private volatile Socket socket; + + /** The target host of this connection. */ + private HttpHost targetHost; + + /** Whether this connection is secure. */ + private boolean connSecure; + + /** True if this connection was shutdown. */ + private volatile boolean shutdown; + + /** connection specific attributes */ + private final Map attributes; + + public DefaultClientConnection() { + super(); + this.attributes = new HashMap(); + } + + public String getId() { + return null; + } + + public final HttpHost getTargetHost() { + return this.targetHost; + } + + public final boolean isSecure() { + return this.connSecure; + } + + @Override + public final Socket getSocket() { + return this.socket; + } + + public SSLSession getSSLSession() { + if (this.socket instanceof SSLSocket) { + return ((SSLSocket) this.socket).getSession(); + } else { + return null; + } + } + + public void opening(final Socket sock, final HttpHost target) throws IOException { + assertNotOpen(); + this.socket = sock; + this.targetHost = target; + + // Check for shutdown after assigning socket, so that + if (this.shutdown) { + sock.close(); // allow this to throw... + // ...but if it doesn't, explicitly throw one ourselves. + throw new InterruptedIOException("Connection already shutdown"); + } + } + + public void openCompleted(final boolean secure, final HttpParams params) throws IOException { + Args.notNull(params, "Parameters"); + assertNotOpen(); + this.connSecure = secure; + bind(this.socket, params); + } + + /** + * Force-closes this connection. + * If the connection is still in the process of being open (the method + * {@link #opening opening} was already called but + * {@link #openCompleted openCompleted} was not), the associated + * socket that is being connected to a remote address will be closed. + * That will interrupt a thread that is blocked on connecting + * the socket. + * If the connection is not yet open, this will prevent the connection + * from being opened. + * + * @throws IOException in case of a problem + */ + @Override + public void shutdown() throws IOException { + shutdown = true; + try { + super.shutdown(); + if (log.isDebugEnabled()) { + log.debug("Connection " + this + " shut down"); + } + final Socket sock = this.socket; // copy volatile attribute + if (sock != null) { + sock.close(); + } + } catch (final IOException ex) { + log.debug("I/O error shutting down connection", ex); + } + } + + @Override + public void close() throws IOException { + try { + super.close(); + if (log.isDebugEnabled()) { + log.debug("Connection " + this + " closed"); + } + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + + @Override + protected SessionInputBuffer createSessionInputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + SessionInputBuffer inbuffer = super.createSessionInputBuffer( + socket, + buffersize > 0 ? buffersize : 8192, + params); + if (wireLog.isDebugEnabled()) { + inbuffer = new LoggingSessionInputBuffer( + inbuffer, + new Wire(wireLog), + HttpProtocolParams.getHttpElementCharset(params)); + } + return inbuffer; + } + + @Override + protected SessionOutputBuffer createSessionOutputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + SessionOutputBuffer outbuffer = super.createSessionOutputBuffer( + socket, + buffersize > 0 ? buffersize : 8192, + params); + if (wireLog.isDebugEnabled()) { + outbuffer = new LoggingSessionOutputBuffer( + outbuffer, + new Wire(wireLog), + HttpProtocolParams.getHttpElementCharset(params)); + } + return outbuffer; + } + + @Override + protected HttpMessageParser createResponseParser( + final SessionInputBuffer buffer, + final HttpResponseFactory responseFactory, + final HttpParams params) { + // override in derived class to specify a line parser + return new DefaultHttpResponseParser + (buffer, null, responseFactory, params); + } + + public void bind(final Socket socket) throws IOException { + bind(socket, new BasicHttpParams()); + } + + public void update(final Socket sock, final HttpHost target, + final boolean secure, final HttpParams params) + throws IOException { + + assertOpen(); + Args.notNull(target, "Target host"); + Args.notNull(params, "Parameters"); + + if (sock != null) { + this.socket = sock; + bind(sock, params); + } + targetHost = target; + connSecure = secure; + } + + @Override + public HttpResponse receiveResponseHeader() throws HttpException, IOException { + final HttpResponse response = super.receiveResponseHeader(); + if (log.isDebugEnabled()) { + log.debug("Receiving response: " + response.getStatusLine()); + } + if (headerLog.isDebugEnabled()) { + headerLog.debug("<< " + response.getStatusLine().toString()); + final Header[] headers = response.getAllHeaders(); + for (final Header header : headers) { + headerLog.debug("<< " + header.toString()); + } + } + return response; + } + + @Override + public void sendRequestHeader(final HttpRequest request) throws HttpException, IOException { + if (log.isDebugEnabled()) { + log.debug("Sending request: " + request.getRequestLine()); + } + super.sendRequestHeader(request); + if (headerLog.isDebugEnabled()) { + headerLog.debug(">> " + request.getRequestLine().toString()); + final Header[] headers = request.getAllHeaders(); + for (final Header header : headers) { + headerLog.debug(">> " + header.toString()); + } + } + } + + public Object getAttribute(final String id) { + return this.attributes.get(id); + } + + public Object removeAttribute(final String id) { + return this.attributes.remove(id); + } + + public void setAttribute(final String id, final Object obj) { + this.attributes.put(id, obj); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java new file mode 100644 index 000000000..3cdf706ec --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java @@ -0,0 +1,263 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.protocol.ClientContext; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpInetSocketAddress; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeLayeredSocketFactory; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeSocketFactory; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry} + * to look up {@link SchemeSocketFactory} objects. + *

    + * This connection operator is multihome network aware and will attempt to retry failed connects + * against all known IP addresses sequentially until the connect is successful or all known + * addresses fail to respond. Please note the same + * {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT} value will be used + * for each connection attempt, so in the worst case the total elapsed time before timeout + * can be CONNECTION_TIMEOUT * n where n is the number of IP addresses + * of the given host. One can disable multihome support by overriding + * the {@link #resolveHostname(String)} method and returning only one IP address for the given + * host name. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link PoolingHttpClientConnectionManager}. + */ +@Deprecated +@ThreadSafe +public class DefaultClientConnectionOperator implements ClientConnectionOperator { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** The scheme registry for looking up socket factories. */ + protected final SchemeRegistry schemeRegistry; // @ThreadSafe + + /** the custom-configured DNS lookup mechanism. */ + protected final DnsResolver dnsResolver; + + /** + * Creates a new client connection operator for the given scheme registry. + * + * @param schemes the scheme registry + * + * @since 4.2 + */ + public DefaultClientConnectionOperator(final SchemeRegistry schemes) { + Args.notNull(schemes, "Scheme registry"); + this.schemeRegistry = schemes; + this.dnsResolver = new SystemDefaultDnsResolver(); + } + + /** + * Creates a new client connection operator for the given scheme registry + * and the given custom DNS lookup mechanism. + * + * @param schemes + * the scheme registry + * @param dnsResolver + * the custom DNS lookup mechanism + */ + public DefaultClientConnectionOperator(final SchemeRegistry schemes,final DnsResolver dnsResolver) { + Args.notNull(schemes, "Scheme registry"); + + Args.notNull(dnsResolver, "DNS resolver"); + + this.schemeRegistry = schemes; + this.dnsResolver = dnsResolver; + } + + public OperatedClientConnection createConnection() { + return new DefaultClientConnection(); + } + + private SchemeRegistry getSchemeRegistry(final HttpContext context) { + SchemeRegistry reg = (SchemeRegistry) context.getAttribute( + ClientContext.SCHEME_REGISTRY); + if (reg == null) { + reg = this.schemeRegistry; + } + return reg; + } + + public void openConnection( + final OperatedClientConnection conn, + final HttpHost target, + final InetAddress local, + final HttpContext context, + final HttpParams params) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(target, "Target host"); + Args.notNull(params, "HTTP parameters"); + Asserts.check(!conn.isOpen(), "Connection must not be open"); + + final SchemeRegistry registry = getSchemeRegistry(context); + final Scheme schm = registry.getScheme(target.getSchemeName()); + final SchemeSocketFactory sf = schm.getSchemeSocketFactory(); + + final InetAddress[] addresses = resolveHostname(target.getHostName()); + final int port = schm.resolvePort(target.getPort()); + for (int i = 0; i < addresses.length; i++) { + final InetAddress address = addresses[i]; + final boolean last = i == addresses.length - 1; + + Socket sock = sf.createSocket(params); + conn.opening(sock, target); + + final InetSocketAddress remoteAddress = new HttpInetSocketAddress(target, address, port); + InetSocketAddress localAddress = null; + if (local != null) { + localAddress = new InetSocketAddress(local, 0); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connecting to " + remoteAddress); + } + try { + final Socket connsock = sf.connectSocket(sock, remoteAddress, localAddress, params); + if (sock != connsock) { + sock = connsock; + conn.opening(sock, target); + } + prepareSocket(sock, context, params); + conn.openCompleted(sf.isSecure(sock), params); + return; + } catch (final ConnectException ex) { + if (last) { + throw ex; + } + } catch (final ConnectTimeoutException ex) { + if (last) { + throw ex; + } + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connect to " + remoteAddress + " timed out. " + + "Connection will be retried using another IP address"); + } + } + } + + public void updateSecureConnection( + final OperatedClientConnection conn, + final HttpHost target, + final HttpContext context, + final HttpParams params) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(target, "Target host"); + Args.notNull(params, "Parameters"); + Asserts.check(conn.isOpen(), "Connection must be open"); + + final SchemeRegistry registry = getSchemeRegistry(context); + final Scheme schm = registry.getScheme(target.getSchemeName()); + Asserts.check(schm.getSchemeSocketFactory() instanceof SchemeLayeredSocketFactory, + "Socket factory must implement SchemeLayeredSocketFactory"); + final SchemeLayeredSocketFactory lsf = (SchemeLayeredSocketFactory) schm.getSchemeSocketFactory(); + final Socket sock = lsf.createLayeredSocket( + conn.getSocket(), target.getHostName(), schm.resolvePort(target.getPort()), params); + prepareSocket(sock, context, params); + conn.update(sock, target, lsf.isSecure(sock), params); + } + + /** + * Performs standard initializations on a newly created socket. + * + * @param sock the socket to prepare + * @param context the context for the connection + * @param params the parameters from which to prepare the socket + * + * @throws IOException in case of an IO problem + */ + protected void prepareSocket( + final Socket sock, + final HttpContext context, + final HttpParams params) throws IOException { + sock.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params)); + sock.setSoTimeout(HttpConnectionParams.getSoTimeout(params)); + + final int linger = HttpConnectionParams.getLinger(params); + if (linger >= 0) { + sock.setSoLinger(linger > 0, linger); + } + } + + /** + * Resolves the given host name to an array of corresponding IP addresses, based on the + * configured name service on the provided DNS resolver. If one wasn't provided, the system + * configuration is used. + * + * @param host host name to resolve + * @return array of IP addresses + * @exception UnknownHostException if no IP address for the host could be determined. + * + * @see DnsResolver + * @see SystemDefaultDnsResolver + * + * @since 4.1 + */ + protected InetAddress[] resolveHostname(final String host) throws UnknownHostException { + return dnsResolver.resolve(host); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParser.java new file mode 100644 index 000000000..9f249e5f7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParser.java @@ -0,0 +1,168 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory; +import ch.boye.httpclientandroidlib.impl.io.AbstractMessageParser; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Lenient HTTP response parser implementation that can skip malformed data until + * a valid HTTP response message head is encountered. + * + * @since 4.2 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public class DefaultHttpResponseParser extends AbstractMessageParser { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpResponseFactory responseFactory; + private final CharArrayBuffer lineBuf; + + /** + * @deprecated (4.3) use {@link DefaultHttpResponseParser#DefaultHttpResponseParser( + * SessionInputBuffer, LineParser, HttpResponseFactory, MessageConstraints)} + */ + @Deprecated + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, + final LineParser parser, + final HttpResponseFactory responseFactory, + final HttpParams params) { + super(buffer, parser, params); + Args.notNull(responseFactory, "Response factory"); + this.responseFactory = responseFactory; + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * Creates new instance of DefaultHttpResponseParser. + * + * @param buffer the session input buffer. + * @param lineParser the line parser. If null + * {@link ch.boye.httpclientandroidlib.message.BasicLineParser#INSTANCE} will be used. + * @param responseFactory HTTP response factory. If null + * {@link DefaultHttpResponseFactory#INSTANCE} will be used. + * @param constraints the message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.3 + */ + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, + final LineParser lineParser, + final HttpResponseFactory responseFactory, + final MessageConstraints constraints) { + super(buffer, lineParser, constraints); + this.responseFactory = responseFactory != null ? responseFactory : + DefaultHttpResponseFactory.INSTANCE; + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * Creates new instance of DefaultHttpResponseParser. + * + * @param buffer the session input buffer. + * @param constraints the message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.3 + */ + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, final MessageConstraints constraints) { + this(buffer, null, null, constraints); + } + + /** + * Creates new instance of DefaultHttpResponseParser. + * + * @param buffer the session input buffer. + * + * @since 4.3 + */ + public DefaultHttpResponseParser(final SessionInputBuffer buffer) { + this(buffer, null, null, MessageConstraints.DEFAULT); + } + + @Override + protected HttpResponse parseHead( + final SessionInputBuffer sessionBuffer) throws IOException, HttpException { + //read out the HTTP status string + int count = 0; + ParserCursor cursor = null; + do { + // clear the buffer + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1 && count == 0) { + // The server just dropped connection on us + throw new NoHttpResponseException("The target server failed to respond"); + } + cursor = new ParserCursor(0, this.lineBuf.length()); + if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) { + // Got one + break; + } else if (i == -1 || reject(this.lineBuf, count)) { + // Giving up + throw new ProtocolException("The server failed to respond with a " + + "valid HTTP response"); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Garbage in response: " + this.lineBuf.toString()); + } + count++; + } while(true); + //create the status line from the status string + final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor); + return this.responseFactory.newHttpResponse(statusline, null); + } + + protected boolean reject(final CharArrayBuffer line, final int count) { + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParserFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParserFactory.java new file mode 100644 index 000000000..a229b2ca6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParserFactory.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineParser; +import ch.boye.httpclientandroidlib.message.LineParser; + +/** + * Default factory for response message parsers. + * + * @since 4.3 + */ +@Immutable +public class DefaultHttpResponseParserFactory implements HttpMessageParserFactory { + + public static final DefaultHttpResponseParserFactory INSTANCE = new DefaultHttpResponseParserFactory(); + + private final LineParser lineParser; + private final HttpResponseFactory responseFactory; + + public DefaultHttpResponseParserFactory( + final LineParser lineParser, + final HttpResponseFactory responseFactory) { + super(); + this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE; + this.responseFactory = responseFactory != null ? responseFactory + : DefaultHttpResponseFactory.INSTANCE; + } + + public DefaultHttpResponseParserFactory( + final HttpResponseFactory responseFactory) { + this(null, responseFactory); + } + + public DefaultHttpResponseParserFactory() { + this(null, null); + } + + public HttpMessageParser create(final SessionInputBuffer buffer, + final MessageConstraints constraints) { + return new DefaultHttpResponseParser(buffer, lineParser, responseFactory, constraints); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java new file mode 100644 index 000000000..4b3525329 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java @@ -0,0 +1,123 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + + +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.params.ConnRouteParams; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Default implementation of an {@link HttpRoutePlanner}. This implementation + * is based on {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames parameters}. + * It will not make use of any Java system properties, nor of system or + * browser proxy settings. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultRoutePlanner} + */ +@ThreadSafe +@Deprecated +public class DefaultHttpRoutePlanner implements HttpRoutePlanner { + + /** The scheme registry. */ + protected final SchemeRegistry schemeRegistry; // class is @ThreadSafe + + /** + * Creates a new default route planner. + * + * @param schreg the scheme registry + */ + public DefaultHttpRoutePlanner(final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + schemeRegistry = schreg; + } + + public HttpRoute determineRoute(final HttpHost target, + final HttpRequest request, + final HttpContext context) + throws HttpException { + + Args.notNull(request, "HTTP request"); + + // If we have a forced route, we can do without a target. + HttpRoute route = + ConnRouteParams.getForcedRoute(request.getParams()); + if (route != null) { + return route; + } + + // If we get here, there is no forced route. + // So we need a target to compute a route. + + Asserts.notNull(target, "Target host"); + + final InetAddress local = + ConnRouteParams.getLocalAddress(request.getParams()); + final HttpHost proxy = + ConnRouteParams.getDefaultProxy(request.getParams()); + + final Scheme schm; + try { + schm = this.schemeRegistry.getScheme(target.getSchemeName()); + } catch (final IllegalStateException ex) { + throw new HttpException(ex.getMessage()); + } + // as it is typically used for TLS/SSL, we assume that + // a layered scheme implies a secure connection + final boolean secure = schm.isLayered(); + + if (proxy == null) { + route = new HttpRoute(target, local, secure); + } else { + route = new HttpRoute(target, local, proxy, secure); + } + return route; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultManagedHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultManagedHttpClientConnection.java new file mode 100644 index 000000000..c46b93405 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultManagedHttpClientConnection.java @@ -0,0 +1,135 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.Socket; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.DefaultBHttpClientConnection; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Default {@link ManagedHttpClientConnection} implementation. + * @since 4.3 + */ +@NotThreadSafe +public class DefaultManagedHttpClientConnection extends DefaultBHttpClientConnection + implements ManagedHttpClientConnection, HttpContext { + + private final String id; + private final Map attributes; + + private volatile boolean shutdown; + + public DefaultManagedHttpClientConnection( + final String id, + final int buffersize, + final int fragmentSizeHint, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageWriterFactory requestWriterFactory, + final HttpMessageParserFactory responseParserFactory) { + super(buffersize, fragmentSizeHint, chardecoder, charencoder, + constraints, incomingContentStrategy, outgoingContentStrategy, + requestWriterFactory, responseParserFactory); + this.id = id; + this.attributes = new ConcurrentHashMap(); + } + + public DefaultManagedHttpClientConnection( + final String id, + final int buffersize) { + this(id, buffersize, buffersize, null, null, null, null, null, null, null); + } + + public String getId() { + return this.id; + } + + @Override + public void shutdown() throws IOException { + this.shutdown = true; + super.shutdown(); + } + + public Object getAttribute(final String id) { + return this.attributes.get(id); + } + + public Object removeAttribute(final String id) { + return this.attributes.remove(id); + } + + public void setAttribute(final String id, final Object obj) { + this.attributes.put(id, obj); + } + + @Override + public void bind(final Socket socket) throws IOException { + if (this.shutdown) { + socket.close(); // allow this to throw... + // ...but if it doesn't, explicitly throw one ourselves. + throw new InterruptedIOException("Connection already shutdown"); + } + super.bind(socket); + } + + @Override + public Socket getSocket() { + return super.getSocket(); + } + + public SSLSession getSSLSession() { + final Socket socket = super.getSocket(); + if (socket instanceof SSLSocket) { + return ((SSLSocket) socket).getSession(); + } else { + return null; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultProxyRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultProxyRoutePlanner.java new file mode 100644 index 000000000..fd569556c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultProxyRoutePlanner.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Implementation of an {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner} + * that routes requests through a default proxy. + * + * @since 4.3 + */ +@Immutable +public class DefaultProxyRoutePlanner extends DefaultRoutePlanner { + + private final HttpHost proxy; + + public DefaultProxyRoutePlanner(final HttpHost proxy, final SchemePortResolver schemePortResolver) { + super(schemePortResolver); + this.proxy = Args.notNull(proxy, "Proxy host"); + } + + public DefaultProxyRoutePlanner(final HttpHost proxy) { + this(proxy, null); + } + + @Override + protected HttpHost determineProxy( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws HttpException { + return proxy; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultResponseParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultResponseParser.java new file mode 100644 index 000000000..e82452ff5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultResponseParser.java @@ -0,0 +1,125 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.impl.io.AbstractMessageParser; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Default HTTP response parser implementation. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnConnectionPNames#MAX_STATUS_LINE_GARBAGE}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.2) use {@link DefaultHttpResponseParser} + */ +@Deprecated +@ThreadSafe // no public methods +public class DefaultResponseParser extends AbstractMessageParser { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpResponseFactory responseFactory; + private final CharArrayBuffer lineBuf; + private final int maxGarbageLines; + + public DefaultResponseParser( + final SessionInputBuffer buffer, + final LineParser parser, + final HttpResponseFactory responseFactory, + final HttpParams params) { + super(buffer, parser, params); + Args.notNull(responseFactory, "Response factory"); + this.responseFactory = responseFactory; + this.lineBuf = new CharArrayBuffer(128); + this.maxGarbageLines = getMaxGarbageLines(params); + } + + protected int getMaxGarbageLines(final HttpParams params) { + return params.getIntParameter( + ch.boye.httpclientandroidlib.conn.params.ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, + Integer.MAX_VALUE); + } + + @Override + protected HttpMessage parseHead( + final SessionInputBuffer sessionBuffer) throws IOException, HttpException { + //read out the HTTP status string + int count = 0; + ParserCursor cursor = null; + do { + // clear the buffer + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1 && count == 0) { + // The server just dropped connection on us + throw new NoHttpResponseException("The target server failed to respond"); + } + cursor = new ParserCursor(0, this.lineBuf.length()); + if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) { + // Got one + break; + } else if (i == -1 || count >= this.maxGarbageLines) { + // Giving up + throw new ProtocolException("The server failed to respond with a " + + "valid HTTP response"); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Garbage in response: " + this.lineBuf.toString()); + } + count++; + } while(true); + //create the status line from the status string + final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor); + return this.responseFactory.newHttpResponse(statusline, null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultRoutePlanner.java new file mode 100644 index 000000000..2c08a7118 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultRoutePlanner.java @@ -0,0 +1,107 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of an {@link HttpRoutePlanner}. It will not make use of + * any Java system properties, nor of system or browser proxy settings. + * + * @since 4.3 + */ +@Immutable +public class DefaultRoutePlanner implements HttpRoutePlanner { + + private final SchemePortResolver schemePortResolver; + + public DefaultRoutePlanner(final SchemePortResolver schemePortResolver) { + super(); + this.schemePortResolver = schemePortResolver != null ? schemePortResolver : + DefaultSchemePortResolver.INSTANCE; + } + + public HttpRoute determineRoute( + final HttpHost host, + final HttpRequest request, + final HttpContext context) throws HttpException { + Args.notNull(request, "Request"); + if (host == null) { + throw new ProtocolException("Target host is not specified"); + } + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final RequestConfig config = clientContext.getRequestConfig(); + final InetAddress local = config.getLocalAddress(); + HttpHost proxy = config.getProxy(); + if (proxy == null) { + proxy = determineProxy(host, request, context); + } + + final HttpHost target; + if (host.getPort() <= 0) { + try { + target = new HttpHost( + host.getHostName(), + this.schemePortResolver.resolve(host), + host.getSchemeName()); + } catch (final UnsupportedSchemeException ex) { + throw new HttpException(ex.getMessage()); + } + } else { + target = host; + } + final boolean secure = target.getSchemeName().equalsIgnoreCase("https"); + if (proxy == null) { + return new HttpRoute(target, local, secure); + } else { + return new HttpRoute(target, local, proxy, secure); + } + } + + protected HttpHost determineProxy( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws HttpException { + return null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultSchemePortResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultSchemePortResolver.java new file mode 100644 index 000000000..4b384f681 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultSchemePortResolver.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default {@link SchemePortResolver}. + * + * @since 4.3 + */ +@Immutable +public class DefaultSchemePortResolver implements SchemePortResolver { + + public static final DefaultSchemePortResolver INSTANCE = new DefaultSchemePortResolver(); + + public int resolve(final HttpHost host) throws UnsupportedSchemeException { + Args.notNull(host, "HTTP host"); + final int port = host.getPort(); + if (port > 0) { + return port; + } + final String name = host.getSchemeName(); + if (name.equalsIgnoreCase("http")) { + return 80; + } else if (name.equalsIgnoreCase("https")) { + return 443; + } else { + throw new UnsupportedSchemeException(name + " protocol is not supported"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpClientConnectionOperator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpClientConnectionOperator.java new file mode 100644 index 000000000..a13368237 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpClientConnectionOperator.java @@ -0,0 +1,173 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpHostConnectException; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +@Immutable +class HttpClientConnectionOperator { + + static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry"; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final Lookup socketFactoryRegistry; + private final SchemePortResolver schemePortResolver; + private final DnsResolver dnsResolver; + + HttpClientConnectionOperator( + final Lookup socketFactoryRegistry, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver) { + super(); + Args.notNull(socketFactoryRegistry, "Socket factory registry"); + this.socketFactoryRegistry = socketFactoryRegistry; + this.schemePortResolver = schemePortResolver != null ? schemePortResolver : + DefaultSchemePortResolver.INSTANCE; + this.dnsResolver = dnsResolver != null ? dnsResolver : + SystemDefaultDnsResolver.INSTANCE; + } + + @SuppressWarnings("unchecked") + private Lookup getSocketFactoryRegistry(final HttpContext context) { + Lookup reg = (Lookup) context.getAttribute( + SOCKET_FACTORY_REGISTRY); + if (reg == null) { + reg = this.socketFactoryRegistry; + } + return reg; + } + + public void connect( + final ManagedHttpClientConnection conn, + final HttpHost host, + final InetSocketAddress localAddress, + final int connectTimeout, + final SocketConfig socketConfig, + final HttpContext context) throws IOException { + final Lookup registry = getSocketFactoryRegistry(context); + final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); + if (sf == null) { + throw new UnsupportedSchemeException(host.getSchemeName() + + " protocol is not supported"); + } + final InetAddress[] addresses = this.dnsResolver.resolve(host.getHostName()); + final int port = this.schemePortResolver.resolve(host); + for (int i = 0; i < addresses.length; i++) { + final InetAddress address = addresses[i]; + final boolean last = i == addresses.length - 1; + + Socket sock = sf.createSocket(context); + sock.setSoTimeout(socketConfig.getSoTimeout()); + sock.setReuseAddress(socketConfig.isSoReuseAddress()); + sock.setTcpNoDelay(socketConfig.isTcpNoDelay()); + sock.setKeepAlive(socketConfig.isSoKeepAlive()); + final int linger = socketConfig.getSoLinger(); + if (linger >= 0) { + sock.setSoLinger(linger > 0, linger); + } + conn.bind(sock); + + final InetSocketAddress remoteAddress = new InetSocketAddress(address, port); + if (this.log.isDebugEnabled()) { + this.log.debug("Connecting to " + remoteAddress); + } + try { + sock = sf.connectSocket( + connectTimeout, sock, host, remoteAddress, localAddress, context); + conn.bind(sock); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection established " + conn); + } + return; + } catch (final SocketTimeoutException ex) { + if (last) { + throw new ConnectTimeoutException(ex, host, addresses); + } + } catch (final ConnectException ex) { + if (last) { + final String msg = ex.getMessage(); + if ("Connection timed out".equals(msg)) { + throw new ConnectTimeoutException(ex, host, addresses); + } else { + throw new HttpHostConnectException(ex, host, addresses); + } + } + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connect to " + remoteAddress + " timed out. " + + "Connection will be retried using another IP address"); + } + } + } + + public void upgrade( + final ManagedHttpClientConnection conn, + final HttpHost host, + final HttpContext context) throws IOException { + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final Lookup registry = getSocketFactoryRegistry(clientContext); + final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); + if (sf == null) { + throw new UnsupportedSchemeException(host.getSchemeName() + + " protocol is not supported"); + } + if (!(sf instanceof LayeredConnectionSocketFactory)) { + throw new UnsupportedSchemeException(host.getSchemeName() + + " protocol does not support connection upgrade"); + } + final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf; + Socket sock = conn.getSocket(); + final int port = this.schemePortResolver.resolve(host); + sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context); + conn.bind(sock); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpConnPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpConnPool.java new file mode 100644 index 000000000..e10bd6cf6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpConnPool.java @@ -0,0 +1,84 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.pool.AbstractConnPool; +import ch.boye.httpclientandroidlib.pool.ConnFactory; + +/** + * @since 4.2 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +class HttpConnPool extends AbstractConnPool { + + private static final AtomicLong COUNTER = new AtomicLong(); + + public HttpClientAndroidLog log; + private final long timeToLive; + private final TimeUnit tunit; + + public HttpConnPool(final HttpClientAndroidLog log, + final ClientConnectionOperator connOperator, + final int defaultMaxPerRoute, final int maxTotal, + final long timeToLive, final TimeUnit tunit) { + super(new InternalConnFactory(connOperator), defaultMaxPerRoute, maxTotal); + this.log = log; + this.timeToLive = timeToLive; + this.tunit = tunit; + } + + @Override + protected HttpPoolEntry createEntry(final HttpRoute route, final OperatedClientConnection conn) { + final String id = Long.toString(COUNTER.getAndIncrement()); + return new HttpPoolEntry(this.log, id, route, conn, this.timeToLive, this.tunit); + } + + static class InternalConnFactory implements ConnFactory { + + private final ClientConnectionOperator connOperator; + + InternalConnFactory(final ClientConnectionOperator connOperator) { + this.connOperator = connOperator; + } + + public OperatedClientConnection create(final HttpRoute route) throws IOException { + return connOperator.createConnection(); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpPoolEntry.java new file mode 100644 index 000000000..d04023126 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpPoolEntry.java @@ -0,0 +1,98 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.pool.PoolEntry; + +/** + * @since 4.2 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +class HttpPoolEntry extends PoolEntry { + + public HttpClientAndroidLog log; + private final RouteTracker tracker; + + public HttpPoolEntry( + final HttpClientAndroidLog log, + final String id, + final HttpRoute route, + final OperatedClientConnection conn, + final long timeToLive, final TimeUnit tunit) { + super(id, route, conn, timeToLive, tunit); + this.log = log; + this.tracker = new RouteTracker(route); + } + + @Override + public boolean isExpired(final long now) { + final boolean expired = super.isExpired(now); + if (expired && this.log.isDebugEnabled()) { + this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry())); + } + return expired; + } + + RouteTracker getTracker() { + return this.tracker; + } + + HttpRoute getPlannedRoute() { + return getRoute(); + } + + HttpRoute getEffectiveRoute() { + return this.tracker.toRoute(); + } + + @Override + public boolean isClosed() { + final OperatedClientConnection conn = getConnection(); + return !conn.isOpen(); + } + + @Override + public void close() { + final OperatedClientConnection conn = getConnection(); + try { + conn.close(); + } catch (final IOException ex) { + this.log.debug("I/O error closing connection", ex); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/IdleConnectionHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/IdleConnectionHandler.java new file mode 100644 index 000000000..cbcd5d04f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/IdleConnectionHandler.java @@ -0,0 +1,181 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpConnection; + +// Currently only used by AbstractConnPool +/** + * A helper class for connection managers to track idle connections. + * + *

    This class is not synchronized.

    + * + * @see ch.boye.httpclientandroidlib.conn.ClientConnectionManager#closeIdleConnections + * + * @since 4.0 + * + * @deprecated (4.1) no longer used + */ +@Deprecated +public class IdleConnectionHandler { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** Holds connections and the time they were added. */ + private final Map connectionToTimes; + + + public IdleConnectionHandler() { + super(); + connectionToTimes = new HashMap(); + } + + /** + * Registers the given connection with this handler. The connection will be held until + * {@link #remove} or {@link #closeIdleConnections} is called. + * + * @param connection the connection to add + * + * @see #remove + */ + public void add(final HttpConnection connection, final long validDuration, final TimeUnit unit) { + + final long timeAdded = System.currentTimeMillis(); + + if (log.isDebugEnabled()) { + log.debug("Adding connection at: " + timeAdded); + } + + connectionToTimes.put(connection, new TimeValues(timeAdded, validDuration, unit)); + } + + /** + * Removes the given connection from the list of connections to be closed when idle. + * This will return true if the connection is still valid, and false + * if the connection should be considered expired and not used. + * + * @param connection + * @return True if the connection is still valid. + */ + public boolean remove(final HttpConnection connection) { + final TimeValues times = connectionToTimes.remove(connection); + if(times == null) { + log.warn("Removing a connection that never existed!"); + return true; + } else { + return System.currentTimeMillis() <= times.timeExpires; + } + } + + /** + * Removes all connections referenced by this handler. + */ + public void removeAll() { + this.connectionToTimes.clear(); + } + + /** + * Closes connections that have been idle for at least the given amount of time. + * + * @param idleTime the minimum idle time, in milliseconds, for connections to be closed + */ + public void closeIdleConnections(final long idleTime) { + + // the latest time for which connections will be closed + final long idleTimeout = System.currentTimeMillis() - idleTime; + + if (log.isDebugEnabled()) { + log.debug("Checking for connections, idle timeout: " + idleTimeout); + } + + for (final Entry entry : connectionToTimes.entrySet()) { + final HttpConnection conn = entry.getKey(); + final TimeValues times = entry.getValue(); + final long connectionTime = times.timeAdded; + if (connectionTime <= idleTimeout) { + if (log.isDebugEnabled()) { + log.debug("Closing idle connection, connection time: " + connectionTime); + } + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + } + + + public void closeExpiredConnections() { + final long now = System.currentTimeMillis(); + if (log.isDebugEnabled()) { + log.debug("Checking for expired connections, now: " + now); + } + + for (final Entry entry : connectionToTimes.entrySet()) { + final HttpConnection conn = entry.getKey(); + final TimeValues times = entry.getValue(); + if(times.timeExpires <= now) { + if (log.isDebugEnabled()) { + log.debug("Closing connection, expired @: " + times.timeExpires); + } + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + } + + private static class TimeValues { + private final long timeAdded; + private final long timeExpires; + + /** + * @param now The current time in milliseconds + * @param validDuration The duration this connection is valid for + * @param validUnit The unit of time the duration is specified in. + */ + TimeValues(final long now, final long validDuration, final TimeUnit validUnit) { + this.timeAdded = now; + if(validDuration > 0) { + this.timeExpires = now + validUnit.toMillis(validDuration); + } else { + this.timeExpires = Long.MAX_VALUE; + } + } + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/InMemoryDnsResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/InMemoryDnsResolver.java new file mode 100644 index 000000000..34f02b11b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/InMemoryDnsResolver.java @@ -0,0 +1,94 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * In-memory {@link DnsResolver} implementation. + * + * @since 4.2 + */ +public class InMemoryDnsResolver implements DnsResolver { + + /** Logger associated to this class. */ + public HttpClientAndroidLog log = new HttpClientAndroidLog(InMemoryDnsResolver.class); + + /** + * In-memory collection that will hold the associations between a host name + * and an array of InetAddress instances. + */ + private final Map dnsMap; + + /** + * Builds a DNS resolver that will resolve the host names against a + * collection held in-memory. + */ + public InMemoryDnsResolver() { + dnsMap = new ConcurrentHashMap(); + } + + /** + * Associates the given array of IP addresses to the given host in this DNS overrider. + * The IP addresses are assumed to be already resolved. + * + * @param host + * The host name to be associated with the given IP. + * @param ips + * array of IP addresses to be resolved by this DNS overrider to the given + * host name. + */ + public void add(final String host, final InetAddress... ips) { + Args.notNull(host, "Host name"); + Args.notNull(ips, "Array of IP addresses"); + dnsMap.put(host, ips); + } + + /** + * {@inheritDoc} + */ + public InetAddress[] resolve(final String host) throws UnknownHostException { + final InetAddress[] resolvedAddresses = dnsMap.get(host); + if (log.isInfoEnabled()) { + log.info("Resolving " + host + " to " + Arrays.deepToString(resolvedAddresses)); + } + if(resolvedAddresses == null){ + throw new UnknownHostException(host + " cannot be resolved"); + } + return resolvedAddresses; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingInputStream.java new file mode 100644 index 000000000..0c117395c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingInputStream.java @@ -0,0 +1,145 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Internal class. + * + * @since 4.3 + */ +@NotThreadSafe +class LoggingInputStream extends InputStream { + + private final InputStream in; + private final Wire wire; + + public LoggingInputStream(final InputStream in, final Wire wire) { + super(); + this.in = in; + this.wire = wire; + } + + @Override + public int read() throws IOException { + try { + final int b = in.read(); + if (b == -1) { + wire.input("end of stream"); + } else { + wire.input(b); + } + return b; + } catch (IOException ex) { + wire.input("[read] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public int read(final byte[] b) throws IOException { + try { + final int bytesRead = in.read(b); + if (bytesRead == -1) { + wire.input("end of stream"); + } else if (bytesRead > 0) { + wire.input(b, 0, bytesRead); + } + return bytesRead; + } catch (IOException ex) { + wire.input("[read] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + try { + final int bytesRead = in.read(b, off, len); + if (bytesRead == -1) { + wire.input("end of stream"); + } else if (bytesRead > 0) { + wire.input(b, off, bytesRead); + } + return bytesRead; + } catch (IOException ex) { + wire.input("[read] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public long skip(final long n) throws IOException { + try { + return super.skip(n); + } catch (IOException ex) { + wire.input("[skip] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public int available() throws IOException { + try { + return in.available(); + } catch (IOException ex) { + wire.input("[available] I/O error : " + ex.getMessage()); + throw ex; + } + } + + @Override + public void mark(final int readlimit) { + super.mark(readlimit); + } + + @Override + public void reset() throws IOException { + super.reset(); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public void close() throws IOException { + try { + in.close(); + } catch (IOException ex) { + wire.input("[close] I/O error: " + ex.getMessage()); + throw ex; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingManagedHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingManagedHttpClientConnection.java new file mode 100644 index 000000000..5b06179c0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingManagedHttpClientConnection.java @@ -0,0 +1,132 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +@NotThreadSafe +class LoggingManagedHttpClientConnection extends DefaultManagedHttpClientConnection { + + public HttpClientAndroidLog log; + private final HttpClientAndroidLog headerlog; + private final Wire wire; + + public LoggingManagedHttpClientConnection( + final String id, + final HttpClientAndroidLog log, + final HttpClientAndroidLog headerlog, + final HttpClientAndroidLog wirelog, + final int buffersize, + final int fragmentSizeHint, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageWriterFactory requestWriterFactory, + final HttpMessageParserFactory responseParserFactory) { + super(id, buffersize, fragmentSizeHint, chardecoder, charencoder, + constraints, incomingContentStrategy, outgoingContentStrategy, + requestWriterFactory, responseParserFactory); + this.log = log; + this.headerlog = headerlog; + this.wire = new Wire(wirelog, id); + } + + @Override + public void close() throws IOException { + if (this.log.isDebugEnabled()) { + this.log.debug(getId() + ": Close connection"); + } + super.close(); + } + + @Override + public void shutdown() throws IOException { + if (this.log.isDebugEnabled()) { + this.log.debug(getId() + ": Shutdown connection"); + } + super.shutdown(); + } + + @Override + protected InputStream getSocketInputStream(final Socket socket) throws IOException { + InputStream in = super.getSocketInputStream(socket); + if (this.wire.enabled()) { + in = new LoggingInputStream(in, this.wire); + } + return in; + } + + @Override + protected OutputStream getSocketOutputStream(final Socket socket) throws IOException { + OutputStream out = super.getSocketOutputStream(socket); + if (this.wire.enabled()) { + out = new LoggingOutputStream(out, this.wire); + } + return out; + } + + @Override + protected void onResponseReceived(final HttpResponse response) { + if (response != null && this.headerlog.isDebugEnabled()) { + this.headerlog.debug(getId() + " << " + response.getStatusLine().toString()); + final Header[] headers = response.getAllHeaders(); + for (final Header header : headers) { + this.headerlog.debug(getId() + " << " + header.toString()); + } + } + } + + @Override + protected void onRequestSubmitted(final HttpRequest request) { + if (request != null && this.headerlog.isDebugEnabled()) { + this.headerlog.debug(getId() + " >> " + request.getRequestLine().toString()); + final Header[] headers = request.getAllHeaders(); + for (final Header header : headers) { + this.headerlog.debug(getId() + " >> " + header.toString()); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingOutputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingOutputStream.java new file mode 100644 index 000000000..86c47b502 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingOutputStream.java @@ -0,0 +1,104 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Internal class. + * + * @since 4.3 + */ +@NotThreadSafe +class LoggingOutputStream extends OutputStream { + + private final OutputStream out; + private final Wire wire; + + public LoggingOutputStream(final OutputStream out, final Wire wire) { + super(); + this.out = out; + this.wire = wire; + } + + @Override + public void write(final int b) throws IOException { + try { + wire.output(b); + } catch (IOException ex) { + wire.output("[write] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void write(final byte[] b) throws IOException { + try { + wire.output(b); + out.write(b); + } catch (IOException ex) { + wire.output("[write] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + try { + wire.output(b, off, len); + out.write(b, off, len); + } catch (IOException ex) { + wire.output("[write] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void flush() throws IOException { + try { + out.flush(); + } catch (IOException ex) { + wire.output("[flush] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void close() throws IOException { + try { + out.close(); + } catch (IOException ex) { + wire.output("[close] I/O error: " + ex.getMessage()); + throw ex; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java new file mode 100644 index 000000000..95617206e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java @@ -0,0 +1,138 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.io.EofSensor; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Logs all data read to the wire LOG. + * + * @since 4.0 + * + * @deprecated (4.3) no longer used. + */ +@Immutable +@Deprecated +public class LoggingSessionInputBuffer implements SessionInputBuffer, EofSensor { + + /** Original session input buffer. */ + private final SessionInputBuffer in; + + private final EofSensor eofSensor; + + /** The wire log to use for writing. */ + private final Wire wire; + + private final String charset; + + /** + * Create an instance that wraps the specified session input buffer. + * @param in The session input buffer. + * @param wire The wire log to use. + * @param charset protocol charset, ASCII if null + */ + public LoggingSessionInputBuffer( + final SessionInputBuffer in, final Wire wire, final String charset) { + super(); + this.in = in; + this.eofSensor = in instanceof EofSensor ? (EofSensor) in : null; + this.wire = wire; + this.charset = charset != null ? charset : Consts.ASCII.name(); + } + + public LoggingSessionInputBuffer(final SessionInputBuffer in, final Wire wire) { + this(in, wire, null); + } + + public boolean isDataAvailable(final int timeout) throws IOException { + return this.in.isDataAvailable(timeout); + } + + public int read(final byte[] b, final int off, final int len) throws IOException { + final int l = this.in.read(b, off, len); + if (this.wire.enabled() && l > 0) { + this.wire.input(b, off, l); + } + return l; + } + + public int read() throws IOException { + final int l = this.in.read(); + if (this.wire.enabled() && l != -1) { + this.wire.input(l); + } + return l; + } + + public int read(final byte[] b) throws IOException { + final int l = this.in.read(b); + if (this.wire.enabled() && l > 0) { + this.wire.input(b, 0, l); + } + return l; + } + + public String readLine() throws IOException { + final String s = this.in.readLine(); + if (this.wire.enabled() && s != null) { + final String tmp = s + "\r\n"; + this.wire.input(tmp.getBytes(this.charset)); + } + return s; + } + + public int readLine(final CharArrayBuffer buffer) throws IOException { + final int l = this.in.readLine(buffer); + if (this.wire.enabled() && l >= 0) { + final int pos = buffer.length() - l; + final String s = new String(buffer.buffer(), pos, l); + final String tmp = s + "\r\n"; + this.wire.input(tmp.getBytes(this.charset)); + } + return l; + } + + public HttpTransportMetrics getMetrics() { + return this.in.getMetrics(); + } + + public boolean isEof() { + if (this.eofSensor != null) { + return this.eofSensor.isEof(); + } else { + return false; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java new file mode 100644 index 000000000..bf1100b4e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java @@ -0,0 +1,118 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Logs all data written to the wire LOG. + * @since 4.0 + * @deprecated (4.3) no longer used. + */ +@Immutable +@Deprecated +public class LoggingSessionOutputBuffer implements SessionOutputBuffer { + + /** Original data transmitter. */ + private final SessionOutputBuffer out; + + /** The wire log to use. */ + private final Wire wire; + + private final String charset; + + /** + * Create an instance that wraps the specified session output buffer. + * @param out The session output buffer. + * @param wire The Wire log to use. + * @param charset protocol charset, ASCII if null + */ + public LoggingSessionOutputBuffer( + final SessionOutputBuffer out, final Wire wire, final String charset) { + super(); + this.out = out; + this.wire = wire; + this.charset = charset != null ? charset : Consts.ASCII.name(); + } + + public LoggingSessionOutputBuffer(final SessionOutputBuffer out, final Wire wire) { + this(out, wire, null); + } + + public void write(final byte[] b, final int off, final int len) throws IOException { + this.out.write(b, off, len); + if (this.wire.enabled()) { + this.wire.output(b, off, len); + } + } + + public void write(final int b) throws IOException { + this.out.write(b); + if (this.wire.enabled()) { + this.wire.output(b); + } + } + + public void write(final byte[] b) throws IOException { + this.out.write(b); + if (this.wire.enabled()) { + this.wire.output(b); + } + } + + public void flush() throws IOException { + this.out.flush(); + } + + public void writeLine(final CharArrayBuffer buffer) throws IOException { + this.out.writeLine(buffer); + if (this.wire.enabled()) { + final String s = new String(buffer.buffer(), 0, buffer.length()); + final String tmp = s + "\r\n"; + this.wire.output(tmp.getBytes(this.charset)); + } + } + + public void writeLine(final String s) throws IOException { + this.out.writeLine(s); + if (this.wire.enabled()) { + final String tmp = s + "\r\n"; + this.wire.output(tmp.getBytes(this.charset)); + } + } + + public HttpTransportMetrics getMetrics() { + return this.out.getMetrics(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedClientConnectionImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedClientConnectionImpl.java new file mode 100644 index 000000000..d1db2d8eb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedClientConnectionImpl.java @@ -0,0 +1,461 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * @since 4.2 + * + * @deprecated (4.3) use {@link ManagedHttpClientConnectionFactory}. + */ +@Deprecated +@NotThreadSafe +class ManagedClientConnectionImpl implements ManagedClientConnection { + + private final ClientConnectionManager manager; + private final ClientConnectionOperator operator; + private volatile HttpPoolEntry poolEntry; + private volatile boolean reusable; + private volatile long duration; + + ManagedClientConnectionImpl( + final ClientConnectionManager manager, + final ClientConnectionOperator operator, + final HttpPoolEntry entry) { + super(); + Args.notNull(manager, "Connection manager"); + Args.notNull(operator, "Connection operator"); + Args.notNull(entry, "HTTP pool entry"); + this.manager = manager; + this.operator = operator; + this.poolEntry = entry; + this.reusable = false; + this.duration = Long.MAX_VALUE; + } + + public String getId() { + return null; + } + + HttpPoolEntry getPoolEntry() { + return this.poolEntry; + } + + HttpPoolEntry detach() { + final HttpPoolEntry local = this.poolEntry; + this.poolEntry = null; + return local; + } + + public ClientConnectionManager getManager() { + return this.manager; + } + + private OperatedClientConnection getConnection() { + final HttpPoolEntry local = this.poolEntry; + if (local == null) { + return null; + } + return local.getConnection(); + } + + private OperatedClientConnection ensureConnection() { + final HttpPoolEntry local = this.poolEntry; + if (local == null) { + throw new ConnectionShutdownException(); + } + return local.getConnection(); + } + + private HttpPoolEntry ensurePoolEntry() { + final HttpPoolEntry local = this.poolEntry; + if (local == null) { + throw new ConnectionShutdownException(); + } + return local; + } + + public void close() throws IOException { + final HttpPoolEntry local = this.poolEntry; + if (local != null) { + final OperatedClientConnection conn = local.getConnection(); + local.getTracker().reset(); + conn.close(); + } + } + + public void shutdown() throws IOException { + final HttpPoolEntry local = this.poolEntry; + if (local != null) { + final OperatedClientConnection conn = local.getConnection(); + local.getTracker().reset(); + conn.shutdown(); + } + } + + public boolean isOpen() { + final OperatedClientConnection conn = getConnection(); + if (conn != null) { + return conn.isOpen(); + } else { + return false; + } + } + + public boolean isStale() { + final OperatedClientConnection conn = getConnection(); + if (conn != null) { + return conn.isStale(); + } else { + return true; + } + } + + public void setSocketTimeout(final int timeout) { + final OperatedClientConnection conn = ensureConnection(); + conn.setSocketTimeout(timeout); + } + + public int getSocketTimeout() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getSocketTimeout(); + } + + public HttpConnectionMetrics getMetrics() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getMetrics(); + } + + public void flush() throws IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.flush(); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + final OperatedClientConnection conn = ensureConnection(); + return conn.isResponseAvailable(timeout); + } + + public void receiveResponseEntity( + final HttpResponse response) throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.receiveResponseEntity(response); + } + + public HttpResponse receiveResponseHeader() throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + return conn.receiveResponseHeader(); + } + + public void sendRequestEntity( + final HttpEntityEnclosingRequest request) throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.sendRequestEntity(request); + } + + public void sendRequestHeader( + final HttpRequest request) throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.sendRequestHeader(request); + } + + public InetAddress getLocalAddress() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getLocalAddress(); + } + + public int getLocalPort() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getLocalPort(); + } + + public InetAddress getRemoteAddress() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getRemoteAddress(); + } + + public int getRemotePort() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getRemotePort(); + } + + public boolean isSecure() { + final OperatedClientConnection conn = ensureConnection(); + return conn.isSecure(); + } + + public void bind(final Socket socket) throws IOException { + throw new UnsupportedOperationException(); + } + + public Socket getSocket() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getSocket(); + } + + public SSLSession getSSLSession() { + final OperatedClientConnection conn = ensureConnection(); + SSLSession result = null; + final Socket sock = conn.getSocket(); + if (sock instanceof SSLSocket) { + result = ((SSLSocket)sock).getSession(); + } + return result; + } + + public Object getAttribute(final String id) { + final OperatedClientConnection conn = ensureConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).getAttribute(id); + } else { + return null; + } + } + + public Object removeAttribute(final String id) { + final OperatedClientConnection conn = ensureConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).removeAttribute(id); + } else { + return null; + } + } + + public void setAttribute(final String id, final Object obj) { + final OperatedClientConnection conn = ensureConnection(); + if (conn instanceof HttpContext) { + ((HttpContext) conn).setAttribute(id, obj); + } + } + + public HttpRoute getRoute() { + final HttpPoolEntry local = ensurePoolEntry(); + return local.getEffectiveRoute(); + } + + public void open( + final HttpRoute route, + final HttpContext context, + final HttpParams params) throws IOException { + Args.notNull(route, "Route"); + Args.notNull(params, "HTTP parameters"); + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(!tracker.isConnected(), "Connection already open"); + conn = this.poolEntry.getConnection(); + } + + final HttpHost proxy = route.getProxyHost(); + this.operator.openConnection( + conn, + (proxy != null) ? proxy : route.getTargetHost(), + route.getLocalAddress(), + context, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + if (proxy == null) { + tracker.connectTarget(conn.isSecure()); + } else { + tracker.connectProxy(proxy, conn.isSecure()); + } + } + } + + public void tunnelTarget( + final boolean secure, final HttpParams params) throws IOException { + Args.notNull(params, "HTTP parameters"); + final HttpHost target; + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(tracker.isConnected(), "Connection not open"); + Asserts.check(!tracker.isTunnelled(), "Connection is already tunnelled"); + target = tracker.getTargetHost(); + conn = this.poolEntry.getConnection(); + } + + conn.update(null, target, secure, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + tracker.tunnelTarget(secure); + } + } + + public void tunnelProxy( + final HttpHost next, final boolean secure, final HttpParams params) throws IOException { + Args.notNull(next, "Next proxy"); + Args.notNull(params, "HTTP parameters"); + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(tracker.isConnected(), "Connection not open"); + conn = this.poolEntry.getConnection(); + } + + conn.update(null, next, secure, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + tracker.tunnelProxy(next, secure); + } + } + + public void layerProtocol( + final HttpContext context, final HttpParams params) throws IOException { + Args.notNull(params, "HTTP parameters"); + final HttpHost target; + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(tracker.isConnected(), "Connection not open"); + Asserts.check(tracker.isTunnelled(), "Protocol layering without a tunnel not supported"); + Asserts.check(!tracker.isLayered(), "Multiple protocol layering not supported"); + target = tracker.getTargetHost(); + conn = this.poolEntry.getConnection(); + } + this.operator.updateSecureConnection(conn, target, context, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + tracker.layerProtocol(conn.isSecure()); + } + } + + public Object getState() { + final HttpPoolEntry local = ensurePoolEntry(); + return local.getState(); + } + + public void setState(final Object state) { + final HttpPoolEntry local = ensurePoolEntry(); + local.setState(state); + } + + public void markReusable() { + this.reusable = true; + } + + public void unmarkReusable() { + this.reusable = false; + } + + public boolean isMarkedReusable() { + return this.reusable; + } + + public void setIdleDuration(final long duration, final TimeUnit unit) { + if(duration > 0) { + this.duration = unit.toMillis(duration); + } else { + this.duration = -1; + } + } + + public void releaseConnection() { + synchronized (this) { + if (this.poolEntry == null) { + return; + } + this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS); + this.poolEntry = null; + } + } + + public void abortConnection() { + synchronized (this) { + if (this.poolEntry == null) { + return; + } + this.reusable = false; + final OperatedClientConnection conn = this.poolEntry.getConnection(); + try { + conn.shutdown(); + } catch (final IOException ignore) { + } + this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS); + this.poolEntry = null; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedHttpClientConnectionFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedHttpClientConnectionFactory.java new file mode 100644 index 000000000..51b3844f9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedHttpClientConnectionFactory.java @@ -0,0 +1,121 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpRequestWriterFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; + +/** + * Factory for {@link ManagedHttpClientConnection} instances. + * @since 4.3 + */ +@Immutable +public class ManagedHttpClientConnectionFactory + implements HttpConnectionFactory { + + private static final AtomicLong COUNTER = new AtomicLong(); + + public static final ManagedHttpClientConnectionFactory INSTANCE = new ManagedHttpClientConnectionFactory(); + + public HttpClientAndroidLog log = new HttpClientAndroidLog(DefaultManagedHttpClientConnection.class); + public HttpClientAndroidLog headerlog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.headers"); + public HttpClientAndroidLog wirelog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.wire"); + + private final HttpMessageWriterFactory requestWriterFactory; + private final HttpMessageParserFactory responseParserFactory; + + public ManagedHttpClientConnectionFactory( + final HttpMessageWriterFactory requestWriterFactory, + final HttpMessageParserFactory responseParserFactory) { + super(); + this.requestWriterFactory = requestWriterFactory != null ? requestWriterFactory : + DefaultHttpRequestWriterFactory.INSTANCE; + this.responseParserFactory = responseParserFactory != null ? responseParserFactory : + DefaultHttpResponseParserFactory.INSTANCE; + } + + public ManagedHttpClientConnectionFactory( + final HttpMessageParserFactory responseParserFactory) { + this(null, responseParserFactory); + } + + public ManagedHttpClientConnectionFactory() { + this(null, null); + } + + public ManagedHttpClientConnection create(final HttpRoute route, final ConnectionConfig config) { + final ConnectionConfig cconfig = config != null ? config : ConnectionConfig.DEFAULT; + CharsetDecoder chardecoder = null; + CharsetEncoder charencoder = null; + final Charset charset = cconfig.getCharset(); + final CodingErrorAction malformedInputAction = cconfig.getMalformedInputAction() != null ? + cconfig.getMalformedInputAction() : CodingErrorAction.REPORT; + final CodingErrorAction unmappableInputAction = cconfig.getUnmappableInputAction() != null ? + cconfig.getUnmappableInputAction() : CodingErrorAction.REPORT; + if (charset != null) { + chardecoder = charset.newDecoder(); + chardecoder.onMalformedInput(malformedInputAction); + chardecoder.onUnmappableCharacter(unmappableInputAction); + charencoder = charset.newEncoder(); + charencoder.onMalformedInput(malformedInputAction); + charencoder.onUnmappableCharacter(unmappableInputAction); + } + final String id = "http-outgoing-" + Long.toString(COUNTER.getAndIncrement()); + return new LoggingManagedHttpClientConnection( + id, + log, + headerlog, + wirelog, + cconfig.getBufferSize(), + cconfig.getFragmentSizeHint(), + chardecoder, + charencoder, + cconfig.getMessageConstraints(), + null, + null, + requestWriterFactory, + responseParserFactory); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingClientConnectionManager.java new file mode 100644 index 000000000..f9685b81e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingClientConnectionManager.java @@ -0,0 +1,328 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.pool.ConnPoolControl; +import ch.boye.httpclientandroidlib.pool.PoolStats; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Manages a pool of {@link ch.boye.httpclientandroidlib.conn.OperatedClientConnection} + * and is able to service connection requests from multiple execution threads. + * Connections are pooled on a per route basis. A request for a route which + * already the manager has persistent connections for available in the pool + * will be services by leasing a connection from the pool rather than + * creating a brand new connection. + *

    + * PoolingConnectionManager maintains a maximum limit of connection on + * a per route basis and in total. Per default this implementation will + * create no more than than 2 concurrent connections per given route + * and no more 20 connections in total. For many real-world applications + * these limits may prove too constraining, especially if they use HTTP + * as a transport protocol for their services. Connection limits, however, + * can be adjusted using HTTP parameters. + * + * @since 4.2 + * + * @deprecated (4.3) use {@link PoolingHttpClientConnectionManager}. + */ +@Deprecated +@ThreadSafe +public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final SchemeRegistry schemeRegistry; + + private final HttpConnPool pool; + + private final ClientConnectionOperator operator; + + /** the custom-configured DNS lookup mechanism. */ + private final DnsResolver dnsResolver; + + public PoolingClientConnectionManager(final SchemeRegistry schreg) { + this(schreg, -1, TimeUnit.MILLISECONDS); + } + + public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) { + this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver); + } + + public PoolingClientConnectionManager() { + this(SchemeRegistryFactory.createDefault()); + } + + public PoolingClientConnectionManager( + final SchemeRegistry schemeRegistry, + final long timeToLive, final TimeUnit tunit) { + this(schemeRegistry, timeToLive, tunit, new SystemDefaultDnsResolver()); + } + + public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry, + final long timeToLive, final TimeUnit tunit, + final DnsResolver dnsResolver) { + super(); + Args.notNull(schemeRegistry, "Scheme registry"); + Args.notNull(dnsResolver, "DNS resolver"); + this.schemeRegistry = schemeRegistry; + this.dnsResolver = dnsResolver; + this.operator = createConnectionOperator(schemeRegistry); + this.pool = new HttpConnPool(this.log, this.operator, 2, 20, timeToLive, tunit); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + /** + * Hook for creating the connection operator. + * It is called by the constructor. + * Derived classes can override this method to change the + * instantiation of the operator. + * The default implementation here instantiates + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. + * + * @param schreg the scheme registry. + * + * @return the connection operator to use + */ + protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) { + return new DefaultClientConnectionOperator(schreg, this.dnsResolver); + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + private String format(final HttpRoute route, final Object state) { + final StringBuilder buf = new StringBuilder(); + buf.append("[route: ").append(route).append("]"); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + private String formatStats(final HttpRoute route) { + final StringBuilder buf = new StringBuilder(); + final PoolStats totals = this.pool.getTotalStats(); + final PoolStats stats = this.pool.getStats(route); + buf.append("[total kept alive: ").append(totals.getAvailable()).append("; "); + buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable()); + buf.append(" of ").append(stats.getMax()).append("; "); + buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); + buf.append(" of ").append(totals.getMax()).append("]"); + return buf.toString(); + } + + private String format(final HttpPoolEntry entry) { + final StringBuilder buf = new StringBuilder(); + buf.append("[id: ").append(entry.getId()).append("]"); + buf.append("[route: ").append(entry.getRoute()).append("]"); + final Object state = entry.getState(); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + public ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + Args.notNull(route, "HTTP route"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection request: " + format(route, state) + formatStats(route)); + } + final Future future = this.pool.lease(route, state); + + return new ClientConnectionRequest() { + + public void abortRequest() { + future.cancel(true); + } + + public ManagedClientConnection getConnection( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + return leaseConnection(future, timeout, tunit); + } + + }; + + } + + ManagedClientConnection leaseConnection( + final Future future, + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + final HttpPoolEntry entry; + try { + entry = future.get(timeout, tunit); + if (entry == null || future.isCancelled()) { + throw new InterruptedException(); + } + Asserts.check(entry.getConnection() != null, "Pool entry with no connection"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); + } + return new ManagedClientConnectionImpl(this, this.operator, entry); + } catch (final ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause == null) { + cause = ex; + } + this.log.error("Unexpected exception leasing connection from pool", cause); + // Should never happen + throw new InterruptedException(); + } catch (final TimeoutException ex) { + throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); + } + } + + public void releaseConnection( + final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) { + + Args.check(conn instanceof ManagedClientConnectionImpl, "Connection class mismatch, " + + "connection not obtained from this manager"); + final ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn; + Asserts.check(managedConn.getManager() == this, "Connection not obtained from this manager"); + synchronized (managedConn) { + final HttpPoolEntry entry = managedConn.detach(); + if (entry == null) { + return; + } + try { + if (managedConn.isOpen() && !managedConn.isMarkedReusable()) { + try { + managedConn.shutdown(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception shutting down released connection", iox); + } + } + } + // Only reusable connections can be kept alive + if (managedConn.isMarkedReusable()) { + entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + keepalive + " " + tunit; + } else { + s = "indefinitely"; + } + this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + } + } + } finally { + this.pool.release(entry, managedConn.isMarkedReusable()); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + } + } + } + + public void shutdown() { + this.log.debug("Connection manager is shutting down"); + try { + this.pool.shutdown(); + } catch (final IOException ex) { + this.log.debug("I/O exception shutting down connection manager", ex); + } + this.log.debug("Connection manager shut down"); + } + + public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) { + if (this.log.isDebugEnabled()) { + this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + } + this.pool.closeIdle(idleTimeout, tunit); + } + + public void closeExpiredConnections() { + this.log.debug("Closing expired connections"); + this.pool.closeExpired(); + } + + public int getMaxTotal() { + return this.pool.getMaxTotal(); + } + + public void setMaxTotal(final int max) { + this.pool.setMaxTotal(max); + } + + public int getDefaultMaxPerRoute() { + return this.pool.getDefaultMaxPerRoute(); + } + + public void setDefaultMaxPerRoute(final int max) { + this.pool.setDefaultMaxPerRoute(max); + } + + public int getMaxPerRoute(final HttpRoute route) { + return this.pool.getMaxPerRoute(route); + } + + public void setMaxPerRoute(final HttpRoute route, final int max) { + this.pool.setMaxPerRoute(route, max); + } + + public PoolStats getTotalStats() { + return this.pool.getTotalStats(); + } + + public PoolStats getStats(final HttpRoute route) { + return this.pool.getStats(route); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingHttpClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingHttpClientConnectionManager.java new file mode 100644 index 000000000..a2606b4e6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingHttpClientConnectionManager.java @@ -0,0 +1,516 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.config.Registry; +import ch.boye.httpclientandroidlib.config.RegistryBuilder; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.ConnectionRequest; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.SSLConnectionSocketFactory; +import ch.boye.httpclientandroidlib.pool.ConnFactory; +import ch.boye.httpclientandroidlib.pool.ConnPoolControl; +import ch.boye.httpclientandroidlib.pool.PoolStats; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * ClientConnectionPoolManager maintains a pool of + * {@link HttpClientConnection}s and is able to service connection requests + * from multiple execution threads. Connections are pooled on a per route + * basis. A request for a route which already the manager has persistent + * connections for available in the pool will be services by leasing + * a connection from the pool rather than creating a brand new connection. + *

    + * ClientConnectionPoolManager maintains a maximum limit of connection + * on a per route basis and in total. Per default this implementation will + * create no more than than 2 concurrent connections per given route + * and no more 20 connections in total. For many real-world applications + * these limits may prove too constraining, especially if they use HTTP + * as a transport protocol for their services. Connection limits, however, + * can be adjusted using {@link ConnPoolControl} methods. + * + * @since 4.3 + */ +@ThreadSafe +public class PoolingHttpClientConnectionManager + implements HttpClientConnectionManager, ConnPoolControl, Closeable { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final ConfigData configData; + private final CPool pool; + private final HttpClientConnectionOperator connectionOperator; + private final AtomicBoolean isShutDown; + + private static Registry getDefaultRegistry() { + return RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", SSLConnectionSocketFactory.getSocketFactory()) + .build(); + } + + public PoolingHttpClientConnectionManager() { + this(getDefaultRegistry()); + } + + public PoolingHttpClientConnectionManager(final long timeToLive, final TimeUnit tunit) { + this(getDefaultRegistry(), null, null ,null, timeToLive, tunit); + } + + public PoolingHttpClientConnectionManager( + final Registry socketFactoryRegistry) { + this(socketFactoryRegistry, null, null); + } + + public PoolingHttpClientConnectionManager( + final Registry socketFactoryRegistry, + final DnsResolver dnsResolver) { + this(socketFactoryRegistry, null, dnsResolver); + } + + public PoolingHttpClientConnectionManager( + final Registry socketFactoryRegistry, + final HttpConnectionFactory connFactory) { + this(socketFactoryRegistry, connFactory, null); + } + + public PoolingHttpClientConnectionManager( + final HttpConnectionFactory connFactory) { + this(getDefaultRegistry(), connFactory, null); + } + + public PoolingHttpClientConnectionManager( + final Registry socketFactoryRegistry, + final HttpConnectionFactory connFactory, + final DnsResolver dnsResolver) { + this(socketFactoryRegistry, connFactory, null, dnsResolver, -1, TimeUnit.MILLISECONDS); + } + + public PoolingHttpClientConnectionManager( + final Registry socketFactoryRegistry, + final HttpConnectionFactory connFactory, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver, + final long timeToLive, final TimeUnit tunit) { + super(); + this.configData = new ConfigData(); + this.pool = new CPool( + new InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, tunit); + this.connectionOperator = new HttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver); + this.isShutDown = new AtomicBoolean(false); + } + + PoolingHttpClientConnectionManager( + final CPool pool, + final Lookup socketFactoryRegistry, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver) { + super(); + this.configData = new ConfigData(); + this.pool = pool; + this.connectionOperator = new HttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver); + this.isShutDown = new AtomicBoolean(false); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + public void close() { + shutdown(); + } + + private String format(final HttpRoute route, final Object state) { + final StringBuilder buf = new StringBuilder(); + buf.append("[route: ").append(route).append("]"); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + private String formatStats(final HttpRoute route) { + final StringBuilder buf = new StringBuilder(); + final PoolStats totals = this.pool.getTotalStats(); + final PoolStats stats = this.pool.getStats(route); + buf.append("[total kept alive: ").append(totals.getAvailable()).append("; "); + buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable()); + buf.append(" of ").append(stats.getMax()).append("; "); + buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); + buf.append(" of ").append(totals.getMax()).append("]"); + return buf.toString(); + } + + private String format(final CPoolEntry entry) { + final StringBuilder buf = new StringBuilder(); + buf.append("[id: ").append(entry.getId()).append("]"); + buf.append("[route: ").append(entry.getRoute()).append("]"); + final Object state = entry.getState(); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + public ConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + Args.notNull(route, "HTTP route"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection request: " + format(route, state) + formatStats(route)); + } + final Future future = this.pool.lease(route, state, null); + return new ConnectionRequest() { + + public boolean cancel() { + return future.cancel(true); + } + + public HttpClientConnection get( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { + return leaseConnection(future, timeout, tunit); + } + + }; + + } + + protected HttpClientConnection leaseConnection( + final Future future, + final long timeout, + final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { + final CPoolEntry entry; + try { + entry = future.get(timeout, tunit); + if (entry == null || future.isCancelled()) { + throw new InterruptedException(); + } + Asserts.check(entry.getConnection() != null, "Pool entry with no connection"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); + } + return CPoolProxy.newProxy(entry); + } catch (final TimeoutException ex) { + throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); + } + } + + public void releaseConnection( + final HttpClientConnection managedConn, + final Object state, + final long keepalive, final TimeUnit tunit) { + Args.notNull(managedConn, "Managed connection"); + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.detach(managedConn); + if (entry == null) { + return; + } + final ManagedHttpClientConnection conn = entry.getConnection(); + try { + if (conn.isOpen()) { + entry.setState(state); + entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + (double) keepalive / 1000 + " seconds"; + } else { + s = "indefinitely"; + } + this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + } + } + } finally { + this.pool.release(entry, conn.isOpen() && entry.isRouteComplete()); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + } + } + } + } + + public void connect( + final HttpClientConnection managedConn, + final HttpRoute route, + final int connectTimeout, + final HttpContext context) throws IOException { + Args.notNull(managedConn, "Managed Connection"); + Args.notNull(route, "HTTP route"); + final ManagedHttpClientConnection conn; + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + final HttpHost host; + if (route.getProxyHost() != null) { + host = route.getProxyHost(); + } else { + host = route.getTargetHost(); + } + final InetSocketAddress localAddress = route.getLocalSocketAddress(); + SocketConfig socketConfig = this.configData.getSocketConfig(host); + if (socketConfig == null) { + socketConfig = this.configData.getDefaultSocketConfig(); + } + if (socketConfig == null) { + socketConfig = SocketConfig.DEFAULT; + } + this.connectionOperator.connect( + conn, host, localAddress, connectTimeout, socketConfig, context); + } + + public void upgrade( + final HttpClientConnection managedConn, + final HttpRoute route, + final HttpContext context) throws IOException { + Args.notNull(managedConn, "Managed Connection"); + Args.notNull(route, "HTTP route"); + final ManagedHttpClientConnection conn; + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + this.connectionOperator.upgrade(conn, route.getTargetHost(), context); + } + + public void routeComplete( + final HttpClientConnection managedConn, + final HttpRoute route, + final HttpContext context) throws IOException { + Args.notNull(managedConn, "Managed Connection"); + Args.notNull(route, "HTTP route"); + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + entry.markRouteComplete(); + } + } + + public void shutdown() { + if (this.isShutDown.compareAndSet(false, true)) { + this.log.debug("Connection manager is shutting down"); + try { + this.pool.shutdown(); + } catch (final IOException ex) { + this.log.debug("I/O exception shutting down connection manager", ex); + } + this.log.debug("Connection manager shut down"); + } + } + + public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) { + if (this.log.isDebugEnabled()) { + this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + } + this.pool.closeIdle(idleTimeout, tunit); + } + + public void closeExpiredConnections() { + this.log.debug("Closing expired connections"); + this.pool.closeExpired(); + } + + public int getMaxTotal() { + return this.pool.getMaxTotal(); + } + + public void setMaxTotal(final int max) { + this.pool.setMaxTotal(max); + } + + public int getDefaultMaxPerRoute() { + return this.pool.getDefaultMaxPerRoute(); + } + + public void setDefaultMaxPerRoute(final int max) { + this.pool.setDefaultMaxPerRoute(max); + } + + public int getMaxPerRoute(final HttpRoute route) { + return this.pool.getMaxPerRoute(route); + } + + public void setMaxPerRoute(final HttpRoute route, final int max) { + this.pool.setMaxPerRoute(route, max); + } + + public PoolStats getTotalStats() { + return this.pool.getTotalStats(); + } + + public PoolStats getStats(final HttpRoute route) { + return this.pool.getStats(route); + } + + public SocketConfig getDefaultSocketConfig() { + return this.configData.getDefaultSocketConfig(); + } + + public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) { + this.configData.setDefaultSocketConfig(defaultSocketConfig); + } + + public ConnectionConfig getDefaultConnectionConfig() { + return this.configData.getDefaultConnectionConfig(); + } + + public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) { + this.configData.setDefaultConnectionConfig(defaultConnectionConfig); + } + + public SocketConfig getSocketConfig(final HttpHost host) { + return this.configData.getSocketConfig(host); + } + + public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) { + this.configData.setSocketConfig(host, socketConfig); + } + + public ConnectionConfig getConnectionConfig(final HttpHost host) { + return this.configData.getConnectionConfig(host); + } + + public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { + this.configData.setConnectionConfig(host, connectionConfig); + } + + static class ConfigData { + + private final Map socketConfigMap; + private final Map connectionConfigMap; + private volatile SocketConfig defaultSocketConfig; + private volatile ConnectionConfig defaultConnectionConfig; + + ConfigData() { + super(); + this.socketConfigMap = new ConcurrentHashMap(); + this.connectionConfigMap = new ConcurrentHashMap(); + } + + public SocketConfig getDefaultSocketConfig() { + return this.defaultSocketConfig; + } + + public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) { + this.defaultSocketConfig = defaultSocketConfig; + } + + public ConnectionConfig getDefaultConnectionConfig() { + return this.defaultConnectionConfig; + } + + public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) { + this.defaultConnectionConfig = defaultConnectionConfig; + } + + public SocketConfig getSocketConfig(final HttpHost host) { + return this.socketConfigMap.get(host); + } + + public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) { + this.socketConfigMap.put(host, socketConfig); + } + + public ConnectionConfig getConnectionConfig(final HttpHost host) { + return this.connectionConfigMap.get(host); + } + + public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { + this.connectionConfigMap.put(host, connectionConfig); + } + + } + + static class InternalConnectionFactory implements ConnFactory { + + private final ConfigData configData; + private final HttpConnectionFactory connFactory; + + InternalConnectionFactory( + final ConfigData configData, + final HttpConnectionFactory connFactory) { + super(); + this.configData = configData != null ? configData : new ConfigData(); + this.connFactory = connFactory != null ? connFactory : + ManagedHttpClientConnectionFactory.INSTANCE; + } + + public ManagedHttpClientConnection create(final HttpRoute route) throws IOException { + ConnectionConfig config = null; + if (route.getProxyHost() != null) { + config = this.configData.getConnectionConfig(route.getProxyHost()); + } + if (config == null) { + config = this.configData.getConnectionConfig(route.getTargetHost()); + } + if (config == null) { + config = this.configData.getDefaultConnectionConfig(); + } + if (config == null) { + config = ConnectionConfig.DEFAULT; + } + return this.connFactory.create(route, config); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java new file mode 100644 index 000000000..8c68e5e18 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java @@ -0,0 +1,279 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.params.ConnRouteParams; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + + +/** + * Default implementation of an {@link HttpRoutePlanner}. + * This implementation is based on {@link java.net.ProxySelector}. + * By default, it will pick up the proxy settings of the JVM, either + * from system properties or from the browser running the application. + * Additionally, it interprets some + * {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames parameters}, + * though not the {@link + * ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}
    • + *
    • {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.3) use {@link SystemDefaultRoutePlanner} + */ +@NotThreadSafe // e.g [gs]etProxySelector() +@Deprecated +public class ProxySelectorRoutePlanner implements HttpRoutePlanner { + + /** The scheme registry. */ + protected final SchemeRegistry schemeRegistry; // @ThreadSafe + + /** The proxy selector to use, or null for system default. */ + protected ProxySelector proxySelector; + + /** + * Creates a new proxy selector route planner. + * + * @param schreg the scheme registry + * @param prosel the proxy selector, or + * null for the system default + */ + public ProxySelectorRoutePlanner(final SchemeRegistry schreg, + final ProxySelector prosel) { + Args.notNull(schreg, "SchemeRegistry"); + schemeRegistry = schreg; + proxySelector = prosel; + } + + /** + * Obtains the proxy selector to use. + * + * @return the proxy selector, or null for the system default + */ + public ProxySelector getProxySelector() { + return this.proxySelector; + } + + /** + * Sets the proxy selector to use. + * + * @param prosel the proxy selector, or + * null to use the system default + */ + public void setProxySelector(final ProxySelector prosel) { + this.proxySelector = prosel; + } + + public HttpRoute determineRoute(final HttpHost target, + final HttpRequest request, + final HttpContext context) + throws HttpException { + + Args.notNull(request, "HTTP request"); + + // If we have a forced route, we can do without a target. + HttpRoute route = + ConnRouteParams.getForcedRoute(request.getParams()); + if (route != null) { + return route; + } + + // If we get here, there is no forced route. + // So we need a target to compute a route. + + Asserts.notNull(target, "Target host"); + + final InetAddress local = + ConnRouteParams.getLocalAddress(request.getParams()); + final HttpHost proxy = determineProxy(target, request, context); + + final Scheme schm = + this.schemeRegistry.getScheme(target.getSchemeName()); + // as it is typically used for TLS/SSL, we assume that + // a layered scheme implies a secure connection + final boolean secure = schm.isLayered(); + + if (proxy == null) { + route = new HttpRoute(target, local, secure); + } else { + route = new HttpRoute(target, local, proxy, secure); + } + return route; + } + + /** + * Determines a proxy for the given target. + * + * @param target the planned target, never null + * @param request the request to be sent, never null + * @param context the context, or null + * + * @return the proxy to use, or null for a direct route + * + * @throws HttpException + * in case of system proxy settings that cannot be handled + */ + protected HttpHost determineProxy(final HttpHost target, + final HttpRequest request, + final HttpContext context) + throws HttpException { + + // the proxy selector can be 'unset', so we better deal with null here + ProxySelector psel = this.proxySelector; + if (psel == null) { + psel = ProxySelector.getDefault(); + } + if (psel == null) { + return null; + } + + URI targetURI = null; + try { + targetURI = new URI(target.toURI()); + } catch (final URISyntaxException usx) { + throw new HttpException + ("Cannot convert host to URI: " + target, usx); + } + final List proxies = psel.select(targetURI); + + final Proxy p = chooseProxy(proxies, target, request, context); + + HttpHost result = null; + if (p.type() == Proxy.Type.HTTP) { + // convert the socket address to an HttpHost + if (!(p.address() instanceof InetSocketAddress)) { + throw new HttpException + ("Unable to handle non-Inet proxy address: "+p.address()); + } + final InetSocketAddress isa = (InetSocketAddress) p.address(); + // assume default scheme (http) + result = new HttpHost(getHost(isa), isa.getPort()); + } + + return result; + } + + /** + * Obtains a host from an {@link InetSocketAddress}. + * + * @param isa the socket address + * + * @return a host string, either as a symbolic name or + * as a literal IP address string + *
    + * (TODO: determine format for IPv6 addresses, with or without [brackets]) + */ + protected String getHost(final InetSocketAddress isa) { + + //@@@ Will this work with literal IPv6 addresses, or do we + //@@@ need to wrap these in [] for the string representation? + //@@@ Having it in this method at least allows for easy workarounds. + return isa.isUnresolved() ? + isa.getHostName() : isa.getAddress().getHostAddress(); + + } + + /** + * Chooses a proxy from a list of available proxies. + * The default implementation just picks the first non-SOCKS proxy + * from the list. If there are only SOCKS proxies, + * {@link Proxy#NO_PROXY Proxy.NO_PROXY} is returned. + * Derived classes may implement more advanced strategies, + * such as proxy rotation if there are multiple options. + * + * @param proxies the list of proxies to choose from, + * never null or empty + * @param target the planned target, never null + * @param request the request to be sent, never null + * @param context the context, or null + * + * @return a proxy type + */ + protected Proxy chooseProxy(final List proxies, + final HttpHost target, + final HttpRequest request, + final HttpContext context) { + Args.notEmpty(proxies, "List of proxies"); + + Proxy result = null; + + // check the list for one we can use + for (int i=0; (result == null) && (i < proxies.size()); i++) { + + final Proxy p = proxies.get(i); + switch (p.type()) { + + case DIRECT: + case HTTP: + result = p; + break; + + case SOCKS: + // SOCKS hosts are not handled on the route level. + // The socket may make use of the SOCKS host though. + break; + } + } + + if (result == null) { + //@@@ log as warning or info that only a socks proxy is available? + // result can only be null if all proxies are socks proxies + // socks proxies are not handled on the route planning level + result = Proxy.NO_PROXY; + } + + return result; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SchemeRegistryFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SchemeRegistryFactory.java new file mode 100644 index 000000000..0a17993d1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SchemeRegistryFactory.java @@ -0,0 +1,90 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.scheme.PlainSocketFactory; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.conn.ssl.SSLSocketFactory; + +/** + * @since 4.1 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder}. + */ +@ThreadSafe +@Deprecated +public final class SchemeRegistryFactory { + + /** + * Initializes default scheme registry based on JSSE defaults. System properties will + * not be taken into consideration. + */ + public static SchemeRegistry createDefault() { + final SchemeRegistry registry = new SchemeRegistry(); + registry.register( + new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); + registry.register( + new Scheme("https", 443, SSLSocketFactory.getSocketFactory())); + return registry; + } + + /** + * Initializes default scheme registry using system properties as described in + * + * "JavaTM Secure Socket Extension (JSSE) Reference Guide for the JavaTM 2 Platform + * Standard Edition 5 + *

    + * The following system properties are taken into account by this method: + *

      + *
    • ssl.TrustManagerFactory.algorithm
    • + *
    • javax.net.ssl.trustStoreType
    • + *
    • javax.net.ssl.trustStore
    • + *
    • javax.net.ssl.trustStoreProvider
    • + *
    • javax.net.ssl.trustStorePassword
    • + *
    • java.home
    • + *
    • ssl.KeyManagerFactory.algorithm
    • + *
    • javax.net.ssl.keyStoreType
    • + *
    • javax.net.ssl.keyStore
    • + *
    • javax.net.ssl.keyStoreProvider
    • + *
    • javax.net.ssl.keyStorePassword
    • + *
    + *

    + * + * @since 4.2 + */ + public static SchemeRegistry createSystemDefault() { + final SchemeRegistry registry = new SchemeRegistry(); + registry.register( + new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); + registry.register( + new Scheme("https", 443, SSLSocketFactory.getSystemSocketFactory())); + return registry; + } +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java new file mode 100644 index 000000000..c2b6c8799 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java @@ -0,0 +1,427 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A connection manager for a single connection. This connection manager + * maintains only one active connection at a time. Even though this class + * is thread-safe it ought to be used by one execution thread only. + *

    + * SingleClientConnManager will make an effort to reuse the connection + * for subsequent requests with the same {@link HttpRoute route}. + * It will, however, close the existing connection and open it + * for the given route, if the route of the persistent connection does + * not match that of the connection request. If the connection has been + * already been allocated {@link IllegalStateException} is thrown. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link BasicClientConnectionManager} + */ +@ThreadSafe +@Deprecated +public class SingleClientConnManager implements ClientConnectionManager { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** The message to be logged on multiple allocation. */ + public final static String MISUSE_MESSAGE = + "Invalid use of SingleClientConnManager: connection still allocated.\n" + + "Make sure to release the connection before allocating another one."; + + /** The schemes supported by this connection manager. */ + protected final SchemeRegistry schemeRegistry; + + /** The operator for opening and updating connections. */ + protected final ClientConnectionOperator connOperator; + + /** Whether the connection should be shut down on release. */ + protected final boolean alwaysShutDown; + + /** The one and only entry in this pool. */ + @GuardedBy("this") + protected volatile PoolEntry uniquePoolEntry; + + /** The currently issued managed connection, if any. */ + @GuardedBy("this") + protected volatile ConnAdapter managedConn; + + /** The time of the last connection release, or -1. */ + @GuardedBy("this") + protected volatile long lastReleaseTime; + + /** The time the last released connection expires and shouldn't be reused. */ + @GuardedBy("this") + protected volatile long connectionExpiresTime; + + /** Indicates whether this connection manager is shut down. */ + protected volatile boolean isShutDown; + + /** + * Creates a new simple connection manager. + * + * @param params the parameters for this manager + * @param schreg the scheme registry + * + * @deprecated (4.1) use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)} + */ + @Deprecated + public SingleClientConnManager(final HttpParams params, + final SchemeRegistry schreg) { + this(schreg); + } + /** + * Creates a new simple connection manager. + * + * @param schreg the scheme registry + */ + public SingleClientConnManager(final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + this.schemeRegistry = schreg; + this.connOperator = createConnectionOperator(schreg); + this.uniquePoolEntry = new PoolEntry(); + this.managedConn = null; + this.lastReleaseTime = -1L; + this.alwaysShutDown = false; //@@@ from params? as argument? + this.isShutDown = false; + } + + /** + * @since 4.1 + */ + public SingleClientConnManager() { + this(SchemeRegistryFactory.createDefault()); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { // Make sure we call overridden method even if shutdown barfs + super.finalize(); + } + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + /** + * Hook for creating the connection operator. + * It is called by the constructor. + * Derived classes can override this method to change the + * instantiation of the operator. + * The default implementation here instantiates + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. + * + * @param schreg the scheme registry to use, or null + * + * @return the connection operator to use + */ + protected ClientConnectionOperator + createConnectionOperator(final SchemeRegistry schreg) { + return new DefaultClientConnectionOperator(schreg); + } + + /** + * Asserts that this manager is not shut down. + * + * @throws IllegalStateException if this manager is shut down + */ + protected final void assertStillUp() throws IllegalStateException { + Asserts.check(!this.isShutDown, "Manager is shut down"); + } + + public final ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + + return new ClientConnectionRequest() { + + public void abortRequest() { + // Nothing to abort, since requests are immediate. + } + + public ManagedClientConnection getConnection( + final long timeout, final TimeUnit tunit) { + return SingleClientConnManager.this.getConnection( + route, state); + } + + }; + } + + /** + * Obtains a connection. + * + * @param route where the connection should point to + * + * @return a connection that can be used to communicate + * along the given route + */ + public ManagedClientConnection getConnection(final HttpRoute route, final Object state) { + Args.notNull(route, "Route"); + assertStillUp(); + + if (log.isDebugEnabled()) { + log.debug("Get connection for route " + route); + } + + synchronized (this) { + + Asserts.check(managedConn == null, MISUSE_MESSAGE); + + // check re-usability of the connection + boolean recreate = false; + boolean shutdown = false; + + // Kill the connection if it expired. + closeExpiredConnections(); + + if (uniquePoolEntry.connection.isOpen()) { + final RouteTracker tracker = uniquePoolEntry.tracker; + shutdown = (tracker == null || // can happen if method is aborted + !tracker.toRoute().equals(route)); + } else { + // If the connection is not open, create a new PoolEntry, + // as the connection may have been marked not reusable, + // due to aborts -- and the PoolEntry should not be reused + // either. There's no harm in recreating an entry if + // the connection is closed. + recreate = true; + } + + if (shutdown) { + recreate = true; + try { + uniquePoolEntry.shutdown(); + } catch (final IOException iox) { + log.debug("Problem shutting down connection.", iox); + } + } + + if (recreate) { + uniquePoolEntry = new PoolEntry(); + } + + managedConn = new ConnAdapter(uniquePoolEntry, route); + + return managedConn; + } + } + + public void releaseConnection( + final ManagedClientConnection conn, + final long validDuration, final TimeUnit timeUnit) { + Args.check(conn instanceof ConnAdapter, "Connection class mismatch, " + + "connection not obtained from this manager"); + assertStillUp(); + + if (log.isDebugEnabled()) { + log.debug("Releasing connection " + conn); + } + + final ConnAdapter sca = (ConnAdapter) conn; + synchronized (sca) { + if (sca.poolEntry == null) + { + return; // already released + } + final ClientConnectionManager manager = sca.getManager(); + Asserts.check(manager == this, "Connection not obtained from this manager"); + try { + // make sure that the response has been read completely + if (sca.isOpen() && (this.alwaysShutDown || + !sca.isMarkedReusable()) + ) { + if (log.isDebugEnabled()) { + log.debug + ("Released connection open but not reusable."); + } + + // make sure this connection will not be re-used + // we might have gotten here because of a shutdown trigger + // shutdown of the adapter also clears the tracked route + sca.shutdown(); + } + } catch (final IOException iox) { + if (log.isDebugEnabled()) { + log.debug("Exception shutting down released connection.", + iox); + } + } finally { + sca.detach(); + synchronized (this) { + managedConn = null; + lastReleaseTime = System.currentTimeMillis(); + if(validDuration > 0) { + connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime; + } else { + connectionExpiresTime = Long.MAX_VALUE; + } + } + } + } + } + + public void closeExpiredConnections() { + final long time = connectionExpiresTime; + if (System.currentTimeMillis() >= time) { + closeIdleConnections(0, TimeUnit.MILLISECONDS); + } + } + + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + assertStillUp(); + + // idletime can be 0 or negative, no problem there + Args.notNull(tunit, "Time unit"); + + synchronized (this) { + if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) { + final long cutoff = + System.currentTimeMillis() - tunit.toMillis(idletime); + if (lastReleaseTime <= cutoff) { + try { + uniquePoolEntry.close(); + } catch (final IOException iox) { + // ignore + log.debug("Problem closing idle connection.", iox); + } + } + } + } + } + + public void shutdown() { + this.isShutDown = true; + synchronized (this) { + try { + if (uniquePoolEntry != null) { + uniquePoolEntry.shutdown(); + } + } catch (final IOException iox) { + // ignore + log.debug("Problem while shutting down manager.", iox); + } finally { + uniquePoolEntry = null; + managedConn = null; + } + } + } + + protected void revokeConnection() { + final ConnAdapter conn = managedConn; + if (conn == null) { + return; + } + conn.detach(); + + synchronized (this) { + try { + uniquePoolEntry.shutdown(); + } catch (final IOException iox) { + // ignore + log.debug("Problem while shutting down connection.", iox); + } + } + } + + /** + * The pool entry for this connection manager. + */ + protected class PoolEntry extends AbstractPoolEntry { + + /** + * Creates a new pool entry. + * + */ + protected PoolEntry() { + super(SingleClientConnManager.this.connOperator, null); + } + + /** + * Closes the connection in this pool entry. + */ + protected void close() throws IOException { + shutdownEntry(); + if (connection.isOpen()) { + connection.close(); + } + } + + /** + * Shuts down the connection in this pool entry. + */ + protected void shutdown() throws IOException { + shutdownEntry(); + if (connection.isOpen()) { + connection.shutdown(); + } + } + + } + + /** + * The connection adapter used by this manager. + */ + protected class ConnAdapter extends AbstractPooledConnAdapter { + + /** + * Creates a new connection adapter. + * + * @param entry the pool entry for the connection being wrapped + * @param route the planned route for this connection + */ + protected ConnAdapter(final PoolEntry entry, final HttpRoute route) { + super(SingleClientConnManager.this, entry); + markReusable(); + entry.route = route; + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultDnsResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultDnsResolver.java new file mode 100644 index 000000000..bb6b7c60d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultDnsResolver.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.conn.DnsResolver; + +/** + * DNS resolver that uses the default OS implementation for resolving host names. + * + * @since 4.2 + */ +public class SystemDefaultDnsResolver implements DnsResolver { + + public static final SystemDefaultDnsResolver INSTANCE = new SystemDefaultDnsResolver(); + + public InetAddress[] resolve(final String host) throws UnknownHostException { + return InetAddress.getAllByName(host); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultRoutePlanner.java new file mode 100644 index 000000000..27ea2a3d0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultRoutePlanner.java @@ -0,0 +1,132 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.conn; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner} implementation + * based on {@link ProxySelector}. By default, this class will pick up + * the proxy settings of the JVM, either from system properties + * or from the browser running the application. + * + * @since 4.3 + */ +@Immutable +public class SystemDefaultRoutePlanner extends DefaultRoutePlanner { + + private final ProxySelector proxySelector; + + public SystemDefaultRoutePlanner( + final SchemePortResolver schemePortResolver, + final ProxySelector proxySelector) { + super(schemePortResolver); + this.proxySelector = proxySelector != null ? proxySelector : ProxySelector.getDefault(); + } + + public SystemDefaultRoutePlanner(final ProxySelector proxySelector) { + this(null, proxySelector); + } + + @Override + protected HttpHost determineProxy( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws HttpException { + final URI targetURI; + try { + targetURI = new URI(target.toURI()); + } catch (final URISyntaxException ex) { + throw new HttpException("Cannot convert host to URI: " + target, ex); + } + final List proxies = this.proxySelector.select(targetURI); + final Proxy p = chooseProxy(proxies); + HttpHost result = null; + if (p.type() == Proxy.Type.HTTP) { + // convert the socket address to an HttpHost + if (!(p.address() instanceof InetSocketAddress)) { + throw new HttpException("Unable to handle non-Inet proxy address: " + p.address()); + } + final InetSocketAddress isa = (InetSocketAddress) p.address(); + // assume default scheme (http) + result = new HttpHost(getHost(isa), isa.getPort()); + } + + return result; + } + + private String getHost(final InetSocketAddress isa) { + + //@@@ Will this work with literal IPv6 addresses, or do we + //@@@ need to wrap these in [] for the string representation? + //@@@ Having it in this method at least allows for easy workarounds. + return isa.isUnresolved() ? + isa.getHostName() : isa.getAddress().getHostAddress(); + + } + + private Proxy chooseProxy(final List proxies) { + Proxy result = null; + // check the list for one we can use + for (int i=0; (result == null) && (i < proxies.size()); i++) { + final Proxy p = proxies.get(i); + switch (p.type()) { + + case DIRECT: + case HTTP: + result = p; + break; + + case SOCKS: + // SOCKS hosts are not handled on the route level. + // The socket may make use of the SOCKS host though. + break; + } + } + if (result == null) { + //@@@ log as warning or info that only a socks proxy is available? + // result can only be null if all proxies are socks proxies + // socks proxies are not handled on the route planning level + result = Proxy.NO_PROXY; + } + return result; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/Wire.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/Wire.java new file mode 100644 index 000000000..1a678f05d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/Wire.java @@ -0,0 +1,152 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Logs data to the wire LOG. + * TODO: make package private. Should not be part of the public API. + * + * @since 4.0 + */ +@Immutable +public class Wire { + + public HttpClientAndroidLog log; + private final String id; + + /** + * @since 4.3 + */ + public Wire(final HttpClientAndroidLog log, final String id) { + this.log = log; + this.id = id; + } + + public Wire(final HttpClientAndroidLog log) { + this(log, ""); + } + + private void wire(final String header, final InputStream instream) + throws IOException { + final StringBuilder buffer = new StringBuilder(); + int ch; + while ((ch = instream.read()) != -1) { + if (ch == 13) { + buffer.append("[\\r]"); + } else if (ch == 10) { + buffer.append("[\\n]\""); + buffer.insert(0, "\""); + buffer.insert(0, header); + log.debug(id + " " + buffer.toString()); + buffer.setLength(0); + } else if ((ch < 32) || (ch > 127)) { + buffer.append("[0x"); + buffer.append(Integer.toHexString(ch)); + buffer.append("]"); + } else { + buffer.append((char) ch); + } + } + if (buffer.length() > 0) { + buffer.append('\"'); + buffer.insert(0, '\"'); + buffer.insert(0, header); + log.debug(id + " " + buffer.toString()); + } + } + + + public boolean enabled() { + return log.isDebugEnabled(); + } + + public void output(final InputStream outstream) + throws IOException { + Args.notNull(outstream, "Output"); + wire(">> ", outstream); + } + + public void input(final InputStream instream) + throws IOException { + Args.notNull(instream, "Input"); + wire("<< ", instream); + } + + public void output(final byte[] b, final int off, final int len) + throws IOException { + Args.notNull(b, "Output"); + wire(">> ", new ByteArrayInputStream(b, off, len)); + } + + public void input(final byte[] b, final int off, final int len) + throws IOException { + Args.notNull(b, "Input"); + wire("<< ", new ByteArrayInputStream(b, off, len)); + } + + public void output(final byte[] b) + throws IOException { + Args.notNull(b, "Output"); + wire(">> ", new ByteArrayInputStream(b)); + } + + public void input(final byte[] b) + throws IOException { + Args.notNull(b, "Input"); + wire("<< ", new ByteArrayInputStream(b)); + } + + public void output(final int b) + throws IOException { + output(new byte[] {(byte) b}); + } + + public void input(final int b) + throws IOException { + input(new byte[] {(byte) b}); + } + + public void output(final String s) + throws IOException { + Args.notNull(s, "Output"); + output(s.getBytes()); + } + + public void input(final String s) + throws IOException { + Args.notNull(s, "Input"); + input(s.getBytes()); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/package-info.java new file mode 100644 index 000000000..49305e4b7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default implementations of client connection management + * functions. + */ +package ch.boye.httpclientandroidlib.impl.conn; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java new file mode 100644 index 000000000..f85539c2d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java @@ -0,0 +1,234 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.conn.IdleConnectionHandler; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * An abstract connection pool. + * It is used by the {@link ThreadSafeClientConnManager}. + * The abstract pool includes a {@link #poolLock}, which is used to + * synchronize access to the internal pool datastructures. + * Don't use synchronized for that purpose! + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.AbstractConnPool} + */ +@Deprecated +public abstract class AbstractConnPool { + + public HttpClientAndroidLog log; + + /** + * The global lock for this pool. + */ + protected final Lock poolLock; + + /** References to issued connections */ + @GuardedBy("poolLock") + protected Set leasedConnections; + + /** The current total number of connections. */ + @GuardedBy("poolLock") + protected int numConnections; + + /** Indicates whether this pool is shut down. */ + protected volatile boolean isShutDown; + + protected Set issuedConnections; + + protected ReferenceQueue refQueue; + + protected IdleConnectionHandler idleConnHandler; + + /** + * Creates a new connection pool. + */ + protected AbstractConnPool() { + super(); + this.log = new HttpClientAndroidLog(getClass()); + this.leasedConnections = new HashSet(); + this.idleConnHandler = new IdleConnectionHandler(); + this.poolLock = new ReentrantLock(); + } + + public void enableConnectionGC() + throws IllegalStateException { + } + + /** + * Obtains a pool entry with a connection within the given timeout. + * + * @param route the route for which to get the connection + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the timeout, + * may be null only if there is no timeout + * + * @return pool entry holding a connection for the route + * + * @throws ConnectionPoolTimeoutException + * if the timeout expired + * @throws InterruptedException + * if the calling thread was interrupted + */ + public final + BasicPoolEntry getEntry( + final HttpRoute route, + final Object state, + final long timeout, + final TimeUnit tunit) + throws ConnectionPoolTimeoutException, InterruptedException { + return requestPoolEntry(route, state).getPoolEntry(timeout, tunit); + } + + /** + * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry} + * can be obtained, or the request can be aborted. + */ + public abstract PoolEntryRequest requestPoolEntry(HttpRoute route, Object state); + + + /** + * Returns an entry into the pool. + * The connection of the entry is expected to be in a suitable state, + * either open and re-usable, or closed. The pool will not make any + * attempt to determine whether it can be re-used or not. + * + * @param entry the entry for the connection to release + * @param reusable true if the entry is deemed + * reusable, false otherwise. + * @param validDuration The duration that the entry should remain free and reusable. + * @param timeUnit The unit of time the duration is measured in. + */ + public abstract void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit) + ; + + public void handleReference(final Reference ref) { + } + + protected abstract void handleLostEntry(HttpRoute route); + + /** + * Closes idle connections. + * + * @param idletime the time the connections should have been idle + * in order to be closed now + * @param tunit the unit for the idletime + */ + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + + // idletime can be 0 or negative, no problem there + Args.notNull(tunit, "Time unit"); + + poolLock.lock(); + try { + idleConnHandler.closeIdleConnections(tunit.toMillis(idletime)); + } finally { + poolLock.unlock(); + } + } + + public void closeExpiredConnections() { + poolLock.lock(); + try { + idleConnHandler.closeExpiredConnections(); + } finally { + poolLock.unlock(); + } + } + + + /** + * Deletes all entries for closed connections. + */ + public abstract void deleteClosedConnections(); + + /** + * Shuts down this pool and all associated resources. + * Overriding methods MUST call the implementation here! + */ + public void shutdown() { + + poolLock.lock(); + try { + + if (isShutDown) { + return; + } + + // close all connections that are issued to an application + final Iterator iter = leasedConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + iter.remove(); + closeConnection(entry.getConnection()); + } + idleConnHandler.removeAll(); + + isShutDown = true; + + } finally { + poolLock.unlock(); + } + } + + + /** + * Closes a connection from this pool. + * + * @param conn the connection to close, or null + */ + protected void closeConnection(final OperatedClientConnection conn) { + if (conn != null) { + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + +} // class AbstractConnPool + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java new file mode 100644 index 000000000..2c5bf05ce --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java @@ -0,0 +1,163 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.conn.AbstractPoolEntry; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of a connection pool entry. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.PoolEntry} + */ +@Deprecated +public class BasicPoolEntry extends AbstractPoolEntry { + + private final long created; + + private long updated; + private final long validUntil; + private long expiry; + + public BasicPoolEntry(final ClientConnectionOperator op, + final HttpRoute route, + final ReferenceQueue queue) { + super(op, route); + Args.notNull(route, "HTTP route"); + this.created = System.currentTimeMillis(); + this.validUntil = Long.MAX_VALUE; + this.expiry = this.validUntil; + } + + /** + * Creates a new pool entry. + * + * @param op the connection operator + * @param route the planned route for the connection + */ + public BasicPoolEntry(final ClientConnectionOperator op, + final HttpRoute route) { + this(op, route, -1, TimeUnit.MILLISECONDS); + } + + /** + * Creates a new pool entry with a specified maximum lifetime. + * + * @param op the connection operator + * @param route the planned route for the connection + * @param connTTL maximum lifetime of this entry, <=0 implies "infinity" + * @param timeunit TimeUnit of connTTL + * + * @since 4.1 + */ + public BasicPoolEntry(final ClientConnectionOperator op, + final HttpRoute route, final long connTTL, final TimeUnit timeunit) { + super(op, route); + Args.notNull(route, "HTTP route"); + this.created = System.currentTimeMillis(); + if (connTTL > 0) { + this.validUntil = this.created + timeunit.toMillis(connTTL); + } else { + this.validUntil = Long.MAX_VALUE; + } + this.expiry = this.validUntil; + } + + protected final OperatedClientConnection getConnection() { + return super.connection; + } + + protected final HttpRoute getPlannedRoute() { + return super.route; + } + + protected final BasicPoolEntryRef getWeakRef() { + return null; + } + + @Override + protected void shutdownEntry() { + super.shutdownEntry(); + } + + /** + * @since 4.1 + */ + public long getCreated() { + return this.created; + } + + /** + * @since 4.1 + */ + public long getUpdated() { + return this.updated; + } + + /** + * @since 4.1 + */ + public long getExpiry() { + return this.expiry; + } + + public long getValidUntil() { + return this.validUntil; + } + + /** + * @since 4.1 + */ + public void updateExpiry(final long time, final TimeUnit timeunit) { + this.updated = System.currentTimeMillis(); + final long newExpiry; + if (time > 0) { + newExpiry = this.updated + timeunit.toMillis(time); + } else { + newExpiry = Long.MAX_VALUE; + } + this.expiry = Math.min(validUntil, newExpiry); + } + + /** + * @since 4.1 + */ + public boolean isExpired(final long now) { + return now >= this.expiry; + } + +} + + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java new file mode 100644 index 000000000..202e6b102 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java @@ -0,0 +1,76 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A weak reference to a {@link BasicPoolEntry BasicPoolEntry}. + * This reference explicitly keeps the planned route, so the connection + * can be reclaimed if it is lost to garbage collection. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class BasicPoolEntryRef extends WeakReference { + + /** The planned route of the entry. */ + private final HttpRoute route; // HttpRoute is @Immutable + + + /** + * Creates a new reference to a pool entry. + * + * @param entry the pool entry, must not be null + * @param queue the reference queue, or null + */ + public BasicPoolEntryRef(final BasicPoolEntry entry, + final ReferenceQueue queue) { + super(entry, queue); + Args.notNull(entry, "Pool entry"); + route = entry.getPlannedRoute(); + } + + + /** + * Obtain the planned route for the referenced entry. + * The planned route is still available, even if the entry is gone. + * + * @return the planned route + */ + public final HttpRoute getRoute() { + return this.route; + } + +} // class BasicPoolEntryRef + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java new file mode 100644 index 000000000..3d6433a2d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.impl.conn.AbstractPoolEntry; +import ch.boye.httpclientandroidlib.impl.conn.AbstractPooledConnAdapter; + +/** + * A connection wrapper and callback handler. + * All connections given out by the manager are wrappers which + * can be {@link #detach detach}ed to prevent further use on release. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class BasicPooledConnAdapter extends AbstractPooledConnAdapter { + + /** + * Creates a new adapter. + * + * @param tsccm the connection manager + * @param entry the pool entry for the connection being wrapped + */ + protected BasicPooledConnAdapter(final ThreadSafeClientConnManager tsccm, + final AbstractPoolEntry entry) { + super(tsccm, entry); + markReusable(); + } + + @Override + protected ClientConnectionManager getManager() { + // override needed only to make method visible in this package + return super.getManager(); + } + + @Override + protected AbstractPoolEntry getPoolEntry() { + // override needed only to make method visible in this package + return super.getPoolEntry(); + } + + @Override + protected void detach() { + // override needed only to make method visible in this package + super.detach(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java new file mode 100644 index 000000000..22f6a089d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java @@ -0,0 +1,829 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.params.ConnManagerParams; +import ch.boye.httpclientandroidlib.conn.params.ConnPerRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A connection pool that maintains connections by route. + * This class is derived from MultiThreadedHttpConnectionManager + * in HttpClient 3.x, see there for original authors. It implements the same + * algorithm for connection re-use and connection-per-host enforcement: + *
      + *
    • connections are re-used only for the exact same route
    • + *
    • connection limits are enforced per route rather than per host
    • + *
    + * Note that access to the pool data structures is synchronized via the + * {@link AbstractConnPool#poolLock poolLock} in the base class, + * not via synchronized methods. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.AbstractConnPool} + */ +@Deprecated +public class ConnPoolByRoute extends AbstractConnPool { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final Lock poolLock; + + /** Connection operator for this pool */ + protected final ClientConnectionOperator operator; + + /** Connections per route lookup */ + protected final ConnPerRoute connPerRoute; + + /** References to issued connections */ + protected final Set leasedConnections; + + /** The list of free connections */ + protected final Queue freeConnections; + + /** The list of WaitingThreads waiting for a connection */ + protected final Queue waitingThreads; + + /** Map of route-specific pools */ + protected final Map routeToPool; + + private final long connTTL; + + private final TimeUnit connTTLTimeUnit; + + protected volatile boolean shutdown; + + protected volatile int maxTotalConnections; + + protected volatile int numConnections; + + /** + * Creates a new connection pool, managed by route. + * + * @since 4.1 + */ + public ConnPoolByRoute( + final ClientConnectionOperator operator, + final ConnPerRoute connPerRoute, + final int maxTotalConnections) { + this(operator, connPerRoute, maxTotalConnections, -1, TimeUnit.MILLISECONDS); + } + + /** + * @since 4.1 + */ + public ConnPoolByRoute( + final ClientConnectionOperator operator, + final ConnPerRoute connPerRoute, + final int maxTotalConnections, + final long connTTL, + final TimeUnit connTTLTimeUnit) { + super(); + Args.notNull(operator, "Connection operator"); + Args.notNull(connPerRoute, "Connections per route"); + this.poolLock = super.poolLock; + this.leasedConnections = super.leasedConnections; + this.operator = operator; + this.connPerRoute = connPerRoute; + this.maxTotalConnections = maxTotalConnections; + this.freeConnections = createFreeConnQueue(); + this.waitingThreads = createWaitingThreadQueue(); + this.routeToPool = createRouteToPoolMap(); + this.connTTL = connTTL; + this.connTTLTimeUnit = connTTLTimeUnit; + } + + protected Lock getLock() { + return this.poolLock; + } + + /** + * Creates a new connection pool, managed by route. + * + * @deprecated (4.1) use {@link ConnPoolByRoute#ConnPoolByRoute(ClientConnectionOperator, ConnPerRoute, int)} + */ + @Deprecated + public ConnPoolByRoute(final ClientConnectionOperator operator, final HttpParams params) { + this(operator, + ConnManagerParams.getMaxConnectionsPerRoute(params), + ConnManagerParams.getMaxTotalConnections(params)); + } + + /** + * Creates the queue for {@link #freeConnections}. + * Called once by the constructor. + * + * @return a queue + */ + protected Queue createFreeConnQueue() { + return new LinkedList(); + } + + /** + * Creates the queue for {@link #waitingThreads}. + * Called once by the constructor. + * + * @return a queue + */ + protected Queue createWaitingThreadQueue() { + return new LinkedList(); + } + + /** + * Creates the map for {@link #routeToPool}. + * Called once by the constructor. + * + * @return a map + */ + protected Map createRouteToPoolMap() { + return new HashMap(); + } + + + /** + * Creates a new route-specific pool. + * Called by {@link #getRoutePool} when necessary. + * + * @param route the route + * + * @return the new pool + */ + protected RouteSpecificPool newRouteSpecificPool(final HttpRoute route) { + return new RouteSpecificPool(route, this.connPerRoute); + } + + + /** + * Creates a new waiting thread. + * Called by {@link #getRoutePool} when necessary. + * + * @param cond the condition to wait for + * @param rospl the route specific pool, or null + * + * @return a waiting thread representation + */ + protected WaitingThread newWaitingThread(final Condition cond, + final RouteSpecificPool rospl) { + return new WaitingThread(cond, rospl); + } + + private void closeConnection(final BasicPoolEntry entry) { + final OperatedClientConnection conn = entry.getConnection(); + if (conn != null) { + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + + /** + * Get a route-specific pool of available connections. + * + * @param route the route + * @param create whether to create the pool if it doesn't exist + * + * @return the pool for the argument route, + * never null if create is true + */ + protected RouteSpecificPool getRoutePool(final HttpRoute route, + final boolean create) { + RouteSpecificPool rospl = null; + poolLock.lock(); + try { + + rospl = routeToPool.get(route); + if ((rospl == null) && create) { + // no pool for this route yet (or anymore) + rospl = newRouteSpecificPool(route); + routeToPool.put(route, rospl); + } + + } finally { + poolLock.unlock(); + } + + return rospl; + } + + public int getConnectionsInPool(final HttpRoute route) { + poolLock.lock(); + try { + // don't allow a pool to be created here! + final RouteSpecificPool rospl = getRoutePool(route, false); + return (rospl != null) ? rospl.getEntryCount() : 0; + + } finally { + poolLock.unlock(); + } + } + + public int getConnectionsInPool() { + poolLock.lock(); + try { + return numConnections; + } finally { + poolLock.unlock(); + } + } + + @Override + public PoolEntryRequest requestPoolEntry( + final HttpRoute route, + final Object state) { + + final WaitingThreadAborter aborter = new WaitingThreadAborter(); + + return new PoolEntryRequest() { + + public void abortRequest() { + poolLock.lock(); + try { + aborter.abort(); + } finally { + poolLock.unlock(); + } + } + + public BasicPoolEntry getPoolEntry( + final long timeout, + final TimeUnit tunit) + throws InterruptedException, ConnectionPoolTimeoutException { + return getEntryBlocking(route, state, timeout, tunit, aborter); + } + + }; + } + + /** + * Obtains a pool entry with a connection within the given timeout. + * If a {@link WaitingThread} is used to block, {@link WaitingThreadAborter#setWaitingThread(WaitingThread)} + * must be called before blocking, to allow the thread to be interrupted. + * + * @param route the route for which to get the connection + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the timeout, + * may be null only if there is no timeout + * @param aborter an object which can abort a {@link WaitingThread}. + * + * @return pool entry holding a connection for the route + * + * @throws ConnectionPoolTimeoutException + * if the timeout expired + * @throws InterruptedException + * if the calling thread was interrupted + */ + protected BasicPoolEntry getEntryBlocking( + final HttpRoute route, final Object state, + final long timeout, final TimeUnit tunit, + final WaitingThreadAborter aborter) + throws ConnectionPoolTimeoutException, InterruptedException { + + Date deadline = null; + if (timeout > 0) { + deadline = new Date + (System.currentTimeMillis() + tunit.toMillis(timeout)); + } + + BasicPoolEntry entry = null; + poolLock.lock(); + try { + + RouteSpecificPool rospl = getRoutePool(route, true); + WaitingThread waitingThread = null; + + while (entry == null) { + Asserts.check(!shutdown, "Connection pool shut down"); + + if (log.isDebugEnabled()) { + log.debug("[" + route + "] total kept alive: " + freeConnections.size() + + ", total issued: " + leasedConnections.size() + + ", total allocated: " + numConnections + " out of " + maxTotalConnections); + } + + // the cases to check for: + // - have a free connection for that route + // - allowed to create a free connection for that route + // - can delete and replace a free connection for another route + // - need to wait for one of the things above to come true + + entry = getFreeEntry(rospl, state); + if (entry != null) { + break; + } + + final boolean hasCapacity = rospl.getCapacity() > 0; + + if (log.isDebugEnabled()) { + log.debug("Available capacity: " + rospl.getCapacity() + + " out of " + rospl.getMaxEntries() + + " [" + route + "][" + state + "]"); + } + + if (hasCapacity && numConnections < maxTotalConnections) { + + entry = createEntry(rospl, operator); + + } else if (hasCapacity && !freeConnections.isEmpty()) { + + deleteLeastUsedEntry(); + // if least used entry's route was the same as rospl, + // rospl is now out of date : we preemptively refresh + rospl = getRoutePool(route, true); + entry = createEntry(rospl, operator); + + } else { + + if (log.isDebugEnabled()) { + log.debug("Need to wait for connection" + + " [" + route + "][" + state + "]"); + } + + if (waitingThread == null) { + waitingThread = + newWaitingThread(poolLock.newCondition(), rospl); + aborter.setWaitingThread(waitingThread); + } + + boolean success = false; + try { + rospl.queueThread(waitingThread); + waitingThreads.add(waitingThread); + success = waitingThread.await(deadline); + + } finally { + // In case of 'success', we were woken up by the + // connection pool and should now have a connection + // waiting for us, or else we're shutting down. + // Just continue in the loop, both cases are checked. + rospl.removeThread(waitingThread); + waitingThreads.remove(waitingThread); + } + + // check for spurious wakeup vs. timeout + if (!success && (deadline != null) && + (deadline.getTime() <= System.currentTimeMillis())) { + throw new ConnectionPoolTimeoutException + ("Timeout waiting for connection from pool"); + } + } + } // while no entry + + } finally { + poolLock.unlock(); + } + return entry; + } + + @Override + public void freeEntry(final BasicPoolEntry entry, final boolean reusable, final long validDuration, final TimeUnit timeUnit) { + + final HttpRoute route = entry.getPlannedRoute(); + if (log.isDebugEnabled()) { + log.debug("Releasing connection" + + " [" + route + "][" + entry.getState() + "]"); + } + + poolLock.lock(); + try { + if (shutdown) { + // the pool is shut down, release the + // connection's resources and get out of here + closeConnection(entry); + return; + } + + // no longer issued, we keep a hard reference now + leasedConnections.remove(entry); + + final RouteSpecificPool rospl = getRoutePool(route, true); + + if (reusable && rospl.getCapacity() >= 0) { + if (log.isDebugEnabled()) { + final String s; + if (validDuration > 0) { + s = "for " + validDuration + " " + timeUnit; + } else { + s = "indefinitely"; + } + log.debug("Pooling connection" + + " [" + route + "][" + entry.getState() + "]; keep alive " + s); + } + rospl.freeEntry(entry); + entry.updateExpiry(validDuration, timeUnit); + freeConnections.add(entry); + } else { + closeConnection(entry); + rospl.dropEntry(); + numConnections--; + } + + notifyWaitingThread(rospl); + + } finally { + poolLock.unlock(); + } + } + + /** + * If available, get a free pool entry for a route. + * + * @param rospl the route-specific pool from which to get an entry + * + * @return an available pool entry for the given route, or + * null if none is available + */ + protected BasicPoolEntry getFreeEntry(final RouteSpecificPool rospl, final Object state) { + + BasicPoolEntry entry = null; + poolLock.lock(); + try { + boolean done = false; + while(!done) { + + entry = rospl.allocEntry(state); + + if (entry != null) { + if (log.isDebugEnabled()) { + log.debug("Getting free connection" + + " [" + rospl.getRoute() + "][" + state + "]"); + + } + freeConnections.remove(entry); + if (entry.isExpired(System.currentTimeMillis())) { + // If the free entry isn't valid anymore, get rid of it + // and loop to find another one that might be valid. + if (log.isDebugEnabled()) { + log.debug("Closing expired free connection" + + " [" + rospl.getRoute() + "][" + state + "]"); + } + closeConnection(entry); + // We use dropEntry instead of deleteEntry because the entry + // is no longer "free" (we just allocated it), and deleteEntry + // can only be used to delete free entries. + rospl.dropEntry(); + numConnections--; + } else { + leasedConnections.add(entry); + done = true; + } + + } else { + done = true; + if (log.isDebugEnabled()) { + log.debug("No free connections" + + " [" + rospl.getRoute() + "][" + state + "]"); + } + } + } + } finally { + poolLock.unlock(); + } + return entry; + } + + + /** + * Creates a new pool entry. + * This method assumes that the new connection will be handed + * out immediately. + * + * @param rospl the route-specific pool for which to create the entry + * @param op the operator for creating a connection + * + * @return the new pool entry for a new connection + */ + protected BasicPoolEntry createEntry(final RouteSpecificPool rospl, + final ClientConnectionOperator op) { + + if (log.isDebugEnabled()) { + log.debug("Creating new connection [" + rospl.getRoute() + "]"); + } + + // the entry will create the connection when needed + final BasicPoolEntry entry = new BasicPoolEntry(op, rospl.getRoute(), connTTL, connTTLTimeUnit); + + poolLock.lock(); + try { + rospl.createdEntry(entry); + numConnections++; + leasedConnections.add(entry); + } finally { + poolLock.unlock(); + } + + return entry; + } + + + /** + * Deletes a given pool entry. + * This closes the pooled connection and removes all references, + * so that it can be GCed. + * + *

    Note: Does not remove the entry from the freeConnections list. + * It is assumed that the caller has already handled this step.

    + * + * + * @param entry the pool entry for the connection to delete + */ + protected void deleteEntry(final BasicPoolEntry entry) { + + final HttpRoute route = entry.getPlannedRoute(); + + if (log.isDebugEnabled()) { + log.debug("Deleting connection" + + " [" + route + "][" + entry.getState() + "]"); + } + + poolLock.lock(); + try { + + closeConnection(entry); + + final RouteSpecificPool rospl = getRoutePool(route, true); + rospl.deleteEntry(entry); + numConnections--; + if (rospl.isUnused()) { + routeToPool.remove(route); + } + + } finally { + poolLock.unlock(); + } + } + + + /** + * Delete an old, free pool entry to make room for a new one. + * Used to replace pool entries with ones for a different route. + */ + protected void deleteLeastUsedEntry() { + poolLock.lock(); + try { + + final BasicPoolEntry entry = freeConnections.remove(); + + if (entry != null) { + deleteEntry(entry); + } else if (log.isDebugEnabled()) { + log.debug("No free connection to delete"); + } + + } finally { + poolLock.unlock(); + } + } + + @Override + protected void handleLostEntry(final HttpRoute route) { + + poolLock.lock(); + try { + + final RouteSpecificPool rospl = getRoutePool(route, true); + rospl.dropEntry(); + if (rospl.isUnused()) { + routeToPool.remove(route); + } + + numConnections--; + notifyWaitingThread(rospl); + + } finally { + poolLock.unlock(); + } + } + + /** + * Notifies a waiting thread that a connection is available. + * This will wake a thread waiting in the specific route pool, + * if there is one. + * Otherwise, a thread in the connection pool will be notified. + * + * @param rospl the pool in which to notify, or null + */ + protected void notifyWaitingThread(final RouteSpecificPool rospl) { + + //@@@ while this strategy provides for best connection re-use, + //@@@ is it fair? only do this if the connection is open? + // Find the thread we are going to notify. We want to ensure that + // each waiting thread is only interrupted once, so we will remove + // it from all wait queues before interrupting. + WaitingThread waitingThread = null; + + poolLock.lock(); + try { + + if ((rospl != null) && rospl.hasThread()) { + if (log.isDebugEnabled()) { + log.debug("Notifying thread waiting on pool" + + " [" + rospl.getRoute() + "]"); + } + waitingThread = rospl.nextThread(); + } else if (!waitingThreads.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("Notifying thread waiting on any pool"); + } + waitingThread = waitingThreads.remove(); + } else if (log.isDebugEnabled()) { + log.debug("Notifying no-one, there are no waiting threads"); + } + + if (waitingThread != null) { + waitingThread.wakeup(); + } + + } finally { + poolLock.unlock(); + } + } + + + @Override + public void deleteClosedConnections() { + poolLock.lock(); + try { + final Iterator iter = freeConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + if (!entry.getConnection().isOpen()) { + iter.remove(); + deleteEntry(entry); + } + } + } finally { + poolLock.unlock(); + } + } + + /** + * Closes idle connections. + * + * @param idletime the time the connections should have been idle + * in order to be closed now + * @param tunit the unit for the idletime + */ + @Override + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + final long t = idletime > 0 ? idletime : 0; + if (log.isDebugEnabled()) { + log.debug("Closing connections idle longer than " + t + " " + tunit); + } + // the latest time for which connections will be closed + final long deadline = System.currentTimeMillis() - tunit.toMillis(t); + poolLock.lock(); + try { + final Iterator iter = freeConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + if (entry.getUpdated() <= deadline) { + if (log.isDebugEnabled()) { + log.debug("Closing connection last used @ " + new Date(entry.getUpdated())); + } + iter.remove(); + deleteEntry(entry); + } + } + } finally { + poolLock.unlock(); + } + } + + @Override + public void closeExpiredConnections() { + log.debug("Closing expired connections"); + final long now = System.currentTimeMillis(); + + poolLock.lock(); + try { + final Iterator iter = freeConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + if (entry.isExpired(now)) { + if (log.isDebugEnabled()) { + log.debug("Closing connection expired @ " + new Date(entry.getExpiry())); + } + iter.remove(); + deleteEntry(entry); + } + } + } finally { + poolLock.unlock(); + } + } + + @Override + public void shutdown() { + poolLock.lock(); + try { + if (shutdown) { + return; + } + shutdown = true; + + // close all connections that are issued to an application + final Iterator iter1 = leasedConnections.iterator(); + while (iter1.hasNext()) { + final BasicPoolEntry entry = iter1.next(); + iter1.remove(); + closeConnection(entry); + } + + // close all free connections + final Iterator iter2 = freeConnections.iterator(); + while (iter2.hasNext()) { + final BasicPoolEntry entry = iter2.next(); + iter2.remove(); + + if (log.isDebugEnabled()) { + log.debug("Closing connection" + + " [" + entry.getPlannedRoute() + "][" + entry.getState() + "]"); + } + closeConnection(entry); + } + + // wake up all waiting threads + final Iterator iwth = waitingThreads.iterator(); + while (iwth.hasNext()) { + final WaitingThread waiter = iwth.next(); + iwth.remove(); + waiter.wakeup(); + } + + routeToPool.clear(); + + } finally { + poolLock.unlock(); + } + } + + /** + * since 4.1 + */ + public void setMaxTotalConnections(final int max) { + poolLock.lock(); + try { + maxTotalConnections = max; + } finally { + poolLock.unlock(); + } + } + + + /** + * since 4.1 + */ + public int getMaxTotalConnections() { + return maxTotalConnections; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java new file mode 100644 index 000000000..bf7eb147f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; + +/** + * Encapsulates a request for a {@link BasicPoolEntry}. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link java.util.concurrent.Future} + */ +@Deprecated +public interface PoolEntryRequest { + + /** + * Obtains a pool entry with a connection within the given timeout. + * If {@link #abortRequest()} is called before this completes + * an {@link InterruptedException} is thrown. + * + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the timeout, + * may be null only if there is no timeout + * + * @return pool entry holding a connection for the route + * + * @throws ConnectionPoolTimeoutException + * if the timeout expired + * @throws InterruptedException + * if the calling thread was interrupted or the request was aborted + */ + BasicPoolEntry getPoolEntry( + long timeout, + TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException; + + /** + * Aborts the active or next call to + * {@link #getPoolEntry(long, TimeUnit)}. + */ + void abortRequest(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java new file mode 100644 index 000000000..73964c8d1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java @@ -0,0 +1,313 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.Queue; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.params.ConnPerRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.LangUtils; + + +/** + * A connection sub-pool for a specific route, used by {@link ConnPoolByRoute}. + * The methods in this class are unsynchronized. It is expected that the + * containing pool takes care of synchronization. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.AbstractConnPool} + */ +@Deprecated +public class RouteSpecificPool { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** The route this pool is for. */ + protected final HttpRoute route; //Immutable + + protected final int maxEntries; + + /** Connections per route */ + protected final ConnPerRoute connPerRoute; + + /** + * The list of free entries. + * This list is managed LIFO, to increase idle times and + * allow for closing connections that are not really needed. + */ + protected final LinkedList freeEntries; + + /** The list of threads waiting for this pool. */ + protected final Queue waitingThreads; + + /** The number of created entries. */ + protected int numEntries; + + /** + * @deprecated (4.1) use {@link RouteSpecificPool#RouteSpecificPool(HttpRoute, ConnPerRoute)} + */ + @Deprecated + public RouteSpecificPool(final HttpRoute route, final int maxEntries) { + this.route = route; + this.maxEntries = maxEntries; + this.connPerRoute = new ConnPerRoute() { + public int getMaxForRoute(final HttpRoute route) { + return RouteSpecificPool.this.maxEntries; + } + }; + this.freeEntries = new LinkedList(); + this.waitingThreads = new LinkedList(); + this.numEntries = 0; + } + + + /** + * Creates a new route-specific pool. + * + * @param route the route for which to pool + * @param connPerRoute the connections per route configuration + */ + public RouteSpecificPool(final HttpRoute route, final ConnPerRoute connPerRoute) { + this.route = route; + this.connPerRoute = connPerRoute; + this.maxEntries = connPerRoute.getMaxForRoute(route); + this.freeEntries = new LinkedList(); + this.waitingThreads = new LinkedList(); + this.numEntries = 0; + } + + + /** + * Obtains the route for which this pool is specific. + * + * @return the route + */ + public final HttpRoute getRoute() { + return route; + } + + + /** + * Obtains the maximum number of entries allowed for this pool. + * + * @return the max entry number + */ + public final int getMaxEntries() { + return maxEntries; + } + + + /** + * Indicates whether this pool is unused. + * A pool is unused if there is neither an entry nor a waiting thread. + * All entries count, not only the free but also the allocated ones. + * + * @return true if this pool is unused, + * false otherwise + */ + public boolean isUnused() { + return (numEntries < 1) && waitingThreads.isEmpty(); + } + + + /** + * Return remaining capacity of this pool + * + * @return capacity + */ + public int getCapacity() { + return connPerRoute.getMaxForRoute(route) - numEntries; + } + + + /** + * Obtains the number of entries. + * This includes not only the free entries, but also those that + * have been created and are currently issued to an application. + * + * @return the number of entries for the route of this pool + */ + public final int getEntryCount() { + return numEntries; + } + + + /** + * Obtains a free entry from this pool, if one is available. + * + * @return an available pool entry, or null if there is none + */ + public BasicPoolEntry allocEntry(final Object state) { + if (!freeEntries.isEmpty()) { + final ListIterator it = freeEntries.listIterator(freeEntries.size()); + while (it.hasPrevious()) { + final BasicPoolEntry entry = it.previous(); + if (entry.getState() == null || LangUtils.equals(state, entry.getState())) { + it.remove(); + return entry; + } + } + } + if (getCapacity() == 0 && !freeEntries.isEmpty()) { + final BasicPoolEntry entry = freeEntries.remove(); + entry.shutdownEntry(); + final OperatedClientConnection conn = entry.getConnection(); + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + return entry; + } + return null; + } + + + /** + * Returns an allocated entry to this pool. + * + * @param entry the entry obtained from {@link #allocEntry allocEntry} + * or presented to {@link #createdEntry createdEntry} + */ + public void freeEntry(final BasicPoolEntry entry) { + if (numEntries < 1) { + throw new IllegalStateException + ("No entry created for this pool. " + route); + } + if (numEntries <= freeEntries.size()) { + throw new IllegalStateException + ("No entry allocated from this pool. " + route); + } + freeEntries.add(entry); + } + + + /** + * Indicates creation of an entry for this pool. + * The entry will not be added to the list of free entries, + * it is only recognized as belonging to this pool now. It can then + * be passed to {@link #freeEntry freeEntry}. + * + * @param entry the entry that was created for this pool + */ + public void createdEntry(final BasicPoolEntry entry) { + Args.check(route.equals(entry.getPlannedRoute()), "Entry not planned for this pool"); + numEntries++; + } + + + /** + * Deletes an entry from this pool. + * Only entries that are currently free in this pool can be deleted. + * Allocated entries can not be deleted. + * + * @param entry the entry to delete from this pool + * + * @return true if the entry was found and deleted, or + * false if the entry was not found + */ + public boolean deleteEntry(final BasicPoolEntry entry) { + + final boolean found = freeEntries.remove(entry); + if (found) { + numEntries--; + } + return found; + } + + + /** + * Forgets about an entry from this pool. + * This method is used to indicate that an entry + * {@link #allocEntry allocated} + * from this pool has been lost and will not be returned. + */ + public void dropEntry() { + Asserts.check(numEntries > 0, "There is no entry that could be dropped"); + numEntries--; + } + + + /** + * Adds a waiting thread. + * This pool makes no attempt to match waiting threads with pool entries. + * It is the caller's responsibility to check that there is no entry + * before adding a waiting thread. + * + * @param wt the waiting thread + */ + public void queueThread(final WaitingThread wt) { + Args.notNull(wt, "Waiting thread"); + this.waitingThreads.add(wt); + } + + + /** + * Checks whether there is a waiting thread in this pool. + * + * @return true if there is a waiting thread, + * false otherwise + */ + public boolean hasThread() { + return !this.waitingThreads.isEmpty(); + } + + + /** + * Returns the next thread in the queue. + * + * @return a waiting thread, or null if there is none + */ + public WaitingThread nextThread() { + return this.waitingThreads.peek(); + } + + + /** + * Removes a waiting thread, if it is queued. + * + * @param wt the waiting thread + */ + public void removeThread(final WaitingThread wt) { + if (wt == null) { + return; + } + + this.waitingThreads.remove(wt); + } + + +} // class RouteSpecificPool diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java new file mode 100644 index 000000000..1a1ff5c37 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java @@ -0,0 +1,377 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.params.ConnPerRouteBean; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.impl.conn.DefaultClientConnectionOperator; +import ch.boye.httpclientandroidlib.impl.conn.SchemeRegistryFactory; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Manages a pool of {@link ch.boye.httpclientandroidlib.conn.OperatedClientConnection } + * and is able to service connection requests from multiple execution threads. + * Connections are pooled on a per route basis. A request for a route which + * already the manager has persistent connections for available in the pool + * will be services by leasing a connection from the pool rather than + * creating a brand new connection. + *

    + * ThreadSafeClientConnManager maintains a maximum limit of connection on + * a per route basis and in total. Per default this implementation will + * create no more than than 2 concurrent connections per given route + * and no more 20 connections in total. For many real-world applications + * these limits may prove too constraining, especially if they use HTTP + * as a transport protocol for their services. Connection limits, however, + * can be adjusted using HTTP parameters. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager} + */ +@ThreadSafe +@Deprecated +public class ThreadSafeClientConnManager implements ClientConnectionManager { + + public HttpClientAndroidLog log; + + /** The schemes supported by this connection manager. */ + protected final SchemeRegistry schemeRegistry; // @ThreadSafe + + protected final AbstractConnPool connectionPool; + + /** The pool of connections being managed. */ + protected final ConnPoolByRoute pool; + + /** The operator for opening and updating connections. */ + protected final ClientConnectionOperator connOperator; // DefaultClientConnectionOperator is @ThreadSafe + + protected final ConnPerRouteBean connPerRoute; + + /** + * Creates a new thread safe connection manager. + * + * @param schreg the scheme registry. + */ + public ThreadSafeClientConnManager(final SchemeRegistry schreg) { + this(schreg, -1, TimeUnit.MILLISECONDS); + } + + /** + * @since 4.1 + */ + public ThreadSafeClientConnManager() { + this(SchemeRegistryFactory.createDefault()); + } + + /** + * Creates a new thread safe connection manager. + * + * @param schreg the scheme registry. + * @param connTTL max connection lifetime, <=0 implies "infinity" + * @param connTTLTimeUnit TimeUnit of connTTL + * + * @since 4.1 + */ + public ThreadSafeClientConnManager(final SchemeRegistry schreg, + final long connTTL, final TimeUnit connTTLTimeUnit) { + this(schreg, connTTL, connTTLTimeUnit, new ConnPerRouteBean()); + } + + /** + * Creates a new thread safe connection manager. + * + * @param schreg the scheme registry. + * @param connTTL max connection lifetime, <=0 implies "infinity" + * @param connTTLTimeUnit TimeUnit of connTTL + * @param connPerRoute mapping of maximum connections per route, + * provided as a dependency so it can be managed externally, e.g. + * for dynamic connection pool size management. + * + * @since 4.2 + */ + public ThreadSafeClientConnManager(final SchemeRegistry schreg, + final long connTTL, final TimeUnit connTTLTimeUnit, final ConnPerRouteBean connPerRoute) { + super(); + Args.notNull(schreg, "Scheme registry"); + this.log = new HttpClientAndroidLog(getClass()); + this.schemeRegistry = schreg; + this.connPerRoute = connPerRoute; + this.connOperator = createConnectionOperator(schreg); + this.pool = createConnectionPool(connTTL, connTTLTimeUnit) ; + this.connectionPool = this.pool; + } + + /** + * Creates a new thread safe connection manager. + * + * @param params the parameters for this manager. + * @param schreg the scheme registry. + * + * @deprecated (4.1) use {@link ThreadSafeClientConnManager#ThreadSafeClientConnManager(SchemeRegistry)} + */ + @Deprecated + public ThreadSafeClientConnManager(final HttpParams params, + final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + this.log = new HttpClientAndroidLog(getClass()); + this.schemeRegistry = schreg; + this.connPerRoute = new ConnPerRouteBean(); + this.connOperator = createConnectionOperator(schreg); + this.pool = (ConnPoolByRoute) createConnectionPool(params) ; + this.connectionPool = this.pool; + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + /** + * Hook for creating the connection pool. + * + * @return the connection pool to use + * + * @deprecated (4.1) use #createConnectionPool(long, TimeUnit)) + */ + @Deprecated + protected AbstractConnPool createConnectionPool(final HttpParams params) { + return new ConnPoolByRoute(connOperator, params); + } + + /** + * Hook for creating the connection pool. + * + * @return the connection pool to use + * + * @since 4.1 + */ + protected ConnPoolByRoute createConnectionPool(final long connTTL, final TimeUnit connTTLTimeUnit) { + return new ConnPoolByRoute(connOperator, connPerRoute, 20, connTTL, connTTLTimeUnit); + } + + /** + * Hook for creating the connection operator. + * It is called by the constructor. + * Derived classes can override this method to change the + * instantiation of the operator. + * The default implementation here instantiates + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. + * + * @param schreg the scheme registry. + * + * @return the connection operator to use + */ + protected ClientConnectionOperator + createConnectionOperator(final SchemeRegistry schreg) { + + return new DefaultClientConnectionOperator(schreg);// @ThreadSafe + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + public ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + + final PoolEntryRequest poolRequest = pool.requestPoolEntry( + route, state); + + return new ClientConnectionRequest() { + + public void abortRequest() { + poolRequest.abortRequest(); + } + + public ManagedClientConnection getConnection( + final long timeout, final TimeUnit tunit) throws InterruptedException, + ConnectionPoolTimeoutException { + Args.notNull(route, "Route"); + + if (log.isDebugEnabled()) { + log.debug("Get connection: " + route + ", timeout = " + timeout); + } + + final BasicPoolEntry entry = poolRequest.getPoolEntry(timeout, tunit); + return new BasicPooledConnAdapter(ThreadSafeClientConnManager.this, entry); + } + + }; + + } + + public void releaseConnection(final ManagedClientConnection conn, final long validDuration, final TimeUnit timeUnit) { + Args.check(conn instanceof BasicPooledConnAdapter, "Connection class mismatch, " + + "connection not obtained from this manager"); + final BasicPooledConnAdapter hca = (BasicPooledConnAdapter) conn; + if (hca.getPoolEntry() != null) { + Asserts.check(hca.getManager() == this, "Connection not obtained from this manager"); + } + synchronized (hca) { + final BasicPoolEntry entry = (BasicPoolEntry) hca.getPoolEntry(); + if (entry == null) { + return; + } + try { + // make sure that the response has been read completely + if (hca.isOpen() && !hca.isMarkedReusable()) { + // In MTHCM, there would be a call to + // SimpleHttpConnectionManager.finishLastResponse(conn); + // Consuming the response is handled outside in 4.0. + + // make sure this connection will not be re-used + // Shut down rather than close, we might have gotten here + // because of a shutdown trigger. + // Shutdown of the adapter also clears the tracked route. + hca.shutdown(); + } + } catch (final IOException iox) { + if (log.isDebugEnabled()) { + log.debug("Exception shutting down released connection.", + iox); + } + } finally { + final boolean reusable = hca.isMarkedReusable(); + if (log.isDebugEnabled()) { + if (reusable) { + log.debug("Released connection is reusable."); + } else { + log.debug("Released connection is not reusable."); + } + } + hca.detach(); + pool.freeEntry(entry, reusable, validDuration, timeUnit); + } + } + } + + public void shutdown() { + log.debug("Shutting down"); + pool.shutdown(); + } + + /** + * Gets the total number of pooled connections for the given route. + * This is the total number of connections that have been created and + * are still in use by this connection manager for the route. + * This value will not exceed the maximum number of connections per host. + * + * @param route the route in question + * + * @return the total number of pooled connections for that route + */ + public int getConnectionsInPool(final HttpRoute route) { + return pool.getConnectionsInPool(route); + } + + /** + * Gets the total number of pooled connections. This is the total number of + * connections that have been created and are still in use by this connection + * manager. This value will not exceed the maximum number of connections + * in total. + * + * @return the total number of pooled connections + */ + public int getConnectionsInPool() { + return pool.getConnectionsInPool(); + } + + public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) { + if (log.isDebugEnabled()) { + log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + } + pool.closeIdleConnections(idleTimeout, tunit); + } + + public void closeExpiredConnections() { + log.debug("Closing expired connections"); + pool.closeExpiredConnections(); + } + + /** + * since 4.1 + */ + public int getMaxTotal() { + return pool.getMaxTotalConnections(); + } + + /** + * since 4.1 + */ + public void setMaxTotal(final int max) { + pool.setMaxTotalConnections(max); + } + + /** + * @since 4.1 + */ + public int getDefaultMaxPerRoute() { + return connPerRoute.getDefaultMaxPerRoute(); + } + + /** + * @since 4.1 + */ + public void setDefaultMaxPerRoute(final int max) { + connPerRoute.setDefaultMaxPerRoute(max); + } + + /** + * @since 4.1 + */ + public int getMaxForRoute(final HttpRoute route) { + return connPerRoute.getMaxForRoute(route); + } + + /** + * @since 4.1 + */ + public void setMaxForRoute(final HttpRoute route, final int max) { + connPerRoute.setMaxForRoute(route, max); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThread.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThread.java new file mode 100644 index 000000000..c3f46cacf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThread.java @@ -0,0 +1,198 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + + +import java.util.Date; +import java.util.concurrent.locks.Condition; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Represents a thread waiting for a connection. + * This class implements throwaway objects. It is instantiated whenever + * a thread needs to wait. Instances are not re-used, except if the + * waiting thread experiences a spurious wakeup and continues to wait. + *
    + * All methods assume external synchronization on the condition + * passed to the constructor. + * Instances of this class do not synchronize access! + * + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class WaitingThread { + + /** The condition on which the thread is waiting. */ + private final Condition cond; + + /** The route specific pool on which the thread is waiting. */ + //@@@ replace with generic pool interface + private final RouteSpecificPool pool; + + /** The thread that is waiting for an entry. */ + private Thread waiter; + + /** True if this was interrupted. */ + private boolean aborted; + + + /** + * Creates a new entry for a waiting thread. + * + * @param cond the condition for which to wait + * @param pool the pool on which the thread will be waiting, + * or null + */ + public WaitingThread(final Condition cond, final RouteSpecificPool pool) { + + Args.notNull(cond, "Condition"); + + this.cond = cond; + this.pool = pool; + } + + + /** + * Obtains the condition. + * + * @return the condition on which to wait, never null + */ + public final Condition getCondition() { + // not synchronized + return this.cond; + } + + + /** + * Obtains the pool, if there is one. + * + * @return the pool on which a thread is or was waiting, + * or null + */ + public final RouteSpecificPool getPool() { + // not synchronized + return this.pool; + } + + + /** + * Obtains the thread, if there is one. + * + * @return the thread which is waiting, or null + */ + public final Thread getThread() { + // not synchronized + return this.waiter; + } + + + /** + * Blocks the calling thread. + * This method returns when the thread is notified or interrupted, + * if a timeout occurrs, or if there is a spurious wakeup. + *
    + * This method assumes external synchronization. + * + * @param deadline when to time out, or null for no timeout + * + * @return true if the condition was satisfied, + * false in case of a timeout. + * Typically, a call to {@link #wakeup} is used to indicate + * that the condition was satisfied. Since the condition is + * accessible outside, this cannot be guaranteed though. + * + * @throws InterruptedException if the waiting thread was interrupted + * + * @see #wakeup + */ + public boolean await(final Date deadline) + throws InterruptedException { + + // This is only a sanity check. We cannot synchronize here, + // the lock would not be released on calling cond.await() below. + if (this.waiter != null) { + throw new IllegalStateException + ("A thread is already waiting on this object." + + "\ncaller: " + Thread.currentThread() + + "\nwaiter: " + this.waiter); + } + + if (aborted) { + throw new InterruptedException("Operation interrupted"); + } + + this.waiter = Thread.currentThread(); + + boolean success = false; + try { + if (deadline != null) { + success = this.cond.awaitUntil(deadline); + } else { + this.cond.await(); + success = true; + } + if (aborted) { + throw new InterruptedException("Operation interrupted"); + } + } finally { + this.waiter = null; + } + return success; + + } // await + + + /** + * Wakes up the waiting thread. + *
    + * This method assumes external synchronization. + */ + public void wakeup() { + + // If external synchronization and pooling works properly, + // this cannot happen. Just a sanity check. + if (this.waiter == null) { + throw new IllegalStateException + ("Nobody waiting on this object."); + } + + // One condition might be shared by several WaitingThread instances. + // It probably isn't, but just in case: wake all, not just one. + this.cond.signalAll(); + } + + public void interrupt() { + aborted = true; + this.cond.signalAll(); + } + + +} // class WaitingThread diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java new file mode 100644 index 000000000..7ad999996 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; + +/** + * A simple class that can interrupt a {@link WaitingThread}. + * + * Must be called with the pool lock held. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class WaitingThreadAborter { + + private WaitingThread waitingThread; + private boolean aborted; + + /** + * If a waiting thread has been set, interrupts it. + */ + public void abort() { + aborted = true; + + if (waitingThread != null) { + waitingThread.interrupt(); + } + + } + + /** + * Sets the waiting thread. If this has already been aborted, + * the waiting thread is immediately interrupted. + * + * @param waitingThread The thread to interrupt when aborting. + */ + public void setWaitingThread(final WaitingThread waitingThread) { + this.waitingThread = waitingThread; + if (aborted) { + waitingThread.interrupt(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/package-info.java new file mode 100644 index 000000000..5115d461f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/package-info.java @@ -0,0 +1,33 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Deprecated. + * + * @deprecated (4.3) + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieAttributeHandler.java new file mode 100644 index 000000000..e9406bab2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieAttributeHandler.java @@ -0,0 +1,52 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; + +/** + * + * @since 4.0 + */ +@Immutable +public abstract class AbstractCookieAttributeHandler implements CookieAttributeHandler { + + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + // Do nothing + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + // Always match + return true; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieSpec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieSpec.java new file mode 100644 index 000000000..7dc320ef4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/AbstractCookieSpec.java @@ -0,0 +1,104 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Abstract cookie specification which can delegate the job of parsing, + * validation or matching cookie attributes to a number of arbitrary + * {@link CookieAttributeHandler}s. + * + * + * @since 4.0 + */ +@NotThreadSafe // HashMap is not thread-safe +public abstract class AbstractCookieSpec implements CookieSpec { + + /** + * Stores attribute name -> attribute handler mappings + */ + private final Map attribHandlerMap; + + /** + * Default constructor + * */ + public AbstractCookieSpec() { + super(); + this.attribHandlerMap = new HashMap(10); + } + + public void registerAttribHandler( + final String name, final CookieAttributeHandler handler) { + Args.notNull(name, "Attribute name"); + Args.notNull(handler, "Attribute handler"); + this.attribHandlerMap.put(name, handler); + } + + /** + * Finds an attribute handler {@link CookieAttributeHandler} for the + * given attribute. Returns null if no attribute handler is + * found for the specified attribute. + * + * @param name attribute name. e.g. Domain, Path, etc. + * @return an attribute handler or null + */ + protected CookieAttributeHandler findAttribHandler(final String name) { + return this.attribHandlerMap.get(name); + } + + /** + * Gets attribute handler {@link CookieAttributeHandler} for the + * given attribute. + * + * @param name attribute name. e.g. Domain, Path, etc. + * @throws IllegalStateException if handler not found for the + * specified attribute. + */ + protected CookieAttributeHandler getAttribHandler(final String name) { + final CookieAttributeHandler handler = findAttribHandler(name); + if (handler == null) { + throw new IllegalStateException("Handler not registered for " + + name + " attribute."); + } else { + return handler; + } + } + + protected Collection getAttribHandlers() { + return this.attribHandlerMap.values(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie.java new file mode 100644 index 000000000..c826daada --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie.java @@ -0,0 +1,361 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of {@link SetCookie}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicClientCookie implements SetCookie, ClientCookie, Cloneable, Serializable { + + private static final long serialVersionUID = -3869795591041535538L; + + /** + * Default Constructor taking a name and a value. The value may be null. + * + * @param name The name. + * @param value The value. + */ + public BasicClientCookie(final String name, final String value) { + super(); + Args.notNull(name, "Name"); + this.name = name; + this.attribs = new HashMap(); + this.value = value; + } + + /** + * Returns the name. + * + * @return String name The name + */ + public String getName() { + return this.name; + } + + /** + * Returns the value. + * + * @return String value The current value. + */ + public String getValue() { + return this.value; + } + + /** + * Sets the value + * + * @param value + */ + public void setValue(final String value) { + this.value = value; + } + + /** + * Returns the comment describing the purpose of this cookie, or + * null if no such comment has been defined. + * + * @return comment + * + * @see #setComment(String) + */ + public String getComment() { + return cookieComment; + } + + /** + * If a user agent (web browser) presents this cookie to a user, the + * cookie's purpose will be described using this comment. + * + * @param comment + * + * @see #getComment() + */ + public void setComment(final String comment) { + cookieComment = comment; + } + + + /** + * Returns null. Cookies prior to RFC2965 do not set this attribute + */ + public String getCommentURL() { + return null; + } + + + /** + * Returns the expiration {@link Date} of the cookie, or null + * if none exists. + *

    Note: the object returned by this method is + * considered immutable. Changing it (e.g. using setTime()) could result + * in undefined behaviour. Do so at your peril.

    + * @return Expiration {@link Date}, or null. + * + * @see #setExpiryDate(java.util.Date) + * + */ + public Date getExpiryDate() { + return cookieExpiryDate; + } + + /** + * Sets expiration date. + *

    Note: the object returned by this method is considered + * immutable. Changing it (e.g. using setTime()) could result in undefined + * behaviour. Do so at your peril.

    + * + * @param expiryDate the {@link Date} after which this cookie is no longer valid. + * + * @see #getExpiryDate + * + */ + public void setExpiryDate (final Date expiryDate) { + cookieExpiryDate = expiryDate; + } + + + /** + * Returns false if the cookie should be discarded at the end + * of the "session"; true otherwise. + * + * @return false if the cookie should be discarded at the end + * of the "session"; true otherwise + */ + public boolean isPersistent() { + return (null != cookieExpiryDate); + } + + + /** + * Returns domain attribute of the cookie. + * + * @return the value of the domain attribute + * + * @see #setDomain(java.lang.String) + */ + public String getDomain() { + return cookieDomain; + } + + /** + * Sets the domain attribute. + * + * @param domain The value of the domain attribute + * + * @see #getDomain + */ + public void setDomain(final String domain) { + if (domain != null) { + cookieDomain = domain.toLowerCase(Locale.ENGLISH); + } else { + cookieDomain = null; + } + } + + + /** + * Returns the path attribute of the cookie + * + * @return The value of the path attribute. + * + * @see #setPath(java.lang.String) + */ + public String getPath() { + return cookiePath; + } + + /** + * Sets the path attribute. + * + * @param path The value of the path attribute + * + * @see #getPath + * + */ + public void setPath(final String path) { + cookiePath = path; + } + + /** + * @return true if this cookie should only be sent over secure connections. + * @see #setSecure(boolean) + */ + public boolean isSecure() { + return isSecure; + } + + /** + * Sets the secure attribute of the cookie. + *

    + * When true the cookie should only be sent + * using a secure protocol (https). This should only be set when + * the cookie's originating server used a secure protocol to set the + * cookie's value. + * + * @param secure The value of the secure attribute + * + * @see #isSecure() + */ + public void setSecure (final boolean secure) { + isSecure = secure; + } + + + /** + * Returns null. Cookies prior to RFC2965 do not set this attribute + */ + public int[] getPorts() { + return null; + } + + + /** + * Returns the version of the cookie specification to which this + * cookie conforms. + * + * @return the version of the cookie. + * + * @see #setVersion(int) + * + */ + public int getVersion() { + return cookieVersion; + } + + /** + * Sets the version of the cookie specification to which this + * cookie conforms. + * + * @param version the version of the cookie. + * + * @see #getVersion + */ + public void setVersion(final int version) { + cookieVersion = version; + } + + /** + * Returns true if this cookie has expired. + * @param date Current time + * + * @return true if the cookie has expired. + */ + public boolean isExpired(final Date date) { + Args.notNull(date, "Date"); + return (cookieExpiryDate != null + && cookieExpiryDate.getTime() <= date.getTime()); + } + + public void setAttribute(final String name, final String value) { + this.attribs.put(name, value); + } + + public String getAttribute(final String name) { + return this.attribs.get(name); + } + + public boolean containsAttribute(final String name) { + return this.attribs.get(name) != null; + } + + @Override + public Object clone() throws CloneNotSupportedException { + final BasicClientCookie clone = (BasicClientCookie) super.clone(); + clone.attribs = new HashMap(this.attribs); + return clone; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append("[version: "); + buffer.append(Integer.toString(this.cookieVersion)); + buffer.append("]"); + buffer.append("[name: "); + buffer.append(this.name); + buffer.append("]"); + buffer.append("[value: "); + buffer.append(this.value); + buffer.append("]"); + buffer.append("[domain: "); + buffer.append(this.cookieDomain); + buffer.append("]"); + buffer.append("[path: "); + buffer.append(this.cookiePath); + buffer.append("]"); + buffer.append("[expiry: "); + buffer.append(this.cookieExpiryDate); + buffer.append("]"); + return buffer.toString(); + } + + // ----------------------------------------------------- Instance Variables + + /** Cookie name */ + private final String name; + + /** Cookie attributes as specified by the origin server */ + private Map attribs; + + /** Cookie value */ + private String value; + + /** Comment attribute. */ + private String cookieComment; + + /** Domain attribute. */ + private String cookieDomain; + + /** Expiration {@link Date}. */ + private Date cookieExpiryDate; + + /** Path attribute. */ + private String cookiePath; + + /** My secure flag. */ + private boolean isSecure; + + /** The version of the cookie specification I was created from. */ + private int cookieVersion; + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie2.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie2.java new file mode 100644 index 000000000..7ed0f82b6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicClientCookie2.java @@ -0,0 +1,101 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Date; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.SetCookie2; + +/** + * Default implementation of {@link SetCookie2}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicClientCookie2 extends BasicClientCookie implements SetCookie2 { + + private static final long serialVersionUID = -7744598295706617057L; + + private String commentURL; + private int[] ports; + private boolean discard; + + /** + * Default Constructor taking a name and a value. The value may be null. + * + * @param name The name. + * @param value The value. + */ + public BasicClientCookie2(final String name, final String value) { + super(name, value); + } + + @Override + public int[] getPorts() { + return this.ports; + } + + public void setPorts(final int[] ports) { + this.ports = ports; + } + + @Override + public String getCommentURL() { + return this.commentURL; + } + + public void setCommentURL(final String commentURL) { + this.commentURL = commentURL; + } + + public void setDiscard(final boolean discard) { + this.discard = discard; + } + + @Override + public boolean isPersistent() { + return !this.discard && super.isPersistent(); + } + + @Override + public boolean isExpired(final Date date) { + return this.discard || super.isExpired(date); + } + + @Override + public Object clone() throws CloneNotSupportedException { + final BasicClientCookie2 clone = (BasicClientCookie2) super.clone(); + if (this.ports != null) { + clone.ports = this.ports.clone(); + } + return clone; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicCommentHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicCommentHandler.java new file mode 100644 index 000000000..69d65b961 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicCommentHandler.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class BasicCommentHandler extends AbstractCookieAttributeHandler { + + public BasicCommentHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + cookie.setComment(value); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicDomainHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicDomainHandler.java new file mode 100644 index 000000000..bad00ec48 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicDomainHandler.java @@ -0,0 +1,116 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class BasicDomainHandler implements CookieAttributeHandler { + + public BasicDomainHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (value == null) { + throw new MalformedCookieException("Missing value for domain attribute"); + } + if (value.trim().length() == 0) { + throw new MalformedCookieException("Blank value for domain attribute"); + } + cookie.setDomain(value); + } + + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + // Validate the cookies domain attribute. NOTE: Domains without + // any dots are allowed to support hosts on private LANs that don't + // have DNS names. Since they have no dots, to domain-match the + // request-host and domain must be identical for the cookie to sent + // back to the origin-server. + final String host = origin.getHost(); + String domain = cookie.getDomain(); + if (domain == null) { + throw new CookieRestrictionViolationException("Cookie domain may not be null"); + } + if (host.contains(".")) { + // Not required to have at least two dots. RFC 2965. + // A Set-Cookie2 with Domain=ajax.com will be accepted. + + // domain must match host + if (!host.endsWith(domain)) { + if (domain.startsWith(".")) { + domain = domain.substring(1, domain.length()); + } + if (!host.equals(domain)) { + throw new CookieRestrictionViolationException( + "Illegal domain attribute \"" + domain + + "\". Domain of origin: \"" + host + "\""); + } + } + } else { + if (!host.equals(domain)) { + throw new CookieRestrictionViolationException( + "Illegal domain attribute \"" + domain + + "\". Domain of origin: \"" + host + "\""); + } + } + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final String host = origin.getHost(); + String domain = cookie.getDomain(); + if (domain == null) { + return false; + } + if (host.equals(domain)) { + return true; + } + if (!domain.startsWith(".")) { + domain = '.' + domain; + } + return host.endsWith(domain) || host.equals(domain.substring(1)); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicExpiresHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicExpiresHandler.java new file mode 100644 index 000000000..9b9dee5b2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicExpiresHandler.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Date; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class BasicExpiresHandler extends AbstractCookieAttributeHandler { + + /** Valid date patterns */ + private final String[] datepatterns; + + public BasicExpiresHandler(final String[] datepatterns) { + Args.notNull(datepatterns, "Array of date patterns"); + this.datepatterns = datepatterns; + } + + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (value == null) { + throw new MalformedCookieException("Missing value for expires attribute"); + } + final Date expiry = DateUtils.parseDate(value, this.datepatterns); + if (expiry == null) { + throw new MalformedCookieException("Unable to parse expires attribute: " + + value); + } + cookie.setExpiryDate(expiry); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicMaxAgeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicMaxAgeHandler.java new file mode 100644 index 000000000..750361b22 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicMaxAgeHandler.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Date; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class BasicMaxAgeHandler extends AbstractCookieAttributeHandler { + + public BasicMaxAgeHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (value == null) { + throw new MalformedCookieException("Missing value for max-age attribute"); + } + final int age; + try { + age = Integer.parseInt(value); + } catch (final NumberFormatException e) { + throw new MalformedCookieException ("Invalid max-age attribute: " + + value); + } + if (age < 0) { + throw new MalformedCookieException ("Negative max-age attribute: " + + value); + } + cookie.setExpiryDate(new Date(System.currentTimeMillis() + age * 1000L)); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicPathHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicPathHandler.java new file mode 100644 index 000000000..e73657148 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicPathHandler.java @@ -0,0 +1,87 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.TextUtils; + +/** + * + * @since 4.0 + */ +@Immutable +public class BasicPathHandler implements CookieAttributeHandler { + + public BasicPathHandler() { + super(); + } + + public void parse( + final SetCookie cookie, final String value) throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + cookie.setPath(!TextUtils.isBlank(value) ? value : "/"); + } + + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + if (!match(cookie, origin)) { + throw new CookieRestrictionViolationException( + "Illegal path attribute \"" + cookie.getPath() + + "\". Path of origin: \"" + origin.getPath() + "\""); + } + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final String targetpath = origin.getPath(); + String topmostPath = cookie.getPath(); + if (topmostPath == null) { + topmostPath = "/"; + } + if (topmostPath.length() > 1 && topmostPath.endsWith("/")) { + topmostPath = topmostPath.substring(0, topmostPath.length() - 1); + } + boolean match = targetpath.startsWith (topmostPath); + // if there is a match and these values are not exactly the same we have + // to make sure we're not matcing "/foobar" and "/foo" + if (match && targetpath.length() != topmostPath.length()) { + if (!topmostPath.endsWith("/")) { + match = (targetpath.charAt(topmostPath.length()) == '/'); + } + } + return match; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicSecureHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicSecureHandler.java new file mode 100644 index 000000000..939741fc3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BasicSecureHandler.java @@ -0,0 +1,60 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class BasicSecureHandler extends AbstractCookieAttributeHandler { + + public BasicSecureHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + cookie.setSecure(true); + } + + @Override + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + return !cookie.isSecure() || origin.isSecure(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpec.java new file mode 100644 index 000000000..bdb06090d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpec.java @@ -0,0 +1,207 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.List; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SM; +import ch.boye.httpclientandroidlib.cookie.SetCookie2; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * 'Meta' cookie specification that picks up a cookie policy based on + * the format of cookies sent with the HTTP response. + * + * @since 4.0 + */ +@NotThreadSafe // CookieSpec fields are @NotThreadSafe +public class BestMatchSpec implements CookieSpec { + + private final String[] datepatterns; + private final boolean oneHeader; + + // Cached values of CookieSpec instances + private RFC2965Spec strict; // @NotThreadSafe + private RFC2109Spec obsoleteStrict; // @NotThreadSafe + private BrowserCompatSpec compat; // @NotThreadSafe + + public BestMatchSpec(final String[] datepatterns, final boolean oneHeader) { + super(); + this.datepatterns = datepatterns == null ? null : datepatterns.clone(); + this.oneHeader = oneHeader; + } + + public BestMatchSpec() { + this(null, false); + } + + private RFC2965Spec getStrict() { + if (this.strict == null) { + this.strict = new RFC2965Spec(this.datepatterns, this.oneHeader); + } + return strict; + } + + private RFC2109Spec getObsoleteStrict() { + if (this.obsoleteStrict == null) { + this.obsoleteStrict = new RFC2109Spec(this.datepatterns, this.oneHeader); + } + return obsoleteStrict; + } + + private BrowserCompatSpec getCompat() { + if (this.compat == null) { + this.compat = new BrowserCompatSpec(this.datepatterns); + } + return compat; + } + + public List parse( + final Header header, + final CookieOrigin origin) throws MalformedCookieException { + Args.notNull(header, "Header"); + Args.notNull(origin, "Cookie origin"); + HeaderElement[] helems = header.getElements(); + boolean versioned = false; + boolean netscape = false; + for (final HeaderElement helem: helems) { + if (helem.getParameterByName("version") != null) { + versioned = true; + } + if (helem.getParameterByName("expires") != null) { + netscape = true; + } + } + if (netscape || !versioned) { + // Need to parse the header again, because Netscape style cookies do not correctly + // support multiple header elements (comma cannot be treated as an element separator) + final NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT; + final CharArrayBuffer buffer; + final ParserCursor cursor; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + cursor = new ParserCursor( + ((FormattedHeader) header).getValuePos(), + buffer.length()); + } else { + final String s = header.getValue(); + if (s == null) { + throw new MalformedCookieException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + cursor = new ParserCursor(0, buffer.length()); + } + helems = new HeaderElement[] { parser.parseHeader(buffer, cursor) }; + return getCompat().parse(helems, origin); + } else { + if (SM.SET_COOKIE2.equals(header.getName())) { + return getStrict().parse(helems, origin); + } else { + return getObsoleteStrict().parse(helems, origin); + } + } + } + + public void validate( + final Cookie cookie, + final CookieOrigin origin) throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + if (cookie.getVersion() > 0) { + if (cookie instanceof SetCookie2) { + getStrict().validate(cookie, origin); + } else { + getObsoleteStrict().validate(cookie, origin); + } + } else { + getCompat().validate(cookie, origin); + } + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + if (cookie.getVersion() > 0) { + if (cookie instanceof SetCookie2) { + return getStrict().match(cookie, origin); + } else { + return getObsoleteStrict().match(cookie, origin); + } + } else { + return getCompat().match(cookie, origin); + } + } + + public List

    formatCookies(final List cookies) { + Args.notNull(cookies, "List of cookies"); + int version = Integer.MAX_VALUE; + boolean isSetCookie2 = true; + for (final Cookie cookie: cookies) { + if (!(cookie instanceof SetCookie2)) { + isSetCookie2 = false; + } + if (cookie.getVersion() < version) { + version = cookie.getVersion(); + } + } + if (version > 0) { + if (isSetCookie2) { + return getStrict().formatCookies(cookies); + } else { + return getObsoleteStrict().formatCookies(cookies); + } + } else { + return getCompat().formatCookies(cookies); + } + } + + public int getVersion() { + return getStrict().getVersion(); + } + + public Header getVersionHeader() { + return getStrict().getVersionHeader(); + } + + @Override + public String toString() { + return "best-match"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpecFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpecFactory.java new file mode 100644 index 000000000..c20e4bcd1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BestMatchSpecFactory.java @@ -0,0 +1,86 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecFactory; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link CookieSpecProvider} implementation that creates and initializes + * {@link BestMatchSpec} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class BestMatchSpecFactory implements CookieSpecFactory, CookieSpecProvider { + + private final String[] datepatterns; + private final boolean oneHeader; + + public BestMatchSpecFactory(final String[] datepatterns, final boolean oneHeader) { + super(); + this.datepatterns = datepatterns; + this.oneHeader = oneHeader; + } + + public BestMatchSpecFactory() { + this(null, false); + } + + public CookieSpec newInstance(final HttpParams params) { + if (params != null) { + + String[] patterns = null; + final Collection param = (Collection) params.getParameter( + CookieSpecPNames.DATE_PATTERNS); + if (param != null) { + patterns = new String[param.size()]; + patterns = param.toArray(patterns); + } + final boolean singleHeader = params.getBooleanParameter( + CookieSpecPNames.SINGLE_COOKIE_HEADER, false); + + return new BestMatchSpec(patterns, singleHeader); + } else { + return new BestMatchSpec(); + } + } + + public CookieSpec create(final HttpContext context) { + return new BestMatchSpec(this.datepatterns, this.oneHeader); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpec.java new file mode 100644 index 000000000..6caa71987 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpec.java @@ -0,0 +1,219 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.ArrayList; +import java.util.List; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SM; +import ch.boye.httpclientandroidlib.message.BasicHeaderElement; +import ch.boye.httpclientandroidlib.message.BasicHeaderValueFormatter; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + + +/** + * Cookie specification that strives to closely mimic (mis)behavior of + * common web browser applications such as Microsoft Internet Explorer + * and Mozilla FireFox. + * + * + * @since 4.0 + */ +@NotThreadSafe // superclass is @NotThreadSafe +public class BrowserCompatSpec extends CookieSpecBase { + + + private static final String[] DEFAULT_DATE_PATTERNS = new String[] { + DateUtils.PATTERN_RFC1123, + DateUtils.PATTERN_RFC1036, + DateUtils.PATTERN_ASCTIME, + "EEE, dd-MMM-yyyy HH:mm:ss z", + "EEE, dd-MMM-yyyy HH-mm-ss z", + "EEE, dd MMM yy HH:mm:ss z", + "EEE dd-MMM-yyyy HH:mm:ss z", + "EEE dd MMM yyyy HH:mm:ss z", + "EEE dd-MMM-yyyy HH-mm-ss z", + "EEE dd-MMM-yy HH:mm:ss z", + "EEE dd MMM yy HH:mm:ss z", + "EEE,dd-MMM-yy HH:mm:ss z", + "EEE,dd-MMM-yyyy HH:mm:ss z", + "EEE, dd-MM-yyyy HH:mm:ss z", + }; + + private final String[] datepatterns; + + /** Default constructor */ + public BrowserCompatSpec(final String[] datepatterns, final BrowserCompatSpecFactory.SecurityLevel securityLevel) { + super(); + if (datepatterns != null) { + this.datepatterns = datepatterns.clone(); + } else { + this.datepatterns = DEFAULT_DATE_PATTERNS; + } + switch (securityLevel) { + case SECURITYLEVEL_DEFAULT: + registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler()); + break; + case SECURITYLEVEL_IE_MEDIUM: + registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler() { + @Override + public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { + // No validation + } + } + ); + break; + default: + throw new RuntimeException("Unknown security level"); + } + + registerAttribHandler(ClientCookie.DOMAIN_ATTR, new BasicDomainHandler()); + registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler()); + registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler()); + registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler()); + registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler( + this.datepatterns)); + registerAttribHandler(ClientCookie.VERSION_ATTR, new BrowserCompatVersionAttributeHandler()); + } + + /** Default constructor */ + public BrowserCompatSpec(final String[] datepatterns) { + this(datepatterns, BrowserCompatSpecFactory.SecurityLevel.SECURITYLEVEL_DEFAULT); + } + + /** Default constructor */ + public BrowserCompatSpec() { + this(null, BrowserCompatSpecFactory.SecurityLevel.SECURITYLEVEL_DEFAULT); + } + + public List parse(final Header header, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(header, "Header"); + Args.notNull(origin, "Cookie origin"); + final String headername = header.getName(); + if (!headername.equalsIgnoreCase(SM.SET_COOKIE)) { + throw new MalformedCookieException("Unrecognized cookie header '" + + header.toString() + "'"); + } + HeaderElement[] helems = header.getElements(); + boolean versioned = false; + boolean netscape = false; + for (final HeaderElement helem: helems) { + if (helem.getParameterByName("version") != null) { + versioned = true; + } + if (helem.getParameterByName("expires") != null) { + netscape = true; + } + } + if (netscape || !versioned) { + // Need to parse the header again, because Netscape style cookies do not correctly + // support multiple header elements (comma cannot be treated as an element separator) + final NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT; + final CharArrayBuffer buffer; + final ParserCursor cursor; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + cursor = new ParserCursor( + ((FormattedHeader) header).getValuePos(), + buffer.length()); + } else { + final String s = header.getValue(); + if (s == null) { + throw new MalformedCookieException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + cursor = new ParserCursor(0, buffer.length()); + } + helems = new HeaderElement[] { parser.parseHeader(buffer, cursor) }; + } + return parse(helems, origin); + } + + private static boolean isQuoteEnclosed(final String s) { + return s != null && s.startsWith("\"") && s.endsWith("\""); + } + + public List
    formatCookies(final List cookies) { + Args.notEmpty(cookies, "List of cookies"); + final CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size()); + buffer.append(SM.COOKIE); + buffer.append(": "); + for (int i = 0; i < cookies.size(); i++) { + final Cookie cookie = cookies.get(i); + if (i > 0) { + buffer.append("; "); + } + final String cookieName = cookie.getName(); + final String cookieValue = cookie.getValue(); + if (cookie.getVersion() > 0 && !isQuoteEnclosed(cookieValue)) { + BasicHeaderValueFormatter.INSTANCE.formatHeaderElement( + buffer, + new BasicHeaderElement(cookieName, cookieValue), + false); + } else { + // Netscape style cookies do not support quoted values + buffer.append(cookieName); + buffer.append("="); + if (cookieValue != null) { + buffer.append(cookieValue); + } + } + } + final List
    headers = new ArrayList
    (1); + headers.add(new BufferedHeader(buffer)); + return headers; + } + + public int getVersion() { + return 0; + } + + public Header getVersionHeader() { + return null; + } + + @Override + public String toString() { + return "compatibility"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpecFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpecFactory.java new file mode 100644 index 000000000..f6239b4f3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatSpecFactory.java @@ -0,0 +1,92 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecFactory; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link CookieSpecProvider} implementation that creates and initializes + * {@link BrowserCompatSpec} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class BrowserCompatSpecFactory implements CookieSpecFactory, CookieSpecProvider { + + public enum SecurityLevel { + SECURITYLEVEL_DEFAULT, + SECURITYLEVEL_IE_MEDIUM + } + + private final String[] datepatterns; + private final SecurityLevel securityLevel; + + public BrowserCompatSpecFactory(final String[] datepatterns, final SecurityLevel securityLevel) { + super(); + this.datepatterns = datepatterns; + this.securityLevel = securityLevel; + } + + public BrowserCompatSpecFactory(final String[] datepatterns) { + this(null, SecurityLevel.SECURITYLEVEL_DEFAULT); + } + + public BrowserCompatSpecFactory() { + this(null, SecurityLevel.SECURITYLEVEL_DEFAULT); + } + + public CookieSpec newInstance(final HttpParams params) { + if (params != null) { + + String[] patterns = null; + final Collection param = (Collection) params.getParameter( + CookieSpecPNames.DATE_PATTERNS); + if (param != null) { + patterns = new String[param.size()]; + patterns = param.toArray(patterns); + } + return new BrowserCompatSpec(patterns, securityLevel); + } else { + return new BrowserCompatSpec(null, securityLevel); + } + } + + public CookieSpec create(final HttpContext context) { + return new BrowserCompatSpec(this.datepatterns); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatVersionAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatVersionAttributeHandler.java new file mode 100644 index 000000000..210fac962 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/BrowserCompatVersionAttributeHandler.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * "Version" cookie attribute handler for BrowserCompat cookie spec. + * + * @since 4.3 + */ +@Immutable +public class BrowserCompatVersionAttributeHandler extends + AbstractCookieAttributeHandler { + + public BrowserCompatVersionAttributeHandler() { + super(); + } + + /** + * Parse cookie version attribute. + */ + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (value == null) { + throw new MalformedCookieException("Missing value for version attribute"); + } + int version = 0; + try { + version = Integer.parseInt(value); + } catch (final NumberFormatException e) { + // Just ignore invalid versions + } + cookie.setVersion(version); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/CookieSpecBase.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/CookieSpecBase.java new file mode 100644 index 000000000..e59c3686e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/CookieSpecBase.java @@ -0,0 +1,121 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Cookie management functions shared by all specification. + * + * + * @since 4.0 + */ +@NotThreadSafe // AbstractCookieSpec is not thread-safe +public abstract class CookieSpecBase extends AbstractCookieSpec { + + protected static String getDefaultPath(final CookieOrigin origin) { + String defaultPath = origin.getPath(); + int lastSlashIndex = defaultPath.lastIndexOf('/'); + if (lastSlashIndex >= 0) { + if (lastSlashIndex == 0) { + //Do not remove the very first slash + lastSlashIndex = 1; + } + defaultPath = defaultPath.substring(0, lastSlashIndex); + } + return defaultPath; + } + + protected static String getDefaultDomain(final CookieOrigin origin) { + return origin.getHost(); + } + + protected List parse(final HeaderElement[] elems, final CookieOrigin origin) + throws MalformedCookieException { + final List cookies = new ArrayList(elems.length); + for (final HeaderElement headerelement : elems) { + final String name = headerelement.getName(); + final String value = headerelement.getValue(); + if (name == null || name.length() == 0) { + throw new MalformedCookieException("Cookie name may not be empty"); + } + + final BasicClientCookie cookie = new BasicClientCookie(name, value); + cookie.setPath(getDefaultPath(origin)); + cookie.setDomain(getDefaultDomain(origin)); + + // cycle through the parameters + final NameValuePair[] attribs = headerelement.getParameters(); + for (int j = attribs.length - 1; j >= 0; j--) { + final NameValuePair attrib = attribs[j]; + final String s = attrib.getName().toLowerCase(Locale.ENGLISH); + + cookie.setAttribute(s, attrib.getValue()); + + final CookieAttributeHandler handler = findAttribHandler(s); + if (handler != null) { + handler.parse(cookie, attrib.getValue()); + } + } + cookies.add(cookie); + } + return cookies; + } + + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + for (final CookieAttributeHandler handler: getAttribHandlers()) { + handler.validate(cookie, origin); + } + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + for (final CookieAttributeHandler handler: getAttribHandlers()) { + if (!handler.match(cookie, origin)) { + return false; + } + } + return true; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateParseException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateParseException.java new file mode 100644 index 000000000..08b387272 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateParseException.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * An exception to indicate an error parsing a date string. + * + * @see DateUtils + * + * + * @since 4.0 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +@Immutable +public class DateParseException extends Exception { + + private static final long serialVersionUID = 4417696455000643370L; + + /** + * + */ + public DateParseException() { + super(); + } + + /** + * @param message the exception message + */ + public DateParseException(final String message) { + super(message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateUtils.java new file mode 100644 index 000000000..897d6fb00 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/DateUtils.java @@ -0,0 +1,156 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Date; +import java.util.TimeZone; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * A utility class for parsing and formatting HTTP dates as used in cookies and + * other headers. This class handles dates as defined by RFC 2616 section + * 3.3.1 as well as some other common non-standard formats. + * + * + * @since 4.0 + * + * @deprecated (4.3) Use {@link ch.boye.httpclientandroidlib.client.utils.DateUtils}. + */ +@Deprecated +@Immutable +public final class DateUtils { + + /** + * Date format pattern used to parse HTTP date headers in RFC 1123 format. + */ + public static final String PATTERN_RFC1123 = ch.boye.httpclientandroidlib.client.utils.DateUtils.PATTERN_RFC1123; + + /** + * Date format pattern used to parse HTTP date headers in RFC 1036 format. + */ + public static final String PATTERN_RFC1036 = ch.boye.httpclientandroidlib.client.utils.DateUtils.PATTERN_RFC1036; + + /** + * Date format pattern used to parse HTTP date headers in ANSI C + * asctime() format. + */ + public static final String PATTERN_ASCTIME = ch.boye.httpclientandroidlib.client.utils.DateUtils.PATTERN_ASCTIME; + + public static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + + /** + * Parses a date value. The formats used for parsing the date value are retrieved from + * the default http params. + * + * @param dateValue the date value to parse + * + * @return the parsed date + * + * @throws DateParseException if the value could not be parsed using any of the + * supported date formats + */ + public static Date parseDate(final String dateValue) throws DateParseException { + return parseDate(dateValue, null, null); + } + + /** + * Parses the date value using the given date formats. + * + * @param dateValue the date value to parse + * @param dateFormats the date formats to use + * + * @return the parsed date + * + * @throws DateParseException if none of the dataFormats could parse the dateValue + */ + public static Date parseDate(final String dateValue, final String[] dateFormats) + throws DateParseException { + return parseDate(dateValue, dateFormats, null); + } + + /** + * Parses the date value using the given date formats. + * + * @param dateValue the date value to parse + * @param dateFormats the date formats to use + * @param startDate During parsing, two digit years will be placed in the range + * startDate to startDate + 100 years. This value may + * be null. When null is given as a parameter, year + * 2000 will be used. + * + * @return the parsed date + * + * @throws DateParseException if none of the dataFormats could parse the dateValue + */ + public static Date parseDate( + final String dateValue, + final String[] dateFormats, + final Date startDate + ) throws DateParseException { + final Date d = ch.boye.httpclientandroidlib.client.utils.DateUtils.parseDate(dateValue, dateFormats, startDate); + if (d == null) { + throw new DateParseException("Unable to parse the date " + dateValue); + } + return d; + } + + /** + * Formats the given date according to the RFC 1123 pattern. + * + * @param date The date to format. + * @return An RFC 1123 formatted date string. + * + * @see #PATTERN_RFC1123 + */ + public static String formatDate(final Date date) { + return ch.boye.httpclientandroidlib.client.utils.DateUtils.formatDate(date); + } + + /** + * Formats the given date according to the specified pattern. The pattern + * must conform to that used by the {@link java.text.SimpleDateFormat simple + * date format} class. + * + * @param date The date to format. + * @param pattern The pattern to use for formatting the date. + * @return A formatted date string. + * + * @throws IllegalArgumentException If the given date pattern is invalid. + * + * @see java.text.SimpleDateFormat + */ + public static String formatDate(final Date date, final String pattern) { + return ch.boye.httpclientandroidlib.client.utils.DateUtils.formatDate(date, pattern); + } + + /** This class should not be instantiated. */ + private DateUtils() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpec.java new file mode 100644 index 000000000..3aa350a40 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpec.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collections; +import java.util.List; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; + +/** + * CookieSpec that ignores all cookies + * + * @since 4.1 + */ +@NotThreadSafe // superclass is @NotThreadSafe +public class IgnoreSpec extends CookieSpecBase { + + public int getVersion() { + return 0; + } + + public List parse(final Header header, final CookieOrigin origin) + throws MalformedCookieException { + return Collections.emptyList(); + } + + public List
    formatCookies(final List cookies) { + return Collections.emptyList(); + } + + public Header getVersionHeader() { + return null; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpecFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpecFactory.java new file mode 100644 index 000000000..426e25cad --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/IgnoreSpecFactory.java @@ -0,0 +1,58 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecFactory; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link CookieSpecProvider} implementation that ignores all cookies. + * + * @since 4.1 + */ +@Immutable +@SuppressWarnings("deprecation") +public class IgnoreSpecFactory implements CookieSpecFactory, CookieSpecProvider { + + public IgnoreSpecFactory() { + super(); + } + + public CookieSpec newInstance(final HttpParams params) { + return new IgnoreSpec(); + } + + public CookieSpec create(final HttpContext context) { + return new IgnoreSpec(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDomainHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDomainHandler.java new file mode 100644 index 000000000..adbd40e91 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDomainHandler.java @@ -0,0 +1,106 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Locale; +import java.util.StringTokenizer; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class NetscapeDomainHandler extends BasicDomainHandler { + + public NetscapeDomainHandler() { + super(); + } + + @Override + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + super.validate(cookie, origin); + // Perform Netscape Cookie draft specific validation + final String host = origin.getHost(); + final String domain = cookie.getDomain(); + if (host.contains(".")) { + final int domainParts = new StringTokenizer(domain, ".").countTokens(); + + if (isSpecialDomain(domain)) { + if (domainParts < 2) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + domain + + "\" violates the Netscape cookie specification for " + + "special domains"); + } + } else { + if (domainParts < 3) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + domain + + "\" violates the Netscape cookie specification"); + } + } + } + } + + /** + * Checks if the given domain is in one of the seven special + * top level domains defined by the Netscape cookie specification. + * @param domain The domain. + * @return True if the specified domain is "special" + */ + private static boolean isSpecialDomain(final String domain) { + final String ucDomain = domain.toUpperCase(Locale.ENGLISH); + return ucDomain.endsWith(".COM") + || ucDomain.endsWith(".EDU") + || ucDomain.endsWith(".NET") + || ucDomain.endsWith(".GOV") + || ucDomain.endsWith(".MIL") + || ucDomain.endsWith(".ORG") + || ucDomain.endsWith(".INT"); + } + + @Override + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final String host = origin.getHost(); + final String domain = cookie.getDomain(); + if (domain == null) { + return false; + } + return host.endsWith(domain); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftHeaderParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftHeaderParser.java new file mode 100644 index 000000000..89a59688f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftHeaderParser.java @@ -0,0 +1,138 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.ArrayList; +import java.util.List; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.message.BasicHeaderElement; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * + * @since 4.0 + */ +@Immutable +public class NetscapeDraftHeaderParser { + + public final static NetscapeDraftHeaderParser DEFAULT = new NetscapeDraftHeaderParser(); + + public NetscapeDraftHeaderParser() { + super(); + } + + public HeaderElement parseHeader( + final CharArrayBuffer buffer, + final ParserCursor cursor) throws ParseException { + Args.notNull(buffer, "Char array buffer"); + Args.notNull(cursor, "Parser cursor"); + final NameValuePair nvp = parseNameValuePair(buffer, cursor); + final List params = new ArrayList(); + while (!cursor.atEnd()) { + final NameValuePair param = parseNameValuePair(buffer, cursor); + params.add(param); + } + return new BasicHeaderElement( + nvp.getName(), + nvp.getValue(), params.toArray(new NameValuePair[params.size()])); + } + + private NameValuePair parseNameValuePair( + final CharArrayBuffer buffer, final ParserCursor cursor) { + boolean terminated = false; + + int pos = cursor.getPos(); + final int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + + // Find name + String name = null; + while (pos < indexTo) { + final char ch = buffer.charAt(pos); + if (ch == '=') { + break; + } + if (ch == ';') { + terminated = true; + break; + } + pos++; + } + + if (pos == indexTo) { + terminated = true; + name = buffer.substringTrimmed(indexFrom, indexTo); + } else { + name = buffer.substringTrimmed(indexFrom, pos); + pos++; + } + + if (terminated) { + cursor.updatePos(pos); + return new BasicNameValuePair(name, null); + } + + // Find value + String value = null; + int i1 = pos; + + while (pos < indexTo) { + final char ch = buffer.charAt(pos); + if (ch == ';') { + terminated = true; + break; + } + pos++; + } + + int i2 = pos; + // Trim leading white spaces + while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) { + i1++; + } + // Trim trailing white spaces + while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) { + i2--; + } + value = buffer.substring(i1, i2); + if (terminated) { + pos++; + } + cursor.updatePos(pos); + return new BasicNameValuePair(name, value); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpec.java new file mode 100644 index 000000000..139e5985c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpec.java @@ -0,0 +1,171 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.ArrayList; +import java.util.List; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SM; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * This {@link ch.boye.httpclientandroidlib.cookie.CookieSpec} implementation conforms to + * the original draft specification published by Netscape Communications. + * It should be avoided unless absolutely necessary for compatibility with + * legacy applications. + * + * @since 4.0 + */ +@NotThreadSafe // superclass is @NotThreadSafe +public class NetscapeDraftSpec extends CookieSpecBase { + + protected static final String EXPIRES_PATTERN = "EEE, dd-MMM-yy HH:mm:ss z"; + + private final String[] datepatterns; + + /** Default constructor */ + public NetscapeDraftSpec(final String[] datepatterns) { + super(); + if (datepatterns != null) { + this.datepatterns = datepatterns.clone(); + } else { + this.datepatterns = new String[] { EXPIRES_PATTERN }; + } + registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler()); + registerAttribHandler(ClientCookie.DOMAIN_ATTR, new NetscapeDomainHandler()); + registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler()); + registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler()); + registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler()); + registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler( + this.datepatterns)); + } + + /** Default constructor */ + public NetscapeDraftSpec() { + this(null); + } + + /** + * Parses the Set-Cookie value into an array of Cookies. + * + *

    Syntax of the Set-Cookie HTTP Response Header:

    + * + *

    This is the format a CGI script would use to add to + * the HTTP headers a new piece of data which is to be stored by + * the client for later retrieval.

    + * + *
    +      *  Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
    +      * 
    + * + *

    Please note that the Netscape draft specification does not fully conform to the HTTP + * header format. Comma character if present in Set-Cookie will not be treated + * as a header element separator

    + * + * @see + * The Cookie Spec. + * + * @param header the Set-Cookie received from the server + * @return an array of Cookies parsed from the Set-Cookie value + * @throws MalformedCookieException if an exception occurs during parsing + */ + public List parse(final Header header, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(header, "Header"); + Args.notNull(origin, "Cookie origin"); + if (!header.getName().equalsIgnoreCase(SM.SET_COOKIE)) { + throw new MalformedCookieException("Unrecognized cookie header '" + + header.toString() + "'"); + } + final NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT; + final CharArrayBuffer buffer; + final ParserCursor cursor; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + cursor = new ParserCursor( + ((FormattedHeader) header).getValuePos(), + buffer.length()); + } else { + final String s = header.getValue(); + if (s == null) { + throw new MalformedCookieException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + cursor = new ParserCursor(0, buffer.length()); + } + return parse(new HeaderElement[] { parser.parseHeader(buffer, cursor) }, origin); + } + + public List
    formatCookies(final List cookies) { + Args.notEmpty(cookies, "List of cookies"); + final CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size()); + buffer.append(SM.COOKIE); + buffer.append(": "); + for (int i = 0; i < cookies.size(); i++) { + final Cookie cookie = cookies.get(i); + if (i > 0) { + buffer.append("; "); + } + buffer.append(cookie.getName()); + final String s = cookie.getValue(); + if (s != null) { + buffer.append("="); + buffer.append(s); + } + } + final List
    headers = new ArrayList
    (1); + headers.add(new BufferedHeader(buffer)); + return headers; + } + + public int getVersion() { + return 0; + } + + public Header getVersionHeader() { + return null; + } + + @Override + public String toString() { + return "netscape"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpecFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpecFactory.java new file mode 100644 index 000000000..d1187a150 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/NetscapeDraftSpecFactory.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecFactory; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link CookieSpecProvider} implementation that creates and initializes + * {@link NetscapeDraftSpec} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class NetscapeDraftSpecFactory implements CookieSpecFactory, CookieSpecProvider { + + private final String[] datepatterns; + + public NetscapeDraftSpecFactory(final String[] datepatterns) { + super(); + this.datepatterns = datepatterns; + } + + public NetscapeDraftSpecFactory() { + this(null); + } + + public CookieSpec newInstance(final HttpParams params) { + if (params != null) { + + String[] patterns = null; + final Collection param = (Collection) params.getParameter( + CookieSpecPNames.DATE_PATTERNS); + if (param != null) { + patterns = new String[param.size()]; + patterns = param.toArray(patterns); + } + return new NetscapeDraftSpec(patterns); + } else { + return new NetscapeDraftSpec(); + } + } + + public CookieSpec create(final HttpContext context) { + return new NetscapeDraftSpec(this.datepatterns); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixFilter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixFilter.java new file mode 100644 index 000000000..4777918ba --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixFilter.java @@ -0,0 +1,132 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import ch.boye.httpclientandroidlib.client.utils.Punycode; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; + +/** + * Wraps a CookieAttributeHandler and leverages its match method + * to never match a suffix from a black list. May be used to provide + * additional security for cross-site attack types by preventing + * cookies from apparent domains that are not publicly available. + * An uptodate list of suffixes can be obtained from + * publicsuffix.org + * + * @since 4.0 + */ +public class PublicSuffixFilter implements CookieAttributeHandler { + private final CookieAttributeHandler wrapped; + private Set exceptions; + private Set suffixes; + + public PublicSuffixFilter(final CookieAttributeHandler wrapped) { + this.wrapped = wrapped; + } + + /** + * Sets the suffix blacklist patterns. + * A pattern can be "com", "*.jp" + * TODO add support for patterns like "lib.*.us" + * @param suffixes + */ + public void setPublicSuffixes(final Collection suffixes) { + this.suffixes = new HashSet(suffixes); + } + + /** + * Sets the exceptions from the blacklist. Exceptions can not be patterns. + * TODO add support for patterns + * @param exceptions + */ + public void setExceptions(final Collection exceptions) { + this.exceptions = new HashSet(exceptions); + } + + /** + * Never matches if the cookie's domain is from the blacklist. + */ + public boolean match(final Cookie cookie, final CookieOrigin origin) { + if (isForPublicSuffix(cookie)) { + return false; + } + return wrapped.match(cookie, origin); + } + + public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { + wrapped.parse(cookie, value); + } + + public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { + wrapped.validate(cookie, origin); + } + + private boolean isForPublicSuffix(final Cookie cookie) { + String domain = cookie.getDomain(); + if (domain.startsWith(".")) { + domain = domain.substring(1); + } + domain = Punycode.toUnicode(domain); + + // An exception rule takes priority over any other matching rule. + if (this.exceptions != null) { + if (this.exceptions.contains(domain)) { + return false; + } + } + + + if (this.suffixes == null) { + return false; + } + + do { + if (this.suffixes.contains(domain)) { + return true; + } + // patterns + if (domain.startsWith("*.")) { + domain = domain.substring(2); + } + final int nextdot = domain.indexOf('.'); + if (nextdot == -1) { + break; + } + domain = "*" + domain.substring(nextdot); + } while (domain.length() > 0); + + return false; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixListParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixListParser.java new file mode 100644 index 000000000..4f0374f99 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/PublicSuffixListParser.java @@ -0,0 +1,127 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collection; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Parses the list from publicsuffix.org + * and configures a PublicSuffixFilter. + * + * @since 4.0 + */ +@Immutable +public class PublicSuffixListParser { + private static final int MAX_LINE_LEN = 256; + private final PublicSuffixFilter filter; + + PublicSuffixListParser(final PublicSuffixFilter filter) { + this.filter = filter; + } + + /** + * Parses the public suffix list format. + * When creating the reader from the file, make sure to + * use the correct encoding (the original list is in UTF-8). + * + * @param list the suffix list. The caller is responsible for closing the reader. + * @throws IOException on error while reading from list + */ + public void parse(final Reader list) throws IOException { + final Collection rules = new ArrayList(); + final Collection exceptions = new ArrayList(); + final BufferedReader r = new BufferedReader(list); + final StringBuilder sb = new StringBuilder(256); + boolean more = true; + while (more) { + more = readLine(r, sb); + String line = sb.toString(); + if (line.length() == 0) { + continue; + } + if (line.startsWith("//")) + { + continue; //entire lines can also be commented using // + } + if (line.startsWith(".")) + { + line = line.substring(1); // A leading dot is optional + } + // An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule + final boolean isException = line.startsWith("!"); + if (isException) { + line = line.substring(1); + } + + if (isException) { + exceptions.add(line); + } else { + rules.add(line); + } + } + + filter.setPublicSuffixes(rules); + filter.setExceptions(exceptions); + } + + /** + * + * @param r + * @param sb + * @return false when the end of the stream is reached + * @throws IOException + */ + private boolean readLine(final Reader r, final StringBuilder sb) throws IOException { + sb.setLength(0); + int b; + boolean hitWhitespace = false; + while ((b = r.read()) != -1) { + final char c = (char) b; + if (c == '\n') { + break; + } + // Each line is only read up to the first whitespace + if (Character.isWhitespace(c)) { + hitWhitespace = true; + } + if (!hitWhitespace) { + sb.append(c); + } + if (sb.length() > MAX_LINE_LEN) + { + throw new IOException("Line too long"); // prevent excess memory usage + } + } + return (b != -1); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109DomainHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109DomainHandler.java new file mode 100644 index 000000000..d669dcb43 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109DomainHandler.java @@ -0,0 +1,120 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class RFC2109DomainHandler implements CookieAttributeHandler { + + public RFC2109DomainHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (value == null) { + throw new MalformedCookieException("Missing value for domain attribute"); + } + if (value.trim().length() == 0) { + throw new MalformedCookieException("Blank value for domain attribute"); + } + cookie.setDomain(value); + } + + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + String host = origin.getHost(); + final String domain = cookie.getDomain(); + if (domain == null) { + throw new CookieRestrictionViolationException("Cookie domain may not be null"); + } + if (!domain.equals(host)) { + int dotIndex = domain.indexOf('.'); + if (dotIndex == -1) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + domain + + "\" does not match the host \"" + + host + "\""); + } + // domain must start with dot + if (!domain.startsWith(".")) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + domain + + "\" violates RFC 2109: domain must start with a dot"); + } + // domain must have at least one embedded dot + dotIndex = domain.indexOf('.', 1); + if (dotIndex < 0 || dotIndex == domain.length() - 1) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + domain + + "\" violates RFC 2109: domain must contain an embedded dot"); + } + host = host.toLowerCase(Locale.ENGLISH); + if (!host.endsWith(domain)) { + throw new CookieRestrictionViolationException( + "Illegal domain attribute \"" + domain + + "\". Domain of origin: \"" + host + "\""); + } + // host minus domain may not contain any dots + final String hostWithoutDomain = host.substring(0, host.length() - domain.length()); + if (hostWithoutDomain.indexOf('.') != -1) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + domain + + "\" violates RFC 2109: host minus domain may not contain any dots"); + } + } + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final String host = origin.getHost(); + final String domain = cookie.getDomain(); + if (domain == null) { + return false; + } + return host.equals(domain) || (domain.startsWith(".") && host.endsWith(domain)); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109Spec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109Spec.java new file mode 100644 index 000000000..1292788a0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109Spec.java @@ -0,0 +1,240 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.utils.DateUtils; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookiePathComparator; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SM; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * RFC 2109 compliant {@link ch.boye.httpclientandroidlib.cookie.CookieSpec} implementation. + * This is an older version of the official HTTP state management specification + * superseded by RFC 2965. + * + * @see RFC2965Spec + * + * @since 4.0 + */ +@NotThreadSafe // superclass is @NotThreadSafe +public class RFC2109Spec extends CookieSpecBase { + + private final static CookiePathComparator PATH_COMPARATOR = new CookiePathComparator(); + + private final static String[] DATE_PATTERNS = { + DateUtils.PATTERN_RFC1123, + DateUtils.PATTERN_RFC1036, + DateUtils.PATTERN_ASCTIME + }; + + private final String[] datepatterns; + private final boolean oneHeader; + + /** Default constructor */ + public RFC2109Spec(final String[] datepatterns, final boolean oneHeader) { + super(); + if (datepatterns != null) { + this.datepatterns = datepatterns.clone(); + } else { + this.datepatterns = DATE_PATTERNS; + } + this.oneHeader = oneHeader; + registerAttribHandler(ClientCookie.VERSION_ATTR, new RFC2109VersionHandler()); + registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler()); + registerAttribHandler(ClientCookie.DOMAIN_ATTR, new RFC2109DomainHandler()); + registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler()); + registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler()); + registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler()); + registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler( + this.datepatterns)); + } + + /** Default constructor */ + public RFC2109Spec() { + this(null, false); + } + + public List parse(final Header header, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(header, "Header"); + Args.notNull(origin, "Cookie origin"); + if (!header.getName().equalsIgnoreCase(SM.SET_COOKIE)) { + throw new MalformedCookieException("Unrecognized cookie header '" + + header.toString() + "'"); + } + final HeaderElement[] elems = header.getElements(); + return parse(elems, origin); + } + + @Override + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + final String name = cookie.getName(); + if (name.indexOf(' ') != -1) { + throw new CookieRestrictionViolationException("Cookie name may not contain blanks"); + } + if (name.startsWith("$")) { + throw new CookieRestrictionViolationException("Cookie name may not start with $"); + } + super.validate(cookie, origin); + } + + public List
    formatCookies(final List cookies) { + Args.notEmpty(cookies, "List of cookies"); + List cookieList; + if (cookies.size() > 1) { + // Create a mutable copy and sort the copy. + cookieList = new ArrayList(cookies); + Collections.sort(cookieList, PATH_COMPARATOR); + } else { + cookieList = cookies; + } + if (this.oneHeader) { + return doFormatOneHeader(cookieList); + } else { + return doFormatManyHeaders(cookieList); + } + } + + private List
    doFormatOneHeader(final List cookies) { + int version = Integer.MAX_VALUE; + // Pick the lowest common denominator + for (final Cookie cookie : cookies) { + if (cookie.getVersion() < version) { + version = cookie.getVersion(); + } + } + final CharArrayBuffer buffer = new CharArrayBuffer(40 * cookies.size()); + buffer.append(SM.COOKIE); + buffer.append(": "); + buffer.append("$Version="); + buffer.append(Integer.toString(version)); + for (final Cookie cooky : cookies) { + buffer.append("; "); + final Cookie cookie = cooky; + formatCookieAsVer(buffer, cookie, version); + } + final List
    headers = new ArrayList
    (1); + headers.add(new BufferedHeader(buffer)); + return headers; + } + + private List
    doFormatManyHeaders(final List cookies) { + final List
    headers = new ArrayList
    (cookies.size()); + for (final Cookie cookie : cookies) { + final int version = cookie.getVersion(); + final CharArrayBuffer buffer = new CharArrayBuffer(40); + buffer.append("Cookie: "); + buffer.append("$Version="); + buffer.append(Integer.toString(version)); + buffer.append("; "); + formatCookieAsVer(buffer, cookie, version); + headers.add(new BufferedHeader(buffer)); + } + return headers; + } + + /** + * Return a name/value string suitable for sending in a "Cookie" + * header as defined in RFC 2109 for backward compatibility with cookie + * version 0 + * @param buffer The char array buffer to use for output + * @param name The cookie name + * @param value The cookie value + * @param version The cookie version + */ + protected void formatParamAsVer(final CharArrayBuffer buffer, + final String name, final String value, final int version) { + buffer.append(name); + buffer.append("="); + if (value != null) { + if (version > 0) { + buffer.append('\"'); + buffer.append(value); + buffer.append('\"'); + } else { + buffer.append(value); + } + } + } + + /** + * Return a string suitable for sending in a "Cookie" header + * as defined in RFC 2109 for backward compatibility with cookie version 0 + * @param buffer The char array buffer to use for output + * @param cookie The {@link Cookie} to be formatted as string + * @param version The version to use. + */ + protected void formatCookieAsVer(final CharArrayBuffer buffer, + final Cookie cookie, final int version) { + formatParamAsVer(buffer, cookie.getName(), cookie.getValue(), version); + if (cookie.getPath() != null) { + if (cookie instanceof ClientCookie + && ((ClientCookie) cookie).containsAttribute(ClientCookie.PATH_ATTR)) { + buffer.append("; "); + formatParamAsVer(buffer, "$Path", cookie.getPath(), version); + } + } + if (cookie.getDomain() != null) { + if (cookie instanceof ClientCookie + && ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) { + buffer.append("; "); + formatParamAsVer(buffer, "$Domain", cookie.getDomain(), version); + } + } + } + + public int getVersion() { + return 1; + } + + public Header getVersionHeader() { + return null; + } + + @Override + public String toString() { + return "rfc2109"; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109SpecFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109SpecFactory.java new file mode 100644 index 000000000..24802c351 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109SpecFactory.java @@ -0,0 +1,86 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecFactory; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link CookieSpecProvider} implementation that creates and initializes + * {@link RFC2109Spec} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class RFC2109SpecFactory implements CookieSpecFactory, CookieSpecProvider { + + private final String[] datepatterns; + private final boolean oneHeader; + + public RFC2109SpecFactory(final String[] datepatterns, final boolean oneHeader) { + super(); + this.datepatterns = datepatterns; + this.oneHeader = oneHeader; + } + + public RFC2109SpecFactory() { + this(null, false); + } + + public CookieSpec newInstance(final HttpParams params) { + if (params != null) { + + String[] patterns = null; + final Collection param = (Collection) params.getParameter( + CookieSpecPNames.DATE_PATTERNS); + if (param != null) { + patterns = new String[param.size()]; + patterns = param.toArray(patterns); + } + final boolean singleHeader = params.getBooleanParameter( + CookieSpecPNames.SINGLE_COOKIE_HEADER, false); + + return new RFC2109Spec(patterns, singleHeader); + } else { + return new RFC2109Spec(); + } + } + + public CookieSpec create(final HttpContext context) { + return new RFC2109Spec(this.datepatterns, this.oneHeader); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109VersionHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109VersionHandler.java new file mode 100644 index 000000000..a43fe9b22 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2109VersionHandler.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * + * @since 4.0 + */ +@Immutable +public class RFC2109VersionHandler extends AbstractCookieAttributeHandler { + + public RFC2109VersionHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (value == null) { + throw new MalformedCookieException("Missing value for version attribute"); + } + if (value.trim().length() == 0) { + throw new MalformedCookieException("Blank value for version attribute"); + } + try { + cookie.setVersion(Integer.parseInt(value)); + } catch (final NumberFormatException e) { + throw new MalformedCookieException("Invalid version: " + + e.getMessage()); + } + } + + @Override + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (cookie.getVersion() < 0) { + throw new CookieRestrictionViolationException("Cookie version may not be negative"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965CommentUrlAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965CommentUrlAttributeHandler.java new file mode 100644 index 000000000..10f5f40ec --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965CommentUrlAttributeHandler.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.cookie.SetCookie2; + +/** + * "CommentURL" cookie attribute handler for RFC 2965 cookie spec. + * + * @since 4.0 + */ +@Immutable +public class RFC2965CommentUrlAttributeHandler implements CookieAttributeHandler { + + public RFC2965CommentUrlAttributeHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String commenturl) + throws MalformedCookieException { + if (cookie instanceof SetCookie2) { + final SetCookie2 cookie2 = (SetCookie2) cookie; + cookie2.setCommentURL(commenturl); + } + } + + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + return true; + } + + } diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DiscardAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DiscardAttributeHandler.java new file mode 100644 index 000000000..c4bfca460 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DiscardAttributeHandler.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.cookie.SetCookie2; + +/** + * "Discard" cookie attribute handler for RFC 2965 cookie spec. + * + * @since 4.0 + */ +@Immutable +public class RFC2965DiscardAttributeHandler implements CookieAttributeHandler { + + public RFC2965DiscardAttributeHandler() { + super(); + } + + public void parse(final SetCookie cookie, final String commenturl) + throws MalformedCookieException { + if (cookie instanceof SetCookie2) { + final SetCookie2 cookie2 = (SetCookie2) cookie; + cookie2.setDiscard(true); + } + } + + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + return true; + } + + } diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DomainAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DomainAttributeHandler.java new file mode 100644 index 000000000..62c947c55 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965DomainAttributeHandler.java @@ -0,0 +1,185 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * "Domain" cookie attribute handler for RFC 2965 cookie spec. + * + * + * @since 3.1 + */ +@Immutable +public class RFC2965DomainAttributeHandler implements CookieAttributeHandler { + + public RFC2965DomainAttributeHandler() { + super(); + } + + /** + * Parse cookie domain attribute. + */ + public void parse( + final SetCookie cookie, final String domain) throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (domain == null) { + throw new MalformedCookieException( + "Missing value for domain attribute"); + } + if (domain.trim().length() == 0) { + throw new MalformedCookieException( + "Blank value for domain attribute"); + } + String s = domain; + s = s.toLowerCase(Locale.ENGLISH); + if (!domain.startsWith(".")) { + // Per RFC 2965 section 3.2.2 + // "... If an explicitly specified value does not start with + // a dot, the user agent supplies a leading dot ..." + // That effectively implies that the domain attribute + // MAY NOT be an IP address of a host name + s = '.' + s; + } + cookie.setDomain(s); + } + + /** + * Performs domain-match as defined by the RFC2965. + *

    + * Host A's name domain-matches host B's if + *

      + *
        their host name strings string-compare equal; or
      + *
        A is a HDN string and has the form NB, where N is a non-empty + * name string, B has the form .B', and B' is a HDN string. (So, + * x.y.com domain-matches .Y.com but not Y.com.)
      + *
    + * + * @param host host name where cookie is received from or being sent to. + * @param domain The cookie domain attribute. + * @return true if the specified host matches the given domain. + */ + public boolean domainMatch(final String host, final String domain) { + final boolean match = host.equals(domain) + || (domain.startsWith(".") && host.endsWith(domain)); + + return match; + } + + /** + * Validate cookie domain attribute. + */ + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final String host = origin.getHost().toLowerCase(Locale.ENGLISH); + if (cookie.getDomain() == null) { + throw new CookieRestrictionViolationException("Invalid cookie state: " + + "domain not specified"); + } + final String cookieDomain = cookie.getDomain().toLowerCase(Locale.ENGLISH); + + if (cookie instanceof ClientCookie + && ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) { + // Domain attribute must start with a dot + if (!cookieDomain.startsWith(".")) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + cookie.getDomain() + "\" violates RFC 2109: domain must start with a dot"); + } + + // Domain attribute must contain at least one embedded dot, + // or the value must be equal to .local. + final int dotIndex = cookieDomain.indexOf('.', 1); + if (((dotIndex < 0) || (dotIndex == cookieDomain.length() - 1)) + && (!cookieDomain.equals(".local"))) { + throw new CookieRestrictionViolationException( + "Domain attribute \"" + cookie.getDomain() + + "\" violates RFC 2965: the value contains no embedded dots " + + "and the value is not .local"); + } + + // The effective host name must domain-match domain attribute. + if (!domainMatch(host, cookieDomain)) { + throw new CookieRestrictionViolationException( + "Domain attribute \"" + cookie.getDomain() + + "\" violates RFC 2965: effective host name does not " + + "domain-match domain attribute."); + } + + // effective host name minus domain must not contain any dots + final String effectiveHostWithoutDomain = host.substring( + 0, host.length() - cookieDomain.length()); + if (effectiveHostWithoutDomain.indexOf('.') != -1) { + throw new CookieRestrictionViolationException("Domain attribute \"" + + cookie.getDomain() + "\" violates RFC 2965: " + + "effective host minus domain may not contain any dots"); + } + } else { + // Domain was not specified in header. In this case, domain must + // string match request host (case-insensitive). + if (!cookie.getDomain().equals(host)) { + throw new CookieRestrictionViolationException("Illegal domain attribute: \"" + + cookie.getDomain() + "\"." + + "Domain of origin: \"" + + host + "\""); + } + } + } + + /** + * Match cookie domain attribute. + */ + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final String host = origin.getHost().toLowerCase(Locale.ENGLISH); + final String cookieDomain = cookie.getDomain(); + + // The effective host name MUST domain-match the Domain + // attribute of the cookie. + if (!domainMatch(host, cookieDomain)) { + return false; + } + // effective host name minus domain must not contain any dots + final String effectiveHostWithoutDomain = host.substring( + 0, host.length() - cookieDomain.length()); + return effectiveHostWithoutDomain.indexOf('.') == -1; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965PortAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965PortAttributeHandler.java new file mode 100644 index 000000000..4835d8eb5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965PortAttributeHandler.java @@ -0,0 +1,160 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.StringTokenizer; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.cookie.SetCookie2; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * "Port" cookie attribute handler for RFC 2965 cookie spec. + * + * @since 4.0 + */ +@Immutable +public class RFC2965PortAttributeHandler implements CookieAttributeHandler { + + public RFC2965PortAttributeHandler() { + super(); + } + + /** + * Parses the given Port attribute value (e.g. "8000,8001,8002") + * into an array of ports. + * + * @param portValue port attribute value + * @return parsed array of ports + * @throws MalformedCookieException if there is a problem in + * parsing due to invalid portValue. + */ + private static int[] parsePortAttribute(final String portValue) + throws MalformedCookieException { + final StringTokenizer st = new StringTokenizer(portValue, ","); + final int[] ports = new int[st.countTokens()]; + try { + int i = 0; + while(st.hasMoreTokens()) { + ports[i] = Integer.parseInt(st.nextToken().trim()); + if (ports[i] < 0) { + throw new MalformedCookieException ("Invalid Port attribute."); + } + ++i; + } + } catch (final NumberFormatException e) { + throw new MalformedCookieException ("Invalid Port " + + "attribute: " + e.getMessage()); + } + return ports; + } + + /** + * Returns true if the given port exists in the given + * ports list. + * + * @param port port of host where cookie was received from or being sent to. + * @param ports port list + * @return true returns true if the given port exists in + * the given ports list; false otherwise. + */ + private static boolean portMatch(final int port, final int[] ports) { + boolean portInList = false; + for (final int port2 : ports) { + if (port == port2) { + portInList = true; + break; + } + } + return portInList; + } + + /** + * Parse cookie port attribute. + */ + public void parse(final SetCookie cookie, final String portValue) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (cookie instanceof SetCookie2) { + final SetCookie2 cookie2 = (SetCookie2) cookie; + if (portValue != null && portValue.trim().length() > 0) { + final int[] ports = parsePortAttribute(portValue); + cookie2.setPorts(ports); + } + } + } + + /** + * Validate cookie port attribute. If the Port attribute was specified + * in header, the request port must be in cookie's port list. + */ + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final int port = origin.getPort(); + if (cookie instanceof ClientCookie + && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) { + if (!portMatch(port, cookie.getPorts())) { + throw new CookieRestrictionViolationException( + "Port attribute violates RFC 2965: " + + "Request port not found in cookie's port list."); + } + } + } + + /** + * Match cookie port attribute. If the Port attribute is not specified + * in header, the cookie can be sent to any port. Otherwise, the request port + * must be in the cookie's port list. + */ + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + final int port = origin.getPort(); + if (cookie instanceof ClientCookie + && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) { + if (cookie.getPorts() == null) { + // Invalid cookie state: port not specified + return false; + } + if (!portMatch(port, cookie.getPorts())) { + return false; + } + } + return true; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965Spec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965Spec.java new file mode 100644 index 000000000..1c0dbc4cb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965Spec.java @@ -0,0 +1,239 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SM; +import ch.boye.httpclientandroidlib.message.BufferedHeader; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * RFC 2965 compliant {@link ch.boye.httpclientandroidlib.cookie.CookieSpec} implementation. + * + * @since 4.0 + */ +@NotThreadSafe // superclass is @NotThreadSafe +public class RFC2965Spec extends RFC2109Spec { + + /** + * Default constructor + * + */ + public RFC2965Spec() { + this(null, false); + } + + public RFC2965Spec(final String[] datepatterns, final boolean oneHeader) { + super(datepatterns, oneHeader); + registerAttribHandler(ClientCookie.DOMAIN_ATTR, new RFC2965DomainAttributeHandler()); + registerAttribHandler(ClientCookie.PORT_ATTR, new RFC2965PortAttributeHandler()); + registerAttribHandler(ClientCookie.COMMENTURL_ATTR, new RFC2965CommentUrlAttributeHandler()); + registerAttribHandler(ClientCookie.DISCARD_ATTR, new RFC2965DiscardAttributeHandler()); + registerAttribHandler(ClientCookie.VERSION_ATTR, new RFC2965VersionAttributeHandler()); + } + + @Override + public List parse( + final Header header, + final CookieOrigin origin) throws MalformedCookieException { + Args.notNull(header, "Header"); + Args.notNull(origin, "Cookie origin"); + if (!header.getName().equalsIgnoreCase(SM.SET_COOKIE2)) { + throw new MalformedCookieException("Unrecognized cookie header '" + + header.toString() + "'"); + } + final HeaderElement[] elems = header.getElements(); + return createCookies(elems, adjustEffectiveHost(origin)); + } + + @Override + protected List parse( + final HeaderElement[] elems, + final CookieOrigin origin) throws MalformedCookieException { + return createCookies(elems, adjustEffectiveHost(origin)); + } + + private List createCookies( + final HeaderElement[] elems, + final CookieOrigin origin) throws MalformedCookieException { + final List cookies = new ArrayList(elems.length); + for (final HeaderElement headerelement : elems) { + final String name = headerelement.getName(); + final String value = headerelement.getValue(); + if (name == null || name.length() == 0) { + throw new MalformedCookieException("Cookie name may not be empty"); + } + + final BasicClientCookie2 cookie = new BasicClientCookie2(name, value); + cookie.setPath(getDefaultPath(origin)); + cookie.setDomain(getDefaultDomain(origin)); + cookie.setPorts(new int [] { origin.getPort() }); + // cycle through the parameters + final NameValuePair[] attribs = headerelement.getParameters(); + + // Eliminate duplicate attributes. The first occurrence takes precedence + // See RFC2965: 3.2 Origin Server Role + final Map attribmap = + new HashMap(attribs.length); + for (int j = attribs.length - 1; j >= 0; j--) { + final NameValuePair param = attribs[j]; + attribmap.put(param.getName().toLowerCase(Locale.ENGLISH), param); + } + for (final Map.Entry entry : attribmap.entrySet()) { + final NameValuePair attrib = entry.getValue(); + final String s = attrib.getName().toLowerCase(Locale.ENGLISH); + + cookie.setAttribute(s, attrib.getValue()); + + final CookieAttributeHandler handler = findAttribHandler(s); + if (handler != null) { + handler.parse(cookie, attrib.getValue()); + } + } + cookies.add(cookie); + } + return cookies; + } + + @Override + public void validate( + final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + super.validate(cookie, adjustEffectiveHost(origin)); + } + + @Override + public boolean match(final Cookie cookie, final CookieOrigin origin) { + Args.notNull(cookie, "Cookie"); + Args.notNull(origin, "Cookie origin"); + return super.match(cookie, adjustEffectiveHost(origin)); + } + + /** + * Adds valid Port attribute value, e.g. "8000,8001,8002" + */ + @Override + protected void formatCookieAsVer(final CharArrayBuffer buffer, + final Cookie cookie, final int version) { + super.formatCookieAsVer(buffer, cookie, version); + // format port attribute + if (cookie instanceof ClientCookie) { + // Test if the port attribute as set by the origin server is not blank + final String s = ((ClientCookie) cookie).getAttribute(ClientCookie.PORT_ATTR); + if (s != null) { + buffer.append("; $Port"); + buffer.append("=\""); + if (s.trim().length() > 0) { + final int[] ports = cookie.getPorts(); + if (ports != null) { + final int len = ports.length; + for (int i = 0; i < len; i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(Integer.toString(ports[i])); + } + } + } + buffer.append("\""); + } + } + } + + /** + * Set 'effective host name' as defined in RFC 2965. + *

    + * If a host name contains no dots, the effective host name is + * that name with the string .local appended to it. Otherwise + * the effective host name is the same as the host name. Note + * that all effective host names contain at least one dot. + * + * @param origin origin where cookie is received from or being sent to. + */ + private static CookieOrigin adjustEffectiveHost(final CookieOrigin origin) { + String host = origin.getHost(); + + // Test if the host name appears to be a fully qualified DNS name, + // IPv4 address or IPv6 address + boolean isLocalHost = true; + for (int i = 0; i < host.length(); i++) { + final char ch = host.charAt(i); + if (ch == '.' || ch == ':') { + isLocalHost = false; + break; + } + } + if (isLocalHost) { + host += ".local"; + return new CookieOrigin( + host, + origin.getPort(), + origin.getPath(), + origin.isSecure()); + } else { + return origin; + } + } + + @Override + public int getVersion() { + return 1; + } + + @Override + public Header getVersionHeader() { + final CharArrayBuffer buffer = new CharArrayBuffer(40); + buffer.append(SM.COOKIE2); + buffer.append(": "); + buffer.append("$Version="); + buffer.append(Integer.toString(getVersion())); + return new BufferedHeader(buffer); + } + + @Override + public String toString() { + return "rfc2965"; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965SpecFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965SpecFactory.java new file mode 100644 index 000000000..83b60407c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965SpecFactory.java @@ -0,0 +1,86 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import java.util.Collection; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.CookieSpec; +import ch.boye.httpclientandroidlib.cookie.CookieSpecFactory; +import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider; +import ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link CookieSpecProvider} implementation that creates and initializes + * {@link RFC2965Spec} instances. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class RFC2965SpecFactory implements CookieSpecFactory, CookieSpecProvider { + + private final String[] datepatterns; + private final boolean oneHeader; + + public RFC2965SpecFactory(final String[] datepatterns, final boolean oneHeader) { + super(); + this.datepatterns = datepatterns; + this.oneHeader = oneHeader; + } + + public RFC2965SpecFactory() { + this(null, false); + } + + public CookieSpec newInstance(final HttpParams params) { + if (params != null) { + + String[] patterns = null; + final Collection param = (Collection) params.getParameter( + CookieSpecPNames.DATE_PATTERNS); + if (param != null) { + patterns = new String[param.size()]; + patterns = param.toArray(patterns); + } + final boolean singleHeader = params.getBooleanParameter( + CookieSpecPNames.SINGLE_COOKIE_HEADER, false); + + return new RFC2965Spec(patterns, singleHeader); + } else { + return new RFC2965Spec(); + } + } + + public CookieSpec create(final HttpContext context) { + return new RFC2965Spec(this.datepatterns, this.oneHeader); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965VersionAttributeHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965VersionAttributeHandler.java new file mode 100644 index 000000000..26ae4faa6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/RFC2965VersionAttributeHandler.java @@ -0,0 +1,94 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.cookie; + +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.cookie.ClientCookie; +import ch.boye.httpclientandroidlib.cookie.Cookie; +import ch.boye.httpclientandroidlib.cookie.CookieAttributeHandler; +import ch.boye.httpclientandroidlib.cookie.CookieOrigin; +import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException; +import ch.boye.httpclientandroidlib.cookie.MalformedCookieException; +import ch.boye.httpclientandroidlib.cookie.SetCookie; +import ch.boye.httpclientandroidlib.cookie.SetCookie2; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * "Version" cookie attribute handler for RFC 2965 cookie spec. + * + * @since 4.0 + */ +@Immutable +public class RFC2965VersionAttributeHandler implements CookieAttributeHandler { + + public RFC2965VersionAttributeHandler() { + super(); + } + + /** + * Parse cookie version attribute. + */ + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (value == null) { + throw new MalformedCookieException( + "Missing value for version attribute"); + } + int version = -1; + try { + version = Integer.parseInt(value); + } catch (final NumberFormatException e) { + version = -1; + } + if (version < 0) { + throw new MalformedCookieException("Invalid cookie version."); + } + cookie.setVersion(version); + } + + /** + * validate cookie version attribute. Version attribute is REQUIRED. + */ + public void validate(final Cookie cookie, final CookieOrigin origin) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + if (cookie instanceof SetCookie2) { + if (cookie instanceof ClientCookie + && !((ClientCookie) cookie).containsAttribute(ClientCookie.VERSION_ATTR)) { + throw new CookieRestrictionViolationException( + "Violates RFC 2965. Version attribute is required."); + } + } + } + + public boolean match(final Cookie cookie, final CookieOrigin origin) { + return true; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/package-info.java new file mode 100644 index 000000000..1950e7263 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/cookie/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default implementations of standard and common HTTP state + * management policies. + */ +package ch.boye.httpclientandroidlib.impl.cookie; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/DisallowIdentityContentLengthStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/DisallowIdentityContentLengthStrategy.java new file mode 100644 index 000000000..93556d22b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/DisallowIdentityContentLengthStrategy.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.entity; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; + +/** + * Decorator for {@link ContentLengthStrategy} implementations that disallows the use of + * identity transfer encoding. + * + * @since 4.2 + */ +@Immutable +public class DisallowIdentityContentLengthStrategy implements ContentLengthStrategy { + + public static final DisallowIdentityContentLengthStrategy INSTANCE = + new DisallowIdentityContentLengthStrategy(new LaxContentLengthStrategy(0)); + + private final ContentLengthStrategy contentLengthStrategy; + + public DisallowIdentityContentLengthStrategy(final ContentLengthStrategy contentLengthStrategy) { + super(); + this.contentLengthStrategy = contentLengthStrategy; + } + + public long determineLength(final HttpMessage message) throws HttpException { + final long result = this.contentLengthStrategy.determineLength(message); + if (result == ContentLengthStrategy.IDENTITY) { + throw new ProtocolException("Identity transfer encoding cannot be used"); + } + return result; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntityDeserializer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntityDeserializer.java new file mode 100644 index 000000000..5eba3638c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntityDeserializer.java @@ -0,0 +1,143 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.entity; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.entity.BasicHttpEntity; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.io.ChunkedInputStream; +import ch.boye.httpclientandroidlib.impl.io.ContentLengthInputStream; +import ch.boye.httpclientandroidlib.impl.io.IdentityInputStream; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * HTTP entity deserializer. + *

    + * This entity deserializer supports "chunked" and "identitiy" transfer-coding + * and content length delimited content. + *

    + * This class relies on a specific implementation of + * {@link ContentLengthStrategy} to determine the content length or transfer + * encoding of the entity. + *

    + * This class generates an instance of {@link HttpEntity} based on + * properties of the message. The content of the entity will be decoded + * transparently for the consumer. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.impl.BHttpConnectionBase} + */ +@Immutable // assuming injected dependencies are immutable +@Deprecated +public class EntityDeserializer { + + private final ContentLengthStrategy lenStrategy; + + public EntityDeserializer(final ContentLengthStrategy lenStrategy) { + super(); + this.lenStrategy = Args.notNull(lenStrategy, "Content length strategy"); + } + + /** + * Creates a {@link BasicHttpEntity} based on properties of the given + * message. The content of the entity is created by wrapping + * {@link SessionInputBuffer} with a content decoder depending on the + * transfer mechanism used by the message. + *

    + * This method is called by the public + * {@link #deserialize(SessionInputBuffer, HttpMessage)}. + * + * @param inbuffer the session input buffer. + * @param message the message. + * @return HTTP entity. + * @throws HttpException in case of HTTP protocol violation. + * @throws IOException in case of an I/O error. + */ + protected BasicHttpEntity doDeserialize( + final SessionInputBuffer inbuffer, + final HttpMessage message) throws HttpException, IOException { + final BasicHttpEntity entity = new BasicHttpEntity(); + + final long len = this.lenStrategy.determineLength(message); + if (len == ContentLengthStrategy.CHUNKED) { + entity.setChunked(true); + entity.setContentLength(-1); + entity.setContent(new ChunkedInputStream(inbuffer)); + } else if (len == ContentLengthStrategy.IDENTITY) { + entity.setChunked(false); + entity.setContentLength(-1); + entity.setContent(new IdentityInputStream(inbuffer)); + } else { + entity.setChunked(false); + entity.setContentLength(len); + entity.setContent(new ContentLengthInputStream(inbuffer, len)); + } + + final Header contentTypeHeader = message.getFirstHeader(HTTP.CONTENT_TYPE); + if (contentTypeHeader != null) { + entity.setContentType(contentTypeHeader); + } + final Header contentEncodingHeader = message.getFirstHeader(HTTP.CONTENT_ENCODING); + if (contentEncodingHeader != null) { + entity.setContentEncoding(contentEncodingHeader); + } + return entity; + } + + /** + * Creates an {@link HttpEntity} based on properties of the given message. + * The content of the entity is created by wrapping + * {@link SessionInputBuffer} with a content decoder depending on the + * transfer mechanism used by the message. + *

    + * The content of the entity is NOT retrieved by this method. + * + * @param inbuffer the session input buffer. + * @param message the message. + * @return HTTP entity. + * @throws HttpException in case of HTTP protocol violation. + * @throws IOException in case of an I/O error. + */ + public HttpEntity deserialize( + final SessionInputBuffer inbuffer, + final HttpMessage message) throws HttpException, IOException { + Args.notNull(inbuffer, "Session input buffer"); + Args.notNull(message, "HTTP message"); + return doDeserialize(inbuffer, message); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntitySerializer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntitySerializer.java new file mode 100644 index 000000000..f25115cef --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/EntitySerializer.java @@ -0,0 +1,121 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.entity; + +import java.io.IOException; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.io.ChunkedOutputStream; +import ch.boye.httpclientandroidlib.impl.io.ContentLengthOutputStream; +import ch.boye.httpclientandroidlib.impl.io.IdentityOutputStream; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * HTTP entity serializer. + *

    + * This entity serializer currently supports "chunked" and "identitiy" + * transfer-coding and content length delimited content. + *

    + * This class relies on a specific implementation of + * {@link ContentLengthStrategy} to determine the content length or transfer + * encoding of the entity. + *

    + * This class writes out the content of {@link HttpEntity} to the data stream + * using a transfer coding based on properties on the HTTP message. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.impl.BHttpConnectionBase} + */ +@Immutable // assuming injected dependencies are immutable +@Deprecated +public class EntitySerializer { + + private final ContentLengthStrategy lenStrategy; + + public EntitySerializer(final ContentLengthStrategy lenStrategy) { + super(); + this.lenStrategy = Args.notNull(lenStrategy, "Content length strategy"); + } + + /** + * Creates a transfer codec based on properties of the given HTTP message + * and returns {@link OutputStream} instance that transparently encodes + * output data as it is being written out to the output stream. + *

    + * This method is called by the public + * {@link #serialize(SessionOutputBuffer, HttpMessage, HttpEntity)}. + * + * @param outbuffer the session output buffer. + * @param message the HTTP message. + * @return output stream. + * @throws HttpException in case of HTTP protocol violation. + * @throws IOException in case of an I/O error. + */ + protected OutputStream doSerialize( + final SessionOutputBuffer outbuffer, + final HttpMessage message) throws HttpException, IOException { + final long len = this.lenStrategy.determineLength(message); + if (len == ContentLengthStrategy.CHUNKED) { + return new ChunkedOutputStream(outbuffer); + } else if (len == ContentLengthStrategy.IDENTITY) { + return new IdentityOutputStream(outbuffer); + } else { + return new ContentLengthOutputStream(outbuffer, len); + } + } + + /** + * Writes out the content of the given HTTP entity to the session output + * buffer based on properties of the given HTTP message. + * + * @param outbuffer the output session buffer. + * @param message the HTTP message. + * @param entity the HTTP entity to be written out. + * @throws HttpException in case of HTTP protocol violation. + * @throws IOException in case of an I/O error. + */ + public void serialize( + final SessionOutputBuffer outbuffer, + final HttpMessage message, + final HttpEntity entity) throws HttpException, IOException { + Args.notNull(outbuffer, "Session output buffer"); + Args.notNull(message, "HTTP message"); + Args.notNull(entity, "HTTP entity"); + final OutputStream outstream = doSerialize(outbuffer, message); + entity.writeTo(outstream); + outstream.close(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/LaxContentLengthStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/LaxContentLengthStrategy.java new file mode 100644 index 000000000..0f0dbaf15 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/LaxContentLengthStrategy.java @@ -0,0 +1,126 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.entity; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * The lax implementation of the content length strategy. This class will ignore + * unrecognized transfer encodings and malformed Content-Length + * header values. + *

    + * This class recognizes "chunked" and "identitiy" transfer-coding only. + * + * @since 4.0 + */ +@Immutable +public class LaxContentLengthStrategy implements ContentLengthStrategy { + + public static final LaxContentLengthStrategy INSTANCE = new LaxContentLengthStrategy(); + + private final int implicitLen; + + /** + * Creates LaxContentLengthStrategy instance with the given length used per default + * when content length is not explicitly specified in the message. + * + * @param implicitLen implicit content length. + * + * @since 4.2 + */ + public LaxContentLengthStrategy(final int implicitLen) { + super(); + this.implicitLen = implicitLen; + } + + /** + * Creates LaxContentLengthStrategy instance. {@link ContentLengthStrategy#IDENTITY} + * is used per default when content length is not explicitly specified in the message. + */ + public LaxContentLengthStrategy() { + this(IDENTITY); + } + + public long determineLength(final HttpMessage message) throws HttpException { + Args.notNull(message, "HTTP message"); + + final Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING); + // We use Transfer-Encoding if present and ignore Content-Length. + // RFC2616, 4.4 item number 3 + if (transferEncodingHeader != null) { + final HeaderElement[] encodings; + try { + encodings = transferEncodingHeader.getElements(); + } catch (final ParseException px) { + throw new ProtocolException + ("Invalid Transfer-Encoding header value: " + + transferEncodingHeader, px); + } + // The chunked encoding must be the last one applied RFC2616, 14.41 + final int len = encodings.length; + if (HTTP.IDENTITY_CODING.equalsIgnoreCase(transferEncodingHeader.getValue())) { + return IDENTITY; + } else if ((len > 0) && (HTTP.CHUNK_CODING.equalsIgnoreCase( + encodings[len - 1].getName()))) { + return CHUNKED; + } else { + return IDENTITY; + } + } + final Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN); + if (contentLengthHeader != null) { + long contentlen = -1; + final Header[] headers = message.getHeaders(HTTP.CONTENT_LEN); + for (int i = headers.length - 1; i >= 0; i--) { + final Header header = headers[i]; + try { + contentlen = Long.parseLong(header.getValue()); + break; + } catch (final NumberFormatException ignore) { + } + // See if we can have better luck with another header, if present + } + if (contentlen >= 0) { + return contentlen; + } else { + return IDENTITY; + } + } + return this.implicitLen; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/StrictContentLengthStrategy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/StrictContentLengthStrategy.java new file mode 100644 index 000000000..1693c3111 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/StrictContentLengthStrategy.java @@ -0,0 +1,116 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.entity; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * The strict implementation of the content length strategy. This class + * will throw {@link ProtocolException} if it encounters an unsupported + * transfer encoding or a malformed Content-Length header + * value. + *

    + * This class recognizes "chunked" and "identitiy" transfer-coding only. + * + * @since 4.0 + */ +@Immutable +public class StrictContentLengthStrategy implements ContentLengthStrategy { + + public static final StrictContentLengthStrategy INSTANCE = new StrictContentLengthStrategy(); + + private final int implicitLen; + + /** + * Creates StrictContentLengthStrategy instance with the given length used per default + * when content length is not explicitly specified in the message. + * + * @param implicitLen implicit content length. + * + * @since 4.2 + */ + public StrictContentLengthStrategy(final int implicitLen) { + super(); + this.implicitLen = implicitLen; + } + + /** + * Creates StrictContentLengthStrategy instance. {@link ContentLengthStrategy#IDENTITY} + * is used per default when content length is not explicitly specified in the message. + */ + public StrictContentLengthStrategy() { + this(IDENTITY); + } + + public long determineLength(final HttpMessage message) throws HttpException { + Args.notNull(message, "HTTP message"); + // Although Transfer-Encoding is specified as a list, in practice + // it is either missing or has the single value "chunked". So we + // treat it as a single-valued header here. + final Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING); + if (transferEncodingHeader != null) { + final String s = transferEncodingHeader.getValue(); + if (HTTP.CHUNK_CODING.equalsIgnoreCase(s)) { + if (message.getProtocolVersion().lessEquals(HttpVersion.HTTP_1_0)) { + throw new ProtocolException( + "Chunked transfer encoding not allowed for " + + message.getProtocolVersion()); + } + return CHUNKED; + } else if (HTTP.IDENTITY_CODING.equalsIgnoreCase(s)) { + return IDENTITY; + } else { + throw new ProtocolException( + "Unsupported transfer encoding: " + s); + } + } + final Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN); + if (contentLengthHeader != null) { + final String s = contentLengthHeader.getValue(); + try { + final long len = Long.parseLong(s); + if (len < 0) { + throw new ProtocolException("Negative content length: " + s); + } + return len; + } catch (final NumberFormatException e) { + throw new ProtocolException("Invalid content length: " + s); + } + } + return this.implicitLen; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/package-info.java new file mode 100644 index 000000000..7566c4570 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/entity/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default implementations of entity content strategies. + */ +package ch.boye.httpclientandroidlib.impl.entity; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/BackoffStrategyExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/BackoffStrategyExec.java new file mode 100644 index 000000000..a639da934 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/BackoffStrategyExec.java @@ -0,0 +1,104 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.BackoffManager; +import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * @since 4.3 + */ +@Immutable +public class BackoffStrategyExec implements ClientExecChain { + + private final ClientExecChain requestExecutor; + private final ConnectionBackoffStrategy connectionBackoffStrategy; + private final BackoffManager backoffManager; + + public BackoffStrategyExec( + final ClientExecChain requestExecutor, + final ConnectionBackoffStrategy connectionBackoffStrategy, + final BackoffManager backoffManager) { + super(); + Args.notNull(requestExecutor, "HTTP client request executor"); + Args.notNull(connectionBackoffStrategy, "Connection backoff strategy"); + Args.notNull(backoffManager, "Backoff manager"); + this.requestExecutor = requestExecutor; + this.connectionBackoffStrategy = connectionBackoffStrategy; + this.backoffManager = backoffManager; + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + Args.notNull(route, "HTTP route"); + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + CloseableHttpResponse out = null; + try { + out = this.requestExecutor.execute(route, request, context, execAware); + } catch (final Exception ex) { + if (out != null) { + out.close(); + } + if (this.connectionBackoffStrategy.shouldBackoff(ex)) { + this.backoffManager.backOff(route); + } + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + if (ex instanceof HttpException) { + throw (HttpException) ex; + } + if (ex instanceof IOException) { + throw (IOException) ex; + } + throw new UndeclaredThrowableException(ex); + } + if (this.connectionBackoffStrategy.shouldBackoff(out)) { + this.backoffManager.backOff(route); + } else { + this.backoffManager.probe(route); + } + return out; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ClientExecChain.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ClientExecChain.java new file mode 100644 index 000000000..2b5e3c8f6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ClientExecChain.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; + +/** + * This interface represents an element in the HTTP request execution chain. Each element can + * either be a decorator around another element that implements a cross cutting aspect or + * a self-contained executor capable of producing a response for the given request. + *

    + * Important: please note it is required for decorators that implement post execution aspects + * or response post-processing of any sort to release resources associated with the response + * by calling {@link CloseableHttpResponse#close()} methods in case of an I/O, protocol or + * runtime exception, or in case the response is not propagated to the caller. + * + * @since 4.3 + */ +public interface ClientExecChain { + + /** + * Executes th request either by transmitting it to the target server or + * by passing it onto the next executor in the request execution chain. + * + * @param route connection route. + * @param request current request. + * @param clientContext current HTTP context. + * @param execAware receiver of notifications of blocking I/O operations. + * @return HTTP response either received from the opposite endpoint + * or generated locally. + * @throws IOException in case of a I/O error. + * (this type of exceptions are potentially recoverable). + * @throws HttpException in case of an HTTP protocol error + * (usually this type of exceptions are non-recoverable). + */ + CloseableHttpResponse execute( + HttpRoute route, + HttpRequestWrapper request, + HttpClientContext clientContext, + HttpExecutionAware execAware) throws IOException, HttpException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ConnectionHolder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ConnectionHolder.java new file mode 100644 index 000000000..d9bb17286 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ConnectionHolder.java @@ -0,0 +1,153 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.concurrent.Cancellable; +import ch.boye.httpclientandroidlib.conn.ConnectionReleaseTrigger; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; + +/** + * Internal connection holder. + * + * @since 4.3 + */ +@ThreadSafe +class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeable { + + public HttpClientAndroidLog log; + + private final HttpClientConnectionManager manager; + private final HttpClientConnection managedConn; + private volatile boolean reusable; + private volatile Object state; + private volatile long validDuration; + private volatile TimeUnit tunit; + + private volatile boolean released; + + public ConnectionHolder( + final HttpClientAndroidLog log, + final HttpClientConnectionManager manager, + final HttpClientConnection managedConn) { + super(); + this.log = log; + this.manager = manager; + this.managedConn = managedConn; + } + + public boolean isReusable() { + return this.reusable; + } + + public void markReusable() { + this.reusable = true; + } + + public void markNonReusable() { + this.reusable = false; + } + + public void setState(final Object state) { + this.state = state; + } + + public void setValidFor(final long duration, final TimeUnit tunit) { + synchronized (this.managedConn) { + this.validDuration = duration; + this.tunit = tunit; + } + } + + public void releaseConnection() { + synchronized (this.managedConn) { + if (this.released) { + return; + } + this.released = true; + if (this.reusable) { + this.manager.releaseConnection(this.managedConn, + this.state, this.validDuration, this.tunit); + } else { + try { + this.managedConn.close(); + log.debug("Connection discarded"); + } catch (final IOException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage(), ex); + } + } finally { + this.manager.releaseConnection( + this.managedConn, null, 0, TimeUnit.MILLISECONDS); + } + } + } + } + + public void abortConnection() { + synchronized (this.managedConn) { + if (this.released) { + return; + } + this.released = true; + try { + this.managedConn.shutdown(); + log.debug("Connection discarded"); + } catch (final IOException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage(), ex); + } + } finally { + this.manager.releaseConnection( + this.managedConn, null, 0, TimeUnit.MILLISECONDS); + } + } + } + + public boolean cancel() { + final boolean alreadyReleased = this.released; + log.debug("Cancelling request execution"); + abortConnection(); + return !alreadyReleased; + } + + public boolean isReleased() { + return this.released; + } + + public void close() throws IOException { + abortConnection(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/HttpResponseProxy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/HttpResponseProxy.java new file mode 100644 index 000000000..1fdc027c8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/HttpResponseProxy.java @@ -0,0 +1,185 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * A proxy class for {@link ch.boye.httpclientandroidlib.HttpResponse} that can be used to release client connection + * associated with the original response. + * + * @since 4.3 + */ +@NotThreadSafe +class HttpResponseProxy implements CloseableHttpResponse { + + private final HttpResponse original; + private final ConnectionHolder connHolder; + + public HttpResponseProxy(final HttpResponse original, final ConnectionHolder connHolder) { + this.original = original; + this.connHolder = connHolder; + ResponseEntityProxy.enchance(original, connHolder); + } + + public void close() throws IOException { + if (this.connHolder != null) { + this.connHolder.abortConnection(); + } + } + + public StatusLine getStatusLine() { + return original.getStatusLine(); + } + + public void setStatusLine(final StatusLine statusline) { + original.setStatusLine(statusline); + } + + public void setStatusLine(final ProtocolVersion ver, final int code) { + original.setStatusLine(ver, code); + } + + public void setStatusLine(final ProtocolVersion ver, final int code, final String reason) { + original.setStatusLine(ver, code, reason); + } + + public void setStatusCode(final int code) throws IllegalStateException { + original.setStatusCode(code); + } + + public void setReasonPhrase(final String reason) throws IllegalStateException { + original.setReasonPhrase(reason); + } + + public HttpEntity getEntity() { + return original.getEntity(); + } + + public void setEntity(final HttpEntity entity) { + original.setEntity(entity); + } + + public Locale getLocale() { + return original.getLocale(); + } + + public void setLocale(final Locale loc) { + original.setLocale(loc); + } + + public ProtocolVersion getProtocolVersion() { + return original.getProtocolVersion(); + } + + public boolean containsHeader(final String name) { + return original.containsHeader(name); + } + + public Header[] getHeaders(final String name) { + return original.getHeaders(name); + } + + public Header getFirstHeader(final String name) { + return original.getFirstHeader(name); + } + + public Header getLastHeader(final String name) { + return original.getLastHeader(name); + } + + public Header[] getAllHeaders() { + return original.getAllHeaders(); + } + + public void addHeader(final Header header) { + original.addHeader(header); + } + + public void addHeader(final String name, final String value) { + original.addHeader(name, value); + } + + public void setHeader(final Header header) { + original.setHeader(header); + } + + public void setHeader(final String name, final String value) { + original.setHeader(name, value); + } + + public void setHeaders(final Header[] headers) { + original.setHeaders(headers); + } + + public void removeHeader(final Header header) { + original.removeHeader(header); + } + + public void removeHeaders(final String name) { + original.removeHeaders(name); + } + + public HeaderIterator headerIterator() { + return original.headerIterator(); + } + + public HeaderIterator headerIterator(final String name) { + return original.headerIterator(name); + } + + @Deprecated + public HttpParams getParams() { + return original.getParams(); + } + + @Deprecated + public void setParams(final HttpParams params) { + original.setParams(params); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("HttpResponseProxy{"); + sb.append(original); + sb.append('}'); + return sb.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MainClientExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MainClientExec.java new file mode 100644 index 000000000..cf1dbfde4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MainClientExec.java @@ -0,0 +1,568 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AUTH; +import ch.boye.httpclientandroidlib.auth.AuthProtocolState; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; +import ch.boye.httpclientandroidlib.client.NonRepeatableRequestException; +import ch.boye.httpclientandroidlib.client.UserTokenHandler; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl; +import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy; +import ch.boye.httpclientandroidlib.conn.ConnectionRequest; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.routing.BasicRouteDirector; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRouteDirector; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.entity.BufferedHttpEntity; +import ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator; +import ch.boye.httpclientandroidlib.impl.conn.ConnectionShutdownException; +import ch.boye.httpclientandroidlib.message.BasicHttpRequest; +import ch.boye.httpclientandroidlib.protocol.HttpCoreContext; +import ch.boye.httpclientandroidlib.protocol.HttpProcessor; +import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor; +import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor; +import ch.boye.httpclientandroidlib.protocol.RequestTargetHost; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * The last request executor in the HTTP request execution chain + * that is responsible for execution of request / response + * exchanges with the opposite endpoint. + * This executor will automatically retry the request in case + * of an authentication challenge by an intermediate proxy or + * by the target server. + * + * @since 4.3 + */ +@Immutable +public class MainClientExec implements ClientExecChain { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpRequestExecutor requestExecutor; + private final HttpClientConnectionManager connManager; + private final ConnectionReuseStrategy reuseStrategy; + private final ConnectionKeepAliveStrategy keepAliveStrategy; + private final HttpProcessor proxyHttpProcessor; + private final AuthenticationStrategy targetAuthStrategy; + private final AuthenticationStrategy proxyAuthStrategy; + private final HttpAuthenticator authenticator; + private final UserTokenHandler userTokenHandler; + private final HttpRouteDirector routeDirector; + + + public MainClientExec( + final HttpRequestExecutor requestExecutor, + final HttpClientConnectionManager connManager, + final ConnectionReuseStrategy reuseStrategy, + final ConnectionKeepAliveStrategy keepAliveStrategy, + final AuthenticationStrategy targetAuthStrategy, + final AuthenticationStrategy proxyAuthStrategy, + final UserTokenHandler userTokenHandler) { + Args.notNull(requestExecutor, "HTTP request executor"); + Args.notNull(connManager, "Client connection manager"); + Args.notNull(reuseStrategy, "Connection reuse strategy"); + Args.notNull(keepAliveStrategy, "Connection keep alive strategy"); + Args.notNull(targetAuthStrategy, "Target authentication strategy"); + Args.notNull(proxyAuthStrategy, "Proxy authentication strategy"); + Args.notNull(userTokenHandler, "User token handler"); + this.authenticator = new HttpAuthenticator(); + this.proxyHttpProcessor = new ImmutableHttpProcessor( + new RequestTargetHost(), new RequestClientConnControl()); + this.routeDirector = new BasicRouteDirector(); + this.requestExecutor = requestExecutor; + this.connManager = connManager; + this.reuseStrategy = reuseStrategy; + this.keepAliveStrategy = keepAliveStrategy; + this.targetAuthStrategy = targetAuthStrategy; + this.proxyAuthStrategy = proxyAuthStrategy; + this.userTokenHandler = userTokenHandler; + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + Args.notNull(route, "HTTP route"); + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + AuthState targetAuthState = context.getTargetAuthState(); + if (targetAuthState == null) { + targetAuthState = new AuthState(); + context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, targetAuthState); + } + AuthState proxyAuthState = context.getProxyAuthState(); + if (proxyAuthState == null) { + proxyAuthState = new AuthState(); + context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState); + } + + if (request instanceof HttpEntityEnclosingRequest) { + RequestEntityProxy.enhance((HttpEntityEnclosingRequest) request); + } + + Object userToken = context.getUserToken(); + + final ConnectionRequest connRequest = connManager.requestConnection(route, userToken); + if (execAware != null) { + if (execAware.isAborted()) { + connRequest.cancel(); + throw new RequestAbortedException("Request aborted"); + } else { + execAware.setCancellable(connRequest); + } + } + + final RequestConfig config = context.getRequestConfig(); + + final HttpClientConnection managedConn; + try { + final int timeout = config.getConnectionRequestTimeout(); + managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); + } catch(final InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw new RequestAbortedException("Request aborted", interrupted); + } catch(final ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause == null) { + cause = ex; + } + throw new RequestAbortedException("Request execution failed", cause); + } + + context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn); + + if (config.isStaleConnectionCheckEnabled()) { + // validate connection + if (managedConn.isOpen()) { + this.log.debug("Stale connection check"); + if (managedConn.isStale()) { + this.log.debug("Stale connection detected"); + managedConn.close(); + } + } + } + + final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn); + try { + if (execAware != null) { + execAware.setCancellable(connHolder); + } + + HttpResponse response; + for (int execCount = 1;; execCount++) { + + if (execCount > 1 && !RequestEntityProxy.isRepeatable(request)) { + throw new NonRepeatableRequestException("Cannot retry request " + + "with a non-repeatable request entity."); + } + + if (execAware != null && execAware.isAborted()) { + throw new RequestAbortedException("Request aborted"); + } + + if (!managedConn.isOpen()) { + this.log.debug("Opening connection " + route); + try { + establishRoute(proxyAuthState, managedConn, route, request, context); + } catch (final TunnelRefusedException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage()); + } + response = ex.getResponse(); + break; + } + } + final int timeout = config.getSocketTimeout(); + if (timeout >= 0) { + managedConn.setSocketTimeout(timeout); + } + + if (execAware != null && execAware.isAborted()) { + throw new RequestAbortedException("Request aborted"); + } + + if (this.log.isDebugEnabled()) { + this.log.debug("Executing request " + request.getRequestLine()); + } + + if (!request.containsHeader(AUTH.WWW_AUTH_RESP)) { + if (this.log.isDebugEnabled()) { + this.log.debug("Target auth state: " + targetAuthState.getState()); + } + this.authenticator.generateAuthResponse(request, targetAuthState, context); + } + if (!request.containsHeader(AUTH.PROXY_AUTH_RESP) && !route.isTunnelled()) { + if (this.log.isDebugEnabled()) { + this.log.debug("Proxy auth state: " + proxyAuthState.getState()); + } + this.authenticator.generateAuthResponse(request, proxyAuthState, context); + } + + response = requestExecutor.execute(request, managedConn, context); + + // The connection is in or can be brought to a re-usable state. + if (reuseStrategy.keepAlive(response, context)) { + // Set the idle duration of this connection + final long duration = keepAliveStrategy.getKeepAliveDuration(response, context); + if (this.log.isDebugEnabled()) { + final String s; + if (duration > 0) { + s = "for " + duration + " " + TimeUnit.MILLISECONDS; + } else { + s = "indefinitely"; + } + this.log.debug("Connection can be kept alive " + s); + } + connHolder.setValidFor(duration, TimeUnit.MILLISECONDS); + connHolder.markReusable(); + } else { + connHolder.markNonReusable(); + } + + if (needAuthentication( + targetAuthState, proxyAuthState, route, response, context)) { + // Make sure the response body is fully consumed, if present + final HttpEntity entity = response.getEntity(); + if (connHolder.isReusable()) { + EntityUtils.consume(entity); + } else { + managedConn.close(); + if (proxyAuthState.getState() == AuthProtocolState.SUCCESS + && proxyAuthState.getAuthScheme() != null + && proxyAuthState.getAuthScheme().isConnectionBased()) { + this.log.debug("Resetting proxy auth state"); + proxyAuthState.reset(); + } + if (targetAuthState.getState() == AuthProtocolState.SUCCESS + && targetAuthState.getAuthScheme() != null + && targetAuthState.getAuthScheme().isConnectionBased()) { + this.log.debug("Resetting target auth state"); + targetAuthState.reset(); + } + } + // discard previous auth headers + final HttpRequest original = request.getOriginal(); + if (!original.containsHeader(AUTH.WWW_AUTH_RESP)) { + request.removeHeaders(AUTH.WWW_AUTH_RESP); + } + if (!original.containsHeader(AUTH.PROXY_AUTH_RESP)) { + request.removeHeaders(AUTH.PROXY_AUTH_RESP); + } + } else { + break; + } + } + + if (userToken == null) { + userToken = userTokenHandler.getUserToken(context); + context.setAttribute(HttpClientContext.USER_TOKEN, userToken); + } + if (userToken != null) { + connHolder.setState(userToken); + } + + // check for entity, release connection if possible + final HttpEntity entity = response.getEntity(); + if (entity == null || !entity.isStreaming()) { + // connection not needed and (assumed to be) in re-usable state + connHolder.releaseConnection(); + return new HttpResponseProxy(response, null); + } else { + return new HttpResponseProxy(response, connHolder); + } + } catch (final ConnectionShutdownException ex) { + final InterruptedIOException ioex = new InterruptedIOException( + "Connection has been shut down"); + ioex.initCause(ex); + throw ioex; + } catch (final HttpException ex) { + connHolder.abortConnection(); + throw ex; + } catch (final IOException ex) { + connHolder.abortConnection(); + throw ex; + } catch (final RuntimeException ex) { + connHolder.abortConnection(); + throw ex; + } + } + + /** + * Establishes the target route. + */ + void establishRoute( + final AuthState proxyAuthState, + final HttpClientConnection managedConn, + final HttpRoute route, + final HttpRequest request, + final HttpClientContext context) throws HttpException, IOException { + final RequestConfig config = context.getRequestConfig(); + final int timeout = config.getConnectTimeout(); + final RouteTracker tracker = new RouteTracker(route); + int step; + do { + final HttpRoute fact = tracker.toRoute(); + step = this.routeDirector.nextStep(route, fact); + + switch (step) { + + case HttpRouteDirector.CONNECT_TARGET: + this.connManager.connect( + managedConn, + route, + timeout > 0 ? timeout : 0, + context); + tracker.connectTarget(route.isSecure()); + break; + case HttpRouteDirector.CONNECT_PROXY: + this.connManager.connect( + managedConn, + route, + timeout > 0 ? timeout : 0, + context); + final HttpHost proxy = route.getProxyHost(); + tracker.connectProxy(proxy, false); + break; + case HttpRouteDirector.TUNNEL_TARGET: { + final boolean secure = createTunnelToTarget( + proxyAuthState, managedConn, route, request, context); + this.log.debug("Tunnel to target created."); + tracker.tunnelTarget(secure); + } break; + + case HttpRouteDirector.TUNNEL_PROXY: { + // The most simple example for this case is a proxy chain + // of two proxies, where P1 must be tunnelled to P2. + // route: Source -> P1 -> P2 -> Target (3 hops) + // fact: Source -> P1 -> Target (2 hops) + final int hop = fact.getHopCount()-1; // the hop to establish + final boolean secure = createTunnelToProxy(route, hop, context); + this.log.debug("Tunnel to proxy created."); + tracker.tunnelProxy(route.getHopTarget(hop), secure); + } break; + + case HttpRouteDirector.LAYER_PROTOCOL: + this.connManager.upgrade(managedConn, route, context); + tracker.layerProtocol(route.isSecure()); + break; + + case HttpRouteDirector.UNREACHABLE: + throw new HttpException("Unable to establish route: " + + "planned = " + route + "; current = " + fact); + case HttpRouteDirector.COMPLETE: + this.connManager.routeComplete(managedConn, route, context); + break; + default: + throw new IllegalStateException("Unknown step indicator " + + step + " from RouteDirector."); + } + + } while (step > HttpRouteDirector.COMPLETE); + } + + /** + * Creates a tunnel to the target server. + * The connection must be established to the (last) proxy. + * A CONNECT request for tunnelling through the proxy will + * be created and sent, the response received and checked. + * This method does not update the connection with + * information about the tunnel, that is left to the caller. + */ + private boolean createTunnelToTarget( + final AuthState proxyAuthState, + final HttpClientConnection managedConn, + final HttpRoute route, + final HttpRequest request, + final HttpClientContext context) throws HttpException, IOException { + + final RequestConfig config = context.getRequestConfig(); + final int timeout = config.getConnectTimeout(); + + final HttpHost target = route.getTargetHost(); + final HttpHost proxy = route.getProxyHost(); + HttpResponse response = null; + + final String authority = target.toHostString(); + final HttpRequest connect = new BasicHttpRequest("CONNECT", authority, request.getProtocolVersion()); + + this.requestExecutor.preProcess(connect, this.proxyHttpProcessor, context); + + while (response == null) { + if (!managedConn.isOpen()) { + this.connManager.connect( + managedConn, + route, + timeout > 0 ? timeout : 0, + context); + } + + connect.removeHeaders(AUTH.PROXY_AUTH_RESP); + this.authenticator.generateAuthResponse(connect, proxyAuthState, context); + + response = this.requestExecutor.execute(connect, managedConn, context); + + final int status = response.getStatusLine().getStatusCode(); + if (status < 200) { + throw new HttpException("Unexpected response to CONNECT request: " + + response.getStatusLine()); + } + + if (config.isAuthenticationEnabled()) { + if (this.authenticator.isAuthenticationRequested(proxy, response, + this.proxyAuthStrategy, proxyAuthState, context)) { + if (this.authenticator.handleAuthChallenge(proxy, response, + this.proxyAuthStrategy, proxyAuthState, context)) { + // Retry request + if (this.reuseStrategy.keepAlive(response, context)) { + this.log.debug("Connection kept alive"); + // Consume response content + final HttpEntity entity = response.getEntity(); + EntityUtils.consume(entity); + } else { + managedConn.close(); + } + response = null; + } + } + } + } + + final int status = response.getStatusLine().getStatusCode(); + + if (status > 299) { + + // Buffer response content + final HttpEntity entity = response.getEntity(); + if (entity != null) { + response.setEntity(new BufferedHttpEntity(entity)); + } + + managedConn.close(); + throw new TunnelRefusedException("CONNECT refused by proxy: " + + response.getStatusLine(), response); + } + + // How to decide on security of the tunnelled connection? + // The socket factory knows only about the segment to the proxy. + // Even if that is secure, the hop to the target may be insecure. + // Leave it to derived classes, consider insecure by default here. + return false; + } + + /** + * Creates a tunnel to an intermediate proxy. + * This method is not implemented in this class. + * It just throws an exception here. + */ + private boolean createTunnelToProxy( + final HttpRoute route, + final int hop, + final HttpClientContext context) throws HttpException { + + // Have a look at createTunnelToTarget and replicate the parts + // you need in a custom derived class. If your proxies don't require + // authentication, it is not too hard. But for the stock version of + // HttpClient, we cannot make such simplifying assumptions and would + // have to include proxy authentication code. The HttpComponents team + // is currently not in a position to support rarely used code of this + // complexity. Feel free to submit patches that refactor the code in + // createTunnelToTarget to facilitate re-use for proxy tunnelling. + + throw new HttpException("Proxy chains are not supported."); + } + + private boolean needAuthentication( + final AuthState targetAuthState, + final AuthState proxyAuthState, + final HttpRoute route, + final HttpResponse response, + final HttpClientContext context) { + final RequestConfig config = context.getRequestConfig(); + if (config.isAuthenticationEnabled()) { + HttpHost target = context.getTargetHost(); + if (target == null) { + target = route.getTargetHost(); + } + if (target.getPort() < 0) { + target = new HttpHost( + target.getHostName(), + route.getTargetHost().getPort(), + target.getSchemeName()); + } + final boolean targetAuthRequested = this.authenticator.isAuthenticationRequested( + target, response, this.targetAuthStrategy, targetAuthState, context); + + HttpHost proxy = route.getProxyHost(); + // if proxy is not set use target host instead + if (proxy == null) { + proxy = route.getTargetHost(); + } + final boolean proxyAuthRequested = this.authenticator.isAuthenticationRequested( + proxy, response, this.proxyAuthStrategy, proxyAuthState, context); + + if (targetAuthRequested) { + return this.authenticator.handleAuthChallenge(target, response, + this.targetAuthStrategy, targetAuthState, context); + } + if (proxyAuthRequested) { + return this.authenticator.handleAuthChallenge(proxy, response, + this.proxyAuthStrategy, proxyAuthState, context); + } + } + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MinimalClientExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MinimalClientExec.java new file mode 100644 index 000000000..16f6176eb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/MinimalClientExec.java @@ -0,0 +1,251 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy; +import ch.boye.httpclientandroidlib.conn.ConnectionRequest; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.conn.ConnectionShutdownException; +import ch.boye.httpclientandroidlib.protocol.HttpCoreContext; +import ch.boye.httpclientandroidlib.protocol.HttpProcessor; +import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor; +import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor; +import ch.boye.httpclientandroidlib.protocol.RequestContent; +import ch.boye.httpclientandroidlib.protocol.RequestTargetHost; +import ch.boye.httpclientandroidlib.protocol.RequestUserAgent; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.VersionInfo; + +/** + * Request executor that implements the most fundamental aspects of + * the HTTP specification and the most straight-forward request / response + * exchange with the target server. This executor does not support + * execution via proxy and will make no attempts to retry the request + * in case of a redirect, authentication challenge or I/O error. + * + * @since 4.3 + */ +@Immutable +public class MinimalClientExec implements ClientExecChain { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpRequestExecutor requestExecutor; + private final HttpClientConnectionManager connManager; + private final ConnectionReuseStrategy reuseStrategy; + private final ConnectionKeepAliveStrategy keepAliveStrategy; + private final HttpProcessor httpProcessor; + + public MinimalClientExec( + final HttpRequestExecutor requestExecutor, + final HttpClientConnectionManager connManager, + final ConnectionReuseStrategy reuseStrategy, + final ConnectionKeepAliveStrategy keepAliveStrategy) { + Args.notNull(requestExecutor, "HTTP request executor"); + Args.notNull(connManager, "Client connection manager"); + Args.notNull(reuseStrategy, "Connection reuse strategy"); + Args.notNull(keepAliveStrategy, "Connection keep alive strategy"); + this.httpProcessor = new ImmutableHttpProcessor( + new RequestContent(), + new RequestTargetHost(), + new RequestClientConnControl(), + new RequestUserAgent(VersionInfo.getUserAgent( + "Apache-HttpClient", "ch.boye.httpclientandroidlib.client", getClass()))); + this.requestExecutor = requestExecutor; + this.connManager = connManager; + this.reuseStrategy = reuseStrategy; + this.keepAliveStrategy = keepAliveStrategy; + } + + static void rewriteRequestURI( + final HttpRequestWrapper request, + final HttpRoute route) throws ProtocolException { + try { + URI uri = request.getURI(); + if (uri != null) { + // Make sure the request URI is relative + if (uri.isAbsolute()) { + uri = URIUtils.rewriteURI(uri, null, true); + } else { + uri = URIUtils.rewriteURI(uri); + } + request.setURI(uri); + } + } catch (final URISyntaxException ex) { + throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex); + } + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + Args.notNull(route, "HTTP route"); + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + rewriteRequestURI(request, route); + + final ConnectionRequest connRequest = connManager.requestConnection(route, null); + if (execAware != null) { + if (execAware.isAborted()) { + connRequest.cancel(); + throw new RequestAbortedException("Request aborted"); + } else { + execAware.setCancellable(connRequest); + } + } + + final RequestConfig config = context.getRequestConfig(); + + final HttpClientConnection managedConn; + try { + final int timeout = config.getConnectionRequestTimeout(); + managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); + } catch(final InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw new RequestAbortedException("Request aborted", interrupted); + } catch(final ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause == null) { + cause = ex; + } + throw new RequestAbortedException("Request execution failed", cause); + } + + final ConnectionHolder releaseTrigger = new ConnectionHolder(log, connManager, managedConn); + try { + if (execAware != null) { + if (execAware.isAborted()) { + releaseTrigger.close(); + throw new RequestAbortedException("Request aborted"); + } else { + execAware.setCancellable(releaseTrigger); + } + } + + if (!managedConn.isOpen()) { + final int timeout = config.getConnectTimeout(); + this.connManager.connect( + managedConn, + route, + timeout > 0 ? timeout : 0, + context); + this.connManager.routeComplete(managedConn, route, context); + } + final int timeout = config.getSocketTimeout(); + if (timeout >= 0) { + managedConn.setSocketTimeout(timeout); + } + + HttpHost target = null; + final HttpRequest original = request.getOriginal(); + if (original instanceof HttpUriRequest) { + final URI uri = ((HttpUriRequest) original).getURI(); + if (uri.isAbsolute()) { + target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); + } + } + if (target == null) { + target = route.getTargetHost(); + } + + context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); + context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); + context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn); + context.setAttribute(HttpClientContext.HTTP_ROUTE, route); + + httpProcessor.process(request, context); + final HttpResponse response = requestExecutor.execute(request, managedConn, context); + httpProcessor.process(response, context); + + // The connection is in or can be brought to a re-usable state. + if (reuseStrategy.keepAlive(response, context)) { + // Set the idle duration of this connection + final long duration = keepAliveStrategy.getKeepAliveDuration(response, context); + releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS); + releaseTrigger.markReusable(); + } else { + releaseTrigger.markNonReusable(); + } + + // check for entity, release connection if possible + final HttpEntity entity = response.getEntity(); + if (entity == null || !entity.isStreaming()) { + // connection not needed and (assumed to be) in re-usable state + releaseTrigger.releaseConnection(); + return new HttpResponseProxy(response, null); + } else { + return new HttpResponseProxy(response, releaseTrigger); + } + } catch (final ConnectionShutdownException ex) { + final InterruptedIOException ioex = new InterruptedIOException( + "Connection has been shut down"); + ioex.initCause(ex); + throw ioex; + } catch (final HttpException ex) { + releaseTrigger.abortConnection(); + throw ex; + } catch (final IOException ex) { + releaseTrigger.abortConnection(); + throw ex; + } catch (final RuntimeException ex) { + releaseTrigger.abortConnection(); + throw ex; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ProtocolExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ProtocolExec.java new file mode 100644 index 000000000..4effac603 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ProtocolExec.java @@ -0,0 +1,214 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.UsernamePasswordCredentials; +import ch.boye.httpclientandroidlib.client.CredentialsProvider; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.params.ClientPNames; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.client.BasicCredentialsProvider; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpCoreContext; +import ch.boye.httpclientandroidlib.protocol.HttpProcessor; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Request executor in the request execution chain that is responsible + * for implementation of HTTP specification requirements. + * Internally this executor relies on a {@link HttpProcessor} to populate + * requisite HTTP request headers, process HTTP response headers and update + * session state in {@link HttpClientContext}. + *

    + * Further responsibilities such as communication with the opposite + * endpoint is delegated to the next executor in the request execution + * chain. + * + * @since 4.3 + */ +@Immutable +@SuppressWarnings("deprecation") +public class ProtocolExec implements ClientExecChain { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final ClientExecChain requestExecutor; + private final HttpProcessor httpProcessor; + + public ProtocolExec(final ClientExecChain requestExecutor, final HttpProcessor httpProcessor) { + Args.notNull(requestExecutor, "HTTP client request executor"); + Args.notNull(httpProcessor, "HTTP protocol processor"); + this.requestExecutor = requestExecutor; + this.httpProcessor = httpProcessor; + } + + void rewriteRequestURI( + final HttpRequestWrapper request, + final HttpRoute route) throws ProtocolException { + try { + URI uri = request.getURI(); + if (uri != null) { + if (route.getProxyHost() != null && !route.isTunnelled()) { + // Make sure the request URI is absolute + if (!uri.isAbsolute()) { + final HttpHost target = route.getTargetHost(); + uri = URIUtils.rewriteURI(uri, target, true); + } else { + uri = URIUtils.rewriteURI(uri); + } + } else { + // Make sure the request URI is relative + if (uri.isAbsolute()) { + uri = URIUtils.rewriteURI(uri, null, true); + } else { + uri = URIUtils.rewriteURI(uri); + } + } + request.setURI(uri); + } + } catch (final URISyntaxException ex) { + throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex); + } + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, + HttpException { + Args.notNull(route, "HTTP route"); + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + final HttpRequest original = request.getOriginal(); + URI uri = null; + if (original instanceof HttpUriRequest) { + uri = ((HttpUriRequest) original).getURI(); + } else { + final String uriString = original.getRequestLine().getUri(); + try { + uri = URI.create(uriString); + } catch (final IllegalArgumentException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug("Unable to parse '" + uriString + "' as a valid URI; " + + "request URI and Host header may be inconsistent", ex); + } + } + + } + request.setURI(uri); + + // Re-write request URI if needed + rewriteRequestURI(request, route); + + final HttpParams params = request.getParams(); + HttpHost virtualHost = (HttpHost) params.getParameter(ClientPNames.VIRTUAL_HOST); + // HTTPCLIENT-1092 - add the port if necessary + if (virtualHost != null && virtualHost.getPort() == -1) { + final int port = route.getTargetHost().getPort(); + if (port != -1) { + virtualHost = new HttpHost(virtualHost.getHostName(), port, + virtualHost.getSchemeName()); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Using virtual host" + virtualHost); + } + } + + HttpHost target = null; + if (virtualHost != null) { + target = virtualHost; + } else { + if (uri != null && uri.isAbsolute() && uri.getHost() != null) { + target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); + } + } + if (target == null) { + target = route.getTargetHost(); + } + + // Get user info from the URI + if (uri != null) { + final String userinfo = uri.getUserInfo(); + if (userinfo != null) { + CredentialsProvider credsProvider = context.getCredentialsProvider(); + if (credsProvider == null) { + credsProvider = new BasicCredentialsProvider(); + context.setCredentialsProvider(credsProvider); + } + credsProvider.setCredentials( + new AuthScope(target), + new UsernamePasswordCredentials(userinfo)); + } + } + + // Run request protocol interceptors + context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); + context.setAttribute(HttpClientContext.HTTP_ROUTE, route); + context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); + + this.httpProcessor.process(request, context); + + final CloseableHttpResponse response = this.requestExecutor.execute(route, request, + context, execAware); + try { + // Run response protocol interceptors + context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); + this.httpProcessor.process(response, context); + return response; + } catch (final RuntimeException ex) { + response.close(); + throw ex; + } catch (final IOException ex) { + response.close(); + throw ex; + } catch (final HttpException ex) { + response.close(); + throw ex; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RedirectExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RedirectExec.java new file mode 100644 index 000000000..c870662f7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RedirectExec.java @@ -0,0 +1,185 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.net.URI; +import java.util.List; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.auth.AuthScheme; +import ch.boye.httpclientandroidlib.auth.AuthState; +import ch.boye.httpclientandroidlib.client.RedirectException; +import ch.boye.httpclientandroidlib.client.RedirectStrategy; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * Request executor in the request execution chain that is responsible + * for handling of request redirects. + *

    + * Further responsibilities such as communication with the opposite + * endpoint is delegated to the next executor in the request execution + * chain. + * + * @since 4.3 + */ +@ThreadSafe +public class RedirectExec implements ClientExecChain { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final ClientExecChain requestExecutor; + private final RedirectStrategy redirectStrategy; + private final HttpRoutePlanner routePlanner; + + public RedirectExec( + final ClientExecChain requestExecutor, + final HttpRoutePlanner routePlanner, + final RedirectStrategy redirectStrategy) { + super(); + Args.notNull(requestExecutor, "HTTP client request executor"); + Args.notNull(routePlanner, "HTTP route planner"); + Args.notNull(redirectStrategy, "HTTP redirect strategy"); + this.requestExecutor = requestExecutor; + this.routePlanner = routePlanner; + this.redirectStrategy = redirectStrategy; + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + Args.notNull(route, "HTTP route"); + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + + final List redirectLocations = context.getRedirectLocations(); + if (redirectLocations != null) { + redirectLocations.clear(); + } + + final RequestConfig config = context.getRequestConfig(); + final int maxRedirects = config.getMaxRedirects() > 0 ? config.getMaxRedirects() : 50; + HttpRoute currentRoute = route; + HttpRequestWrapper currentRequest = request; + for (int redirectCount = 0;;) { + final CloseableHttpResponse response = requestExecutor.execute( + currentRoute, currentRequest, context, execAware); + try { + if (config.isRedirectsEnabled() && + this.redirectStrategy.isRedirected(currentRequest, response, context)) { + + if (redirectCount >= maxRedirects) { + throw new RedirectException("Maximum redirects ("+ maxRedirects + ") exceeded"); + } + redirectCount++; + + final HttpRequest redirect = this.redirectStrategy.getRedirect( + currentRequest, response, context); + if (!redirect.headerIterator().hasNext()) { + final HttpRequest original = request.getOriginal(); + redirect.setHeaders(original.getAllHeaders()); + } + currentRequest = HttpRequestWrapper.wrap(redirect); + + if (currentRequest instanceof HttpEntityEnclosingRequest) { + RequestEntityProxy.enhance((HttpEntityEnclosingRequest) currentRequest); + } + + final URI uri = currentRequest.getURI(); + final HttpHost newTarget = URIUtils.extractHost(uri); + if (newTarget == null) { + throw new ProtocolException("Redirect URI does not specify a valid host name: " + + uri); + } + + // Reset virtual host and auth states if redirecting to another host + if (!currentRoute.getTargetHost().equals(newTarget)) { + final AuthState targetAuthState = context.getTargetAuthState(); + if (targetAuthState != null) { + this.log.debug("Resetting target auth state"); + targetAuthState.reset(); + } + final AuthState proxyAuthState = context.getProxyAuthState(); + if (proxyAuthState != null) { + final AuthScheme authScheme = proxyAuthState.getAuthScheme(); + if (authScheme != null && authScheme.isConnectionBased()) { + this.log.debug("Resetting proxy auth state"); + proxyAuthState.reset(); + } + } + } + + currentRoute = this.routePlanner.determineRoute(newTarget, currentRequest, context); + if (this.log.isDebugEnabled()) { + this.log.debug("Redirecting to '" + uri + "' via " + currentRoute); + } + EntityUtils.consume(response.getEntity()); + response.close(); + } else { + return response; + } + } catch (final RuntimeException ex) { + response.close(); + throw ex; + } catch (final IOException ex) { + response.close(); + throw ex; + } catch (final HttpException ex) { + // Protocol exception related to a direct. + // The underlying connection may still be salvaged. + try { + EntityUtils.consume(response.getEntity()); + } catch (final IOException ioex) { + this.log.debug("I/O error while releasing connection", ioex); + } finally { + response.close(); + } + throw ex; + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestAbortedException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestAbortedException.java new file mode 100644 index 000000000..be58be8f8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestAbortedException.java @@ -0,0 +1,55 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.InterruptedIOException; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that the request has been aborted. + * + * @since 4.3 + */ +@Immutable +public class RequestAbortedException extends InterruptedIOException { + + private static final long serialVersionUID = 4973849966012490112L; + + public RequestAbortedException(final String message) { + super(message); + } + + public RequestAbortedException(final String message, final Throwable cause) { + super(message); + if (cause != null) { + initCause(cause); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestEntityProxy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestEntityProxy.java new file mode 100644 index 000000000..d5b396684 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RequestEntityProxy.java @@ -0,0 +1,137 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * A Proxy class for {@link ch.boye.httpclientandroidlib.HttpEntity} enclosed in a request message. + * + * @since 4.3 + */ +@NotThreadSafe +class RequestEntityProxy implements HttpEntity { + + static void enhance(final HttpEntityEnclosingRequest request) { + final HttpEntity entity = request.getEntity(); + if (entity != null && !entity.isRepeatable() && !isEnhanced(entity)) { + request.setEntity(new RequestEntityProxy(entity)); + } + } + + static boolean isEnhanced(final HttpEntity entity) { + return entity instanceof RequestEntityProxy; + } + + static boolean isRepeatable(final HttpRequest request) { + if (request instanceof HttpEntityEnclosingRequest) { + final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); + if (entity != null) { + if (isEnhanced(entity)) { + final RequestEntityProxy proxy = (RequestEntityProxy) entity; + if (!proxy.isConsumed()) { + return true; + } + } + return entity.isRepeatable(); + } + } + return true; + } + + private final HttpEntity original; + private boolean consumed = false; + + RequestEntityProxy(final HttpEntity original) { + super(); + this.original = original; + } + + public HttpEntity getOriginal() { + return original; + } + + public boolean isConsumed() { + return consumed; + } + + public boolean isRepeatable() { + return original.isRepeatable(); + } + + public boolean isChunked() { + return original.isChunked(); + } + + public long getContentLength() { + return original.getContentLength(); + } + + public Header getContentType() { + return original.getContentType(); + } + + public Header getContentEncoding() { + return original.getContentEncoding(); + } + + public InputStream getContent() throws IOException, IllegalStateException { + return original.getContent(); + } + + public void writeTo(final OutputStream outstream) throws IOException { + consumed = true; + original.writeTo(outstream); + } + + public boolean isStreaming() { + return original.isStreaming(); + } + + @Deprecated + public void consumeContent() throws IOException { + consumed = true; + original.consumeContent(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("RequestEntityProxy{"); + sb.append(original); + sb.append('}'); + return sb.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ResponseEntityProxy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ResponseEntityProxy.java new file mode 100644 index 000000000..8a5df1cf8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ResponseEntityProxy.java @@ -0,0 +1,152 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.EofSensorInputStream; +import ch.boye.httpclientandroidlib.conn.EofSensorWatcher; +import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper; + +/** + * A wrapper class for {@link HttpEntity} enclosed in a response message. + * + * @since 4.3 + */ +@NotThreadSafe +class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher { + + private final ConnectionHolder connHolder; + + public static void enchance(final HttpResponse response, final ConnectionHolder connHolder) { + final HttpEntity entity = response.getEntity(); + if (entity != null && entity.isStreaming() && connHolder != null) { + response.setEntity(new ResponseEntityProxy(entity, connHolder)); + } + } + + ResponseEntityProxy(final HttpEntity entity, final ConnectionHolder connHolder) { + super(entity); + this.connHolder = connHolder; + } + + private void cleanup() { + if (this.connHolder != null) { + this.connHolder.abortConnection(); + } + } + + public void releaseConnection() throws IOException { + if (this.connHolder != null) { + try { + if (this.connHolder.isReusable()) { + this.connHolder.releaseConnection(); + } + } finally { + cleanup(); + } + } + } + + @Override + public boolean isRepeatable() { + return false; + } + + @Override + public InputStream getContent() throws IOException { + return new EofSensorInputStream(this.wrappedEntity.getContent(), this); + } + + @Deprecated + @Override + public void consumeContent() throws IOException { + releaseConnection(); + } + + @Override + public void writeTo(final OutputStream outstream) throws IOException { + try { + this.wrappedEntity.writeTo(outstream); + releaseConnection(); + } finally { + cleanup(); + } + } + + public boolean eofDetected(final InputStream wrapped) throws IOException { + try { + // there may be some cleanup required, such as + // reading trailers after the response body: + wrapped.close(); + releaseConnection(); + } finally { + cleanup(); + } + return false; + } + + public boolean streamClosed(final InputStream wrapped) throws IOException { + try { + final boolean open = connHolder != null && !connHolder.isReleased(); + // this assumes that closing the stream will + // consume the remainder of the response body: + try { + wrapped.close(); + releaseConnection(); + } catch (final SocketException ex) { + if (open) { + throw ex; + } + } + } finally { + cleanup(); + } + return false; + } + + public boolean streamAbort(final InputStream wrapped) throws IOException { + cleanup(); + return false; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ResponseEntityProxy{"); + sb.append(wrappedEntity); + sb.append('}'); + return sb.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RetryExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RetryExec.java new file mode 100644 index 000000000..ab78e3a83 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/RetryExec.java @@ -0,0 +1,126 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler; +import ch.boye.httpclientandroidlib.client.NonRepeatableRequestException; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Request executor in the request execution chain that is responsible + * for making a decision whether a request failed due to an I/O error + * should be re-executed. + *

    + * Further responsibilities such as communication with the opposite + * endpoint is delegated to the next executor in the request execution + * chain. + * + * @since 4.3 + */ +@Immutable +public class RetryExec implements ClientExecChain { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final ClientExecChain requestExecutor; + private final HttpRequestRetryHandler retryHandler; + + public RetryExec( + final ClientExecChain requestExecutor, + final HttpRequestRetryHandler retryHandler) { + Args.notNull(requestExecutor, "HTTP request executor"); + Args.notNull(retryHandler, "HTTP request retry handler"); + this.requestExecutor = requestExecutor; + this.retryHandler = retryHandler; + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + Args.notNull(route, "HTTP route"); + Args.notNull(request, "HTTP request"); + Args.notNull(context, "HTTP context"); + final Header[] origheaders = request.getAllHeaders(); + for (int execCount = 1;; execCount++) { + try { + return this.requestExecutor.execute(route, request, context, execAware); + } catch (final IOException ex) { + if (execAware != null && execAware.isAborted()) { + this.log.debug("Request has been aborted"); + throw ex; + } + if (retryHandler.retryRequest(ex, execCount, context)) { + if (this.log.isInfoEnabled()) { + this.log.info("I/O exception ("+ ex.getClass().getName() + + ") caught when processing request to " + + route + + ": " + + ex.getMessage()); + } + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage(), ex); + } + if (!RequestEntityProxy.isRepeatable(request)) { + this.log.debug("Cannot retry non-repeatable request"); + throw new NonRepeatableRequestException("Cannot retry request " + + "with a non-repeatable request entity", ex); + } + request.setHeaders(origheaders); + if (this.log.isInfoEnabled()) { + this.log.info("Retrying request to " + route); + } + } else { + if (ex instanceof NoHttpResponseException) { + final NoHttpResponseException updatedex = new NoHttpResponseException( + route.getTargetHost().toHostString() + " failed to respond"); + updatedex.setStackTrace(ex.getStackTrace()); + throw updatedex; + } else { + throw ex; + } + } + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ServiceUnavailableRetryExec.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ServiceUnavailableRetryExec.java new file mode 100644 index 000000000..1382d998a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/ServiceUnavailableRetryExec.java @@ -0,0 +1,108 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware; +import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Request executor in the request execution chain that is responsible + * for making a decision whether a request that received a non-2xx response + * from the target server should be re-executed. + *

    + * Further responsibilities such as communication with the opposite + * endpoint is delegated to the next executor in the request execution + * chain. + * + * @since 4.3 + */ +@Immutable +public class ServiceUnavailableRetryExec implements ClientExecChain { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final ClientExecChain requestExecutor; + private final ServiceUnavailableRetryStrategy retryStrategy; + + public ServiceUnavailableRetryExec( + final ClientExecChain requestExecutor, + final ServiceUnavailableRetryStrategy retryStrategy) { + super(); + Args.notNull(requestExecutor, "HTTP request executor"); + Args.notNull(retryStrategy, "Retry strategy"); + this.requestExecutor = requestExecutor; + this.retryStrategy = retryStrategy; + } + + public CloseableHttpResponse execute( + final HttpRoute route, + final HttpRequestWrapper request, + final HttpClientContext context, + final HttpExecutionAware execAware) throws IOException, HttpException { + final Header[] origheaders = request.getAllHeaders(); + for (int c = 1;; c++) { + final CloseableHttpResponse response = this.requestExecutor.execute( + route, request, context, execAware); + try { + if (this.retryStrategy.retryRequest(response, c, context)) { + response.close(); + final long nextInterval = this.retryStrategy.getRetryInterval(); + if (nextInterval > 0) { + try { + this.log.trace("Wait for " + nextInterval); + Thread.sleep(nextInterval); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new InterruptedIOException(); + } + } + request.setHeaders(origheaders); + } else { + return response; + } + } catch (final RuntimeException ex) { + response.close(); + throw ex; + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/TunnelRefusedException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/TunnelRefusedException.java new file mode 100644 index 000000000..5545c7b84 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/TunnelRefusedException.java @@ -0,0 +1,55 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.execchain; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that the tunnel request was rejected by the proxy host. + * + * @since 4.0 + */ +@Immutable +public class TunnelRefusedException extends HttpException { + + private static final long serialVersionUID = -8646722842745617323L; + + private final HttpResponse response; + + public TunnelRefusedException(final String message, final HttpResponse response) { + super(message); + this.response = response; + } + + public HttpResponse getResponse() { + return this.response; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/package-info.java new file mode 100644 index 000000000..740a9eec7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/execchain/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * HTTP request execution chain APIs. + */ +package ch.boye.httpclientandroidlib.impl.execchain; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageParser.java new file mode 100644 index 000000000..cd7aceb52 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageParser.java @@ -0,0 +1,284 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.MessageConstraintException; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineParser; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.params.HttpParamConfig; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract base class for HTTP message parsers that obtain input from + * an instance of {@link SessionInputBuffer}. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public abstract class AbstractMessageParser implements HttpMessageParser { + + private static final int HEAD_LINE = 0; + private static final int HEADERS = 1; + + private final SessionInputBuffer sessionBuffer; + private final MessageConstraints messageConstraints; + private final List headerLines; + protected final LineParser lineParser; + + private int state; + private T message; + + /** + * Creates an instance of AbstractMessageParser. + * + * @param buffer the session input buffer. + * @param parser the line parser. + * @param params HTTP parameters. + * + * @deprecated (4.3) use {@link AbstractMessageParser#AbstractMessageParser(SessionInputBuffer, + * LineParser, MessageConstraints)} + */ + @Deprecated + public AbstractMessageParser( + final SessionInputBuffer buffer, + final LineParser parser, + final HttpParams params) { + super(); + Args.notNull(buffer, "Session input buffer"); + Args.notNull(params, "HTTP parameters"); + this.sessionBuffer = buffer; + this.messageConstraints = HttpParamConfig.getMessageConstraints(params); + this.lineParser = (parser != null) ? parser : BasicLineParser.INSTANCE; + this.headerLines = new ArrayList(); + this.state = HEAD_LINE; + } + + /** + * Creates new instance of AbstractMessageParser. + * + * @param buffer the session input buffer. + * @param lineParser the line parser. If null {@link BasicLineParser#INSTANCE} + * will be used. + * @param constraints the message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.3 + */ + public AbstractMessageParser( + final SessionInputBuffer buffer, + final LineParser lineParser, + final MessageConstraints constraints) { + super(); + this.sessionBuffer = Args.notNull(buffer, "Session input buffer"); + this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE; + this.messageConstraints = constraints != null ? constraints : MessageConstraints.DEFAULT; + this.headerLines = new ArrayList(); + this.state = HEAD_LINE; + } + + /** + * Parses HTTP headers from the data receiver stream according to the generic + * format as given in Section 3.1 of RFC 822, RFC-2616 Section 4 and 19.3. + * + * @param inbuffer Session input buffer + * @param maxHeaderCount maximum number of headers allowed. If the number + * of headers received from the data stream exceeds maxCount value, an + * IOException will be thrown. Setting this parameter to a negative value + * or zero will disable the check. + * @param maxLineLen maximum number of characters for a header line, + * including the continuation lines. Setting this parameter to a negative + * value or zero will disable the check. + * @return array of HTTP headers + * @param parser line parser to use. Can be null, in which case + * the default implementation of this interface will be used. + * + * @throws IOException in case of an I/O error + * @throws HttpException in case of HTTP protocol violation + */ + public static Header[] parseHeaders( + final SessionInputBuffer inbuffer, + final int maxHeaderCount, + final int maxLineLen, + final LineParser parser) throws HttpException, IOException { + final List headerLines = new ArrayList(); + return parseHeaders(inbuffer, maxHeaderCount, maxLineLen, + parser != null ? parser : BasicLineParser.INSTANCE, + headerLines); + } + + /** + * Parses HTTP headers from the data receiver stream according to the generic + * format as given in Section 3.1 of RFC 822, RFC-2616 Section 4 and 19.3. + * + * @param inbuffer Session input buffer + * @param maxHeaderCount maximum number of headers allowed. If the number + * of headers received from the data stream exceeds maxCount value, an + * IOException will be thrown. Setting this parameter to a negative value + * or zero will disable the check. + * @param maxLineLen maximum number of characters for a header line, + * including the continuation lines. Setting this parameter to a negative + * value or zero will disable the check. + * @param parser line parser to use. + * @param headerLines List of header lines. This list will be used to store + * intermediate results. This makes it possible to resume parsing of + * headers in case of a {@link java.io.InterruptedIOException}. + * + * @return array of HTTP headers + * + * @throws IOException in case of an I/O error + * @throws HttpException in case of HTTP protocol violation + * + * @since 4.1 + */ + public static Header[] parseHeaders( + final SessionInputBuffer inbuffer, + final int maxHeaderCount, + final int maxLineLen, + final LineParser parser, + final List headerLines) throws HttpException, IOException { + Args.notNull(inbuffer, "Session input buffer"); + Args.notNull(parser, "Line parser"); + Args.notNull(headerLines, "Header line list"); + + CharArrayBuffer current = null; + CharArrayBuffer previous = null; + for (;;) { + if (current == null) { + current = new CharArrayBuffer(64); + } else { + current.clear(); + } + final int l = inbuffer.readLine(current); + if (l == -1 || current.length() < 1) { + break; + } + // Parse the header name and value + // Check for folded headers first + // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2 + // discussion on folded headers + if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) { + // we have continuation folded header + // so append value + int i = 0; + while (i < current.length()) { + final char ch = current.charAt(i); + if (ch != ' ' && ch != '\t') { + break; + } + i++; + } + if (maxLineLen > 0 + && previous.length() + 1 + current.length() - i > maxLineLen) { + throw new MessageConstraintException("Maximum line length limit exceeded"); + } + previous.append(' '); + previous.append(current, i, current.length() - i); + } else { + headerLines.add(current); + previous = current; + current = null; + } + if (maxHeaderCount > 0 && headerLines.size() >= maxHeaderCount) { + throw new MessageConstraintException("Maximum header count exceeded"); + } + } + final Header[] headers = new Header[headerLines.size()]; + for (int i = 0; i < headerLines.size(); i++) { + final CharArrayBuffer buffer = headerLines.get(i); + try { + headers[i] = parser.parseHeader(buffer); + } catch (final ParseException ex) { + throw new ProtocolException(ex.getMessage()); + } + } + return headers; + } + + /** + * Subclasses must override this method to generate an instance of + * {@link HttpMessage} based on the initial input from the session buffer. + *

    + * Usually this method is expected to read just the very first line or + * the very first valid from the data stream and based on the input generate + * an appropriate instance of {@link HttpMessage}. + * + * @param sessionBuffer the session input buffer. + * @return HTTP message based on the input from the session buffer. + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation. + * @throws ParseException in case of a parse error. + */ + protected abstract T parseHead(SessionInputBuffer sessionBuffer) + throws IOException, HttpException, ParseException; + + public T parse() throws IOException, HttpException { + final int st = this.state; + switch (st) { + case HEAD_LINE: + try { + this.message = parseHead(this.sessionBuffer); + } catch (final ParseException px) { + throw new ProtocolException(px.getMessage(), px); + } + this.state = HEADERS; + //$FALL-THROUGH$ + case HEADERS: + final Header[] headers = AbstractMessageParser.parseHeaders( + this.sessionBuffer, + this.messageConstraints.getMaxHeaderCount(), + this.messageConstraints.getMaxLineLength(), + this.lineParser, + this.headerLines); + this.message.setHeaders(headers); + final T result = this.message; + this.message = null; + this.headerLines.clear(); + this.state = HEAD_LINE; + return result; + default: + throw new IllegalStateException("Inconsistent parser state"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageWriter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageWriter.java new file mode 100644 index 000000000..56caf58d5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractMessageWriter.java @@ -0,0 +1,119 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.HttpMessageWriter; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineFormatter; +import ch.boye.httpclientandroidlib.message.LineFormatter; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract base class for HTTP message writers that serialize output to + * an instance of {@link SessionOutputBuffer}. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public abstract class AbstractMessageWriter implements HttpMessageWriter { + + protected final SessionOutputBuffer sessionBuffer; + protected final CharArrayBuffer lineBuf; + protected final LineFormatter lineFormatter; + + /** + * Creates an instance of AbstractMessageWriter. + * + * @param buffer the session output buffer. + * @param formatter the line formatter. + * @param params HTTP parameters. + * + * @deprecated (4.3) use + * {@link AbstractMessageWriter#AbstractMessageWriter(SessionOutputBuffer, LineFormatter)} + */ + @Deprecated + public AbstractMessageWriter(final SessionOutputBuffer buffer, + final LineFormatter formatter, + final HttpParams params) { + super(); + Args.notNull(buffer, "Session input buffer"); + this.sessionBuffer = buffer; + this.lineBuf = new CharArrayBuffer(128); + this.lineFormatter = (formatter != null) ? formatter : BasicLineFormatter.INSTANCE; + } + + /** + * Creates an instance of AbstractMessageWriter. + * + * @param buffer the session output buffer. + * @param formatter the line formatter If null {@link BasicLineFormatter#INSTANCE} + * will be used. + * + * @since 4.3 + */ + public AbstractMessageWriter( + final SessionOutputBuffer buffer, + final LineFormatter formatter) { + super(); + this.sessionBuffer = Args.notNull(buffer, "Session input buffer"); + this.lineFormatter = (formatter != null) ? formatter : BasicLineFormatter.INSTANCE; + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * Subclasses must override this method to write out the first header line + * based on the {@link HttpMessage} passed as a parameter. + * + * @param message the message whose first line is to be written out. + * @throws IOException in case of an I/O error. + */ + protected abstract void writeHeadLine(T message) throws IOException; + + public void write(final T message) throws IOException, HttpException { + Args.notNull(message, "HTTP message"); + writeHeadLine(message); + for (final HeaderIterator it = message.headerIterator(); it.hasNext(); ) { + final Header header = it.nextHeader(); + this.sessionBuffer.writeLine + (lineFormatter.formatHeader(this.lineBuf, header)); + } + this.lineBuf.clear(); + this.sessionBuffer.writeLine(this.lineBuf); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionInputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionInputBuffer.java new file mode 100644 index 000000000..e53e07a30 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionInputBuffer.java @@ -0,0 +1,401 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.BufferInfo; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.CoreProtocolPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.ByteArrayBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract base class for session input buffers that stream data from + * an arbitrary {@link InputStream}. This class buffers input data in + * an internal byte array for optimal input performance. + *

    + * {@link #readLine(CharArrayBuffer)} and {@link #readLine()} methods of this + * class treat a lone LF as valid line delimiters in addition to CR-LF required + * by the HTTP specification. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link SessionInputBufferImpl} + */ +@NotThreadSafe +@Deprecated +public abstract class AbstractSessionInputBuffer implements SessionInputBuffer, BufferInfo { + + private InputStream instream; + private byte[] buffer; + private ByteArrayBuffer linebuffer; + private Charset charset; + private boolean ascii; + private int maxLineLen; + private int minChunkLimit; + private HttpTransportMetricsImpl metrics; + private CodingErrorAction onMalformedCharAction; + private CodingErrorAction onUnmappableCharAction; + + private int bufferpos; + private int bufferlen; + private CharsetDecoder decoder; + private CharBuffer cbuf; + + public AbstractSessionInputBuffer() { + } + + /** + * Initializes this session input buffer. + * + * @param instream the source input stream. + * @param buffersize the size of the internal buffer. + * @param params HTTP parameters. + */ + protected void init(final InputStream instream, final int buffersize, final HttpParams params) { + Args.notNull(instream, "Input stream"); + Args.notNegative(buffersize, "Buffer size"); + Args.notNull(params, "HTTP parameters"); + this.instream = instream; + this.buffer = new byte[buffersize]; + this.bufferpos = 0; + this.bufferlen = 0; + this.linebuffer = new ByteArrayBuffer(buffersize); + final String charset = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET); + this.charset = charset != null ? Charset.forName(charset) : Consts.ASCII; + this.ascii = this.charset.equals(Consts.ASCII); + this.decoder = null; + this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1); + this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512); + this.metrics = createTransportMetrics(); + final CodingErrorAction a1 = (CodingErrorAction) params.getParameter( + CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION); + this.onMalformedCharAction = a1 != null ? a1 : CodingErrorAction.REPORT; + final CodingErrorAction a2 = (CodingErrorAction) params.getParameter( + CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION); + this.onUnmappableCharAction = a2 != null? a2 : CodingErrorAction.REPORT; + } + + /** + * @since 4.1 + */ + protected HttpTransportMetricsImpl createTransportMetrics() { + return new HttpTransportMetricsImpl(); + } + + /** + * @since 4.1 + */ + public int capacity() { + return this.buffer.length; + } + + /** + * @since 4.1 + */ + public int length() { + return this.bufferlen - this.bufferpos; + } + + /** + * @since 4.1 + */ + public int available() { + return capacity() - length(); + } + + protected int fillBuffer() throws IOException { + // compact the buffer if necessary + if (this.bufferpos > 0) { + final int len = this.bufferlen - this.bufferpos; + if (len > 0) { + System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len); + } + this.bufferpos = 0; + this.bufferlen = len; + } + final int l; + final int off = this.bufferlen; + final int len = this.buffer.length - off; + l = this.instream.read(this.buffer, off, len); + if (l == -1) { + return -1; + } else { + this.bufferlen = off + l; + this.metrics.incrementBytesTransferred(l); + return l; + } + } + + protected boolean hasBufferedData() { + return this.bufferpos < this.bufferlen; + } + + public int read() throws IOException { + int noRead; + while (!hasBufferedData()) { + noRead = fillBuffer(); + if (noRead == -1) { + return -1; + } + } + return this.buffer[this.bufferpos++] & 0xff; + } + + public int read(final byte[] b, final int off, final int len) throws IOException { + if (b == null) { + return 0; + } + if (hasBufferedData()) { + final int chunk = Math.min(len, this.bufferlen - this.bufferpos); + System.arraycopy(this.buffer, this.bufferpos, b, off, chunk); + this.bufferpos += chunk; + return chunk; + } + // If the remaining capacity is big enough, read directly from the + // underlying input stream bypassing the buffer. + if (len > this.minChunkLimit) { + final int read = this.instream.read(b, off, len); + if (read > 0) { + this.metrics.incrementBytesTransferred(read); + } + return read; + } else { + // otherwise read to the buffer first + while (!hasBufferedData()) { + final int noRead = fillBuffer(); + if (noRead == -1) { + return -1; + } + } + final int chunk = Math.min(len, this.bufferlen - this.bufferpos); + System.arraycopy(this.buffer, this.bufferpos, b, off, chunk); + this.bufferpos += chunk; + return chunk; + } + } + + public int read(final byte[] b) throws IOException { + if (b == null) { + return 0; + } + return read(b, 0, b.length); + } + + private int locateLF() { + for (int i = this.bufferpos; i < this.bufferlen; i++) { + if (this.buffer[i] == HTTP.LF) { + return i; + } + } + return -1; + } + + /** + * Reads a complete line of characters up to a line delimiter from this + * session buffer into the given line buffer. The number of chars actually + * read is returned as an integer. The line delimiter itself is discarded. + * If no char is available because the end of the stream has been reached, + * the value -1 is returned. This method blocks until input + * data is available, end of file is detected, or an exception is thrown. + *

    + * This method treats a lone LF as a valid line delimiters in addition + * to CR-LF required by the HTTP specification. + * + * @param charbuffer the line buffer. + * @return one line of characters + * @exception IOException if an I/O error occurs. + */ + public int readLine(final CharArrayBuffer charbuffer) throws IOException { + Args.notNull(charbuffer, "Char array buffer"); + int noRead = 0; + boolean retry = true; + while (retry) { + // attempt to find end of line (LF) + final int i = locateLF(); + if (i != -1) { + // end of line found. + if (this.linebuffer.isEmpty()) { + // the entire line is preset in the read buffer + return lineFromReadBuffer(charbuffer, i); + } + retry = false; + final int len = i + 1 - this.bufferpos; + this.linebuffer.append(this.buffer, this.bufferpos, len); + this.bufferpos = i + 1; + } else { + // end of line not found + if (hasBufferedData()) { + final int len = this.bufferlen - this.bufferpos; + this.linebuffer.append(this.buffer, this.bufferpos, len); + this.bufferpos = this.bufferlen; + } + noRead = fillBuffer(); + if (noRead == -1) { + retry = false; + } + } + if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) { + throw new IOException("Maximum line length limit exceeded"); + } + } + if (noRead == -1 && this.linebuffer.isEmpty()) { + // indicate the end of stream + return -1; + } + return lineFromLineBuffer(charbuffer); + } + + /** + * Reads a complete line of characters up to a line delimiter from this + * session buffer. The line delimiter itself is discarded. If no char is + * available because the end of the stream has been reached, + * null is returned. This method blocks until input data is + * available, end of file is detected, or an exception is thrown. + *

    + * This method treats a lone LF as a valid line delimiters in addition + * to CR-LF required by the HTTP specification. + * + * @return HTTP line as a string + * @exception IOException if an I/O error occurs. + */ + private int lineFromLineBuffer(final CharArrayBuffer charbuffer) + throws IOException { + // discard LF if found + int len = this.linebuffer.length(); + if (len > 0) { + if (this.linebuffer.byteAt(len - 1) == HTTP.LF) { + len--; + } + // discard CR if found + if (len > 0) { + if (this.linebuffer.byteAt(len - 1) == HTTP.CR) { + len--; + } + } + } + if (this.ascii) { + charbuffer.append(this.linebuffer, 0, len); + } else { + final ByteBuffer bbuf = ByteBuffer.wrap(this.linebuffer.buffer(), 0, len); + len = appendDecoded(charbuffer, bbuf); + } + this.linebuffer.clear(); + return len; + } + + private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position) + throws IOException { + final int off = this.bufferpos; + int i = position; + this.bufferpos = i + 1; + if (i > off && this.buffer[i - 1] == HTTP.CR) { + // skip CR if found + i--; + } + int len = i - off; + if (this.ascii) { + charbuffer.append(this.buffer, off, len); + } else { + final ByteBuffer bbuf = ByteBuffer.wrap(this.buffer, off, len); + len = appendDecoded(charbuffer, bbuf); + } + return len; + } + + private int appendDecoded( + final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException { + if (!bbuf.hasRemaining()) { + return 0; + } + if (this.decoder == null) { + this.decoder = this.charset.newDecoder(); + this.decoder.onMalformedInput(this.onMalformedCharAction); + this.decoder.onUnmappableCharacter(this.onUnmappableCharAction); + } + if (this.cbuf == null) { + this.cbuf = CharBuffer.allocate(1024); + } + this.decoder.reset(); + int len = 0; + while (bbuf.hasRemaining()) { + final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true); + len += handleDecodingResult(result, charbuffer, bbuf); + } + final CoderResult result = this.decoder.flush(this.cbuf); + len += handleDecodingResult(result, charbuffer, bbuf); + this.cbuf.clear(); + return len; + } + + private int handleDecodingResult( + final CoderResult result, + final CharArrayBuffer charbuffer, + final ByteBuffer bbuf) throws IOException { + if (result.isError()) { + result.throwException(); + } + this.cbuf.flip(); + final int len = this.cbuf.remaining(); + while (this.cbuf.hasRemaining()) { + charbuffer.append(this.cbuf.get()); + } + this.cbuf.compact(); + return len; + } + + public String readLine() throws IOException { + final CharArrayBuffer charbuffer = new CharArrayBuffer(64); + final int l = readLine(charbuffer); + if (l != -1) { + return charbuffer.toString(); + } else { + return null; + } + } + + public HttpTransportMetrics getMetrics() { + return this.metrics; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionOutputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionOutputBuffer.java new file mode 100644 index 000000000..a2c87d084 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionOutputBuffer.java @@ -0,0 +1,307 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.BufferInfo; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.CoreProtocolPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.ByteArrayBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract base class for session output buffers that stream data to + * an arbitrary {@link OutputStream}. This class buffers small chunks of + * output data in an internal byte array for optimal output performance. + *

    + * {@link #writeLine(CharArrayBuffer)} and {@link #writeLine(String)} methods + * of this class use CR-LF as a line delimiter. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link SessionOutputBufferImpl} + */ +@NotThreadSafe +@Deprecated +public abstract class AbstractSessionOutputBuffer implements SessionOutputBuffer, BufferInfo { + + private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF}; + + private OutputStream outstream; + private ByteArrayBuffer buffer; + private Charset charset; + private boolean ascii; + private int minChunkLimit; + private HttpTransportMetricsImpl metrics; + private CodingErrorAction onMalformedCharAction; + private CodingErrorAction onUnmappableCharAction; + + private CharsetEncoder encoder; + private ByteBuffer bbuf; + + protected AbstractSessionOutputBuffer( + final OutputStream outstream, + final int buffersize, + final Charset charset, + final int minChunkLimit, + final CodingErrorAction malformedCharAction, + final CodingErrorAction unmappableCharAction) { + super(); + Args.notNull(outstream, "Input stream"); + Args.notNegative(buffersize, "Buffer size"); + this.outstream = outstream; + this.buffer = new ByteArrayBuffer(buffersize); + this.charset = charset != null ? charset : Consts.ASCII; + this.ascii = this.charset.equals(Consts.ASCII); + this.encoder = null; + this.minChunkLimit = minChunkLimit >= 0 ? minChunkLimit : 512; + this.metrics = createTransportMetrics(); + this.onMalformedCharAction = malformedCharAction != null ? malformedCharAction : + CodingErrorAction.REPORT; + this.onUnmappableCharAction = unmappableCharAction != null? unmappableCharAction : + CodingErrorAction.REPORT; + } + + public AbstractSessionOutputBuffer() { + } + + protected void init(final OutputStream outstream, final int buffersize, final HttpParams params) { + Args.notNull(outstream, "Input stream"); + Args.notNegative(buffersize, "Buffer size"); + Args.notNull(params, "HTTP parameters"); + this.outstream = outstream; + this.buffer = new ByteArrayBuffer(buffersize); + final String charset = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET); + this.charset = charset != null ? Charset.forName(charset) : Consts.ASCII; + this.ascii = this.charset.equals(Consts.ASCII); + this.encoder = null; + this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512); + this.metrics = createTransportMetrics(); + final CodingErrorAction a1 = (CodingErrorAction) params.getParameter( + CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION); + this.onMalformedCharAction = a1 != null ? a1 : CodingErrorAction.REPORT; + final CodingErrorAction a2 = (CodingErrorAction) params.getParameter( + CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION); + this.onUnmappableCharAction = a2 != null? a2 : CodingErrorAction.REPORT; + } + + /** + * @since 4.1 + */ + protected HttpTransportMetricsImpl createTransportMetrics() { + return new HttpTransportMetricsImpl(); + } + + /** + * @since 4.1 + */ + public int capacity() { + return this.buffer.capacity(); + } + + /** + * @since 4.1 + */ + public int length() { + return this.buffer.length(); + } + + /** + * @since 4.1 + */ + public int available() { + return capacity() - length(); + } + + protected void flushBuffer() throws IOException { + final int len = this.buffer.length(); + if (len > 0) { + this.outstream.write(this.buffer.buffer(), 0, len); + this.buffer.clear(); + this.metrics.incrementBytesTransferred(len); + } + } + + public void flush() throws IOException { + flushBuffer(); + this.outstream.flush(); + } + + public void write(final byte[] b, final int off, final int len) throws IOException { + if (b == null) { + return; + } + // Do not want to buffer large-ish chunks + // if the byte array is larger then MIN_CHUNK_LIMIT + // write it directly to the output stream + if (len > this.minChunkLimit || len > this.buffer.capacity()) { + // flush the buffer + flushBuffer(); + // write directly to the out stream + this.outstream.write(b, off, len); + this.metrics.incrementBytesTransferred(len); + } else { + // Do not let the buffer grow unnecessarily + final int freecapacity = this.buffer.capacity() - this.buffer.length(); + if (len > freecapacity) { + // flush the buffer + flushBuffer(); + } + // buffer + this.buffer.append(b, off, len); + } + } + + public void write(final byte[] b) throws IOException { + if (b == null) { + return; + } + write(b, 0, b.length); + } + + public void write(final int b) throws IOException { + if (this.buffer.isFull()) { + flushBuffer(); + } + this.buffer.append(b); + } + + /** + * Writes characters from the specified string followed by a line delimiter + * to this session buffer. + *

    + * This method uses CR-LF as a line delimiter. + * + * @param s the line. + * @exception IOException if an I/O error occurs. + */ + public void writeLine(final String s) throws IOException { + if (s == null) { + return; + } + if (s.length() > 0) { + if (this.ascii) { + for (int i = 0; i < s.length(); i++) { + write(s.charAt(i)); + } + } else { + final CharBuffer cbuf = CharBuffer.wrap(s); + writeEncoded(cbuf); + } + } + write(CRLF); + } + + /** + * Writes characters from the specified char array followed by a line + * delimiter to this session buffer. + *

    + * This method uses CR-LF as a line delimiter. + * + * @param charbuffer the buffer containing chars of the line. + * @exception IOException if an I/O error occurs. + */ + public void writeLine(final CharArrayBuffer charbuffer) throws IOException { + if (charbuffer == null) { + return; + } + if (this.ascii) { + int off = 0; + int remaining = charbuffer.length(); + while (remaining > 0) { + int chunk = this.buffer.capacity() - this.buffer.length(); + chunk = Math.min(chunk, remaining); + if (chunk > 0) { + this.buffer.append(charbuffer, off, chunk); + } + if (this.buffer.isFull()) { + flushBuffer(); + } + off += chunk; + remaining -= chunk; + } + } else { + final CharBuffer cbuf = CharBuffer.wrap(charbuffer.buffer(), 0, charbuffer.length()); + writeEncoded(cbuf); + } + write(CRLF); + } + + private void writeEncoded(final CharBuffer cbuf) throws IOException { + if (!cbuf.hasRemaining()) { + return; + } + if (this.encoder == null) { + this.encoder = this.charset.newEncoder(); + this.encoder.onMalformedInput(this.onMalformedCharAction); + this.encoder.onUnmappableCharacter(this.onUnmappableCharAction); + } + if (this.bbuf == null) { + this.bbuf = ByteBuffer.allocate(1024); + } + this.encoder.reset(); + while (cbuf.hasRemaining()) { + final CoderResult result = this.encoder.encode(cbuf, this.bbuf, true); + handleEncodingResult(result); + } + final CoderResult result = this.encoder.flush(this.bbuf); + handleEncodingResult(result); + this.bbuf.clear(); + } + + private void handleEncodingResult(final CoderResult result) throws IOException { + if (result.isError()) { + result.throwException(); + } + this.bbuf.flip(); + while (this.bbuf.hasRemaining()) { + write(this.bbuf.get()); + } + this.bbuf.compact(); + } + + public HttpTransportMetrics getMetrics() { + return this.metrics; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedInputStream.java new file mode 100644 index 000000000..447bd676d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedInputStream.java @@ -0,0 +1,301 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.MalformedChunkCodingException; +import ch.boye.httpclientandroidlib.TruncatedChunkException; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.BufferInfo; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Implements chunked transfer coding. The content is received in small chunks. + * Entities transferred using this input stream can be of unlimited length. + * After the stream is read to the end, it provides access to the trailers, + * if any. + *

    + * Note that this class NEVER closes the underlying stream, even when close + * gets called. Instead, it will read until the "end" of its chunking on + * close, which allows for the seamless execution of subsequent HTTP 1.1 + * requests, while not requiring the client to remember to read the entire + * contents of the response. + * + * + * @since 4.0 + * + */ +@NotThreadSafe +public class ChunkedInputStream extends InputStream { + + private static final int CHUNK_LEN = 1; + private static final int CHUNK_DATA = 2; + private static final int CHUNK_CRLF = 3; + + private static final int BUFFER_SIZE = 2048; + + /** The session input buffer */ + private final SessionInputBuffer in; + + private final CharArrayBuffer buffer; + + private int state; + + /** The chunk size */ + private int chunkSize; + + /** The current position within the current chunk */ + private int pos; + + /** True if we've reached the end of stream */ + private boolean eof = false; + + /** True if this stream is closed */ + private boolean closed = false; + + private Header[] footers = new Header[] {}; + + /** + * Wraps session input stream and reads chunk coded input. + * + * @param in The session input buffer + */ + public ChunkedInputStream(final SessionInputBuffer in) { + super(); + this.in = Args.notNull(in, "Session input buffer"); + this.pos = 0; + this.buffer = new CharArrayBuffer(16); + this.state = CHUNK_LEN; + } + + @Override + public int available() throws IOException { + if (this.in instanceof BufferInfo) { + final int len = ((BufferInfo) this.in).length(); + return Math.min(len, this.chunkSize - this.pos); + } else { + return 0; + } + } + + /** + *

    Returns all the data in a chunked stream in coalesced form. A chunk + * is followed by a CRLF. The method returns -1 as soon as a chunksize of 0 + * is detected.

    + * + *

    Trailer headers are read automatically at the end of the stream and + * can be obtained with the getResponseFooters() method.

    + * + * @return -1 of the end of the stream has been reached or the next data + * byte + * @throws IOException in case of an I/O error + */ + @Override + public int read() throws IOException { + if (this.closed) { + throw new IOException("Attempted read from closed stream."); + } + if (this.eof) { + return -1; + } + if (state != CHUNK_DATA) { + nextChunk(); + if (this.eof) { + return -1; + } + } + final int b = in.read(); + if (b != -1) { + pos++; + if (pos >= chunkSize) { + state = CHUNK_CRLF; + } + } + return b; + } + + /** + * Read some bytes from the stream. + * @param b The byte array that will hold the contents from the stream. + * @param off The offset into the byte array at which bytes will start to be + * placed. + * @param len the maximum number of bytes that can be returned. + * @return The number of bytes returned or -1 if the end of stream has been + * reached. + * @throws IOException in case of an I/O error + */ + @Override + public int read (final byte[] b, final int off, final int len) throws IOException { + + if (closed) { + throw new IOException("Attempted read from closed stream."); + } + + if (eof) { + return -1; + } + if (state != CHUNK_DATA) { + nextChunk(); + if (eof) { + return -1; + } + } + final int bytesRead = in.read(b, off, Math.min(len, chunkSize - pos)); + if (bytesRead != -1) { + pos += bytesRead; + if (pos >= chunkSize) { + state = CHUNK_CRLF; + } + return bytesRead; + } else { + eof = true; + throw new TruncatedChunkException("Truncated chunk " + + "( expected size: " + chunkSize + + "; actual size: " + pos + ")"); + } + } + + /** + * Read some bytes from the stream. + * @param b The byte array that will hold the contents from the stream. + * @return The number of bytes returned or -1 if the end of stream has been + * reached. + * @throws IOException in case of an I/O error + */ + @Override + public int read (final byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** + * Read the next chunk. + * @throws IOException in case of an I/O error + */ + private void nextChunk() throws IOException { + chunkSize = getChunkSize(); + if (chunkSize < 0) { + throw new MalformedChunkCodingException("Negative chunk size"); + } + state = CHUNK_DATA; + pos = 0; + if (chunkSize == 0) { + eof = true; + parseTrailerHeaders(); + } + } + + /** + * Expects the stream to start with a chunksize in hex with optional + * comments after a semicolon. The line must end with a CRLF: "a3; some + * comment\r\n" Positions the stream at the start of the next line. + */ + private int getChunkSize() throws IOException { + final int st = this.state; + switch (st) { + case CHUNK_CRLF: + this.buffer.clear(); + final int bytesRead1 = this.in.readLine(this.buffer); + if (bytesRead1 == -1) { + return 0; + } + if (!this.buffer.isEmpty()) { + throw new MalformedChunkCodingException( + "Unexpected content at the end of chunk"); + } + state = CHUNK_LEN; + //$FALL-THROUGH$ + case CHUNK_LEN: + this.buffer.clear(); + final int bytesRead2 = this.in.readLine(this.buffer); + if (bytesRead2 == -1) { + return 0; + } + int separator = this.buffer.indexOf(';'); + if (separator < 0) { + separator = this.buffer.length(); + } + try { + return Integer.parseInt(this.buffer.substringTrimmed(0, separator), 16); + } catch (final NumberFormatException e) { + throw new MalformedChunkCodingException("Bad chunk header"); + } + default: + throw new IllegalStateException("Inconsistent codec state"); + } + } + + /** + * Reads and stores the Trailer headers. + * @throws IOException in case of an I/O error + */ + private void parseTrailerHeaders() throws IOException { + try { + this.footers = AbstractMessageParser.parseHeaders + (in, -1, -1, null); + } catch (final HttpException ex) { + final IOException ioe = new MalformedChunkCodingException("Invalid footer: " + + ex.getMessage()); + ioe.initCause(ex); + throw ioe; + } + } + + /** + * Upon close, this reads the remainder of the chunked message, + * leaving the underlying socket at a position to start reading the + * next response without scanning. + * @throws IOException in case of an I/O error + */ + @Override + public void close() throws IOException { + if (!closed) { + try { + if (!eof) { + // read and discard the remainder of the message + final byte buff[] = new byte[BUFFER_SIZE]; + while (read(buff) >= 0) { + } + } + } finally { + eof = true; + closed = true; + } + } + } + + public Header[] getFooters() { + return this.footers.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedOutputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedOutputStream.java new file mode 100644 index 000000000..828df923c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ChunkedOutputStream.java @@ -0,0 +1,208 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; + +/** + * Implements chunked transfer coding. The content is sent in small chunks. + * Entities transferred using this output stream can be of unlimited length. + * Writes are buffered to an internal buffer (2048 default size). + *

    + * Note that this class NEVER closes the underlying stream, even when close + * gets called. Instead, the stream will be marked as closed and no further + * output will be permitted. + * + * + * @since 4.0 + */ +@NotThreadSafe +public class ChunkedOutputStream extends OutputStream { + + // ----------------------------------------------------- Instance Variables + private final SessionOutputBuffer out; + + private final byte[] cache; + + private int cachePosition = 0; + + private boolean wroteLastChunk = false; + + /** True if the stream is closed. */ + private boolean closed = false; + + /** + * Wraps a session output buffer and chunk-encodes the output. + * + * @param out The session output buffer + * @param bufferSize The minimum chunk size (excluding last chunk) + * @throws IOException not thrown + * + * @deprecated (4.3) use {@link ChunkedOutputStream#ChunkedOutputStream(int, SessionOutputBuffer)} + */ + @Deprecated + public ChunkedOutputStream(final SessionOutputBuffer out, final int bufferSize) + throws IOException { + this(bufferSize, out); + } + + /** + * Wraps a session output buffer and chunks the output. The default buffer + * size of 2048 was chosen because the chunk overhead is less than 0.5% + * + * @param out the output buffer to wrap + * @throws IOException not thrown + * + * @deprecated (4.3) use {@link ChunkedOutputStream#ChunkedOutputStream(int, SessionOutputBuffer)} + */ + @Deprecated + public ChunkedOutputStream(final SessionOutputBuffer out) + throws IOException { + this(2048, out); + } + + /** + * Wraps a session output buffer and chunk-encodes the output. + * + * @param bufferSize The minimum chunk size (excluding last chunk) + * @param out The session output buffer + */ + public ChunkedOutputStream(final int bufferSize, final SessionOutputBuffer out) { + super(); + this.cache = new byte[bufferSize]; + this.out = out; + } + + /** + * Writes the cache out onto the underlying stream + */ + protected void flushCache() throws IOException { + if (this.cachePosition > 0) { + this.out.writeLine(Integer.toHexString(this.cachePosition)); + this.out.write(this.cache, 0, this.cachePosition); + this.out.writeLine(""); + this.cachePosition = 0; + } + } + + /** + * Writes the cache and bufferToAppend to the underlying stream + * as one large chunk + */ + protected void flushCacheWithAppend(final byte bufferToAppend[], final int off, final int len) throws IOException { + this.out.writeLine(Integer.toHexString(this.cachePosition + len)); + this.out.write(this.cache, 0, this.cachePosition); + this.out.write(bufferToAppend, off, len); + this.out.writeLine(""); + this.cachePosition = 0; + } + + protected void writeClosingChunk() throws IOException { + // Write the final chunk. + this.out.writeLine("0"); + this.out.writeLine(""); + } + + // ----------------------------------------------------------- Public Methods + /** + * Must be called to ensure the internal cache is flushed and the closing + * chunk is written. + * @throws IOException in case of an I/O error + */ + public void finish() throws IOException { + if (!this.wroteLastChunk) { + flushCache(); + writeClosingChunk(); + this.wroteLastChunk = true; + } + } + + // -------------------------------------------- OutputStream Methods + @Override + public void write(final int b) throws IOException { + if (this.closed) { + throw new IOException("Attempted write to closed stream."); + } + this.cache[this.cachePosition] = (byte) b; + this.cachePosition++; + if (this.cachePosition == this.cache.length) { + flushCache(); + } + } + + /** + * Writes the array. If the array does not fit within the buffer, it is + * not split, but rather written out as one large chunk. + */ + @Override + public void write(final byte b[]) throws IOException { + write(b, 0, b.length); + } + + /** + * Writes the array. If the array does not fit within the buffer, it is + * not split, but rather written out as one large chunk. + */ + @Override + public void write(final byte src[], final int off, final int len) throws IOException { + if (this.closed) { + throw new IOException("Attempted write to closed stream."); + } + if (len >= this.cache.length - this.cachePosition) { + flushCacheWithAppend(src, off, len); + } else { + System.arraycopy(src, off, cache, this.cachePosition, len); + this.cachePosition += len; + } + } + + /** + * Flushes the content buffer and the underlying stream. + */ + @Override + public void flush() throws IOException { + flushCache(); + this.out.flush(); + } + + /** + * Finishes writing to the underlying stream, but does NOT close the underlying stream. + */ + @Override + public void close() throws IOException { + if (!this.closed) { + this.closed = true; + finish(); + this.out.flush(); + } + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthInputStream.java new file mode 100644 index 000000000..65a271cb8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthInputStream.java @@ -0,0 +1,232 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.ConnectionClosedException; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.BufferInfo; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Input stream that cuts off after a defined number of bytes. This class + * is used to receive content of HTTP messages where the end of the content + * entity is determined by the value of the Content-Length header. + * Entities transferred using this stream can be maximum {@link Long#MAX_VALUE} + * long. + *

    + * Note that this class NEVER closes the underlying stream, even when close + * gets called. Instead, it will read until the "end" of its limit on + * close, which allows for the seamless execution of subsequent HTTP 1.1 + * requests, while not requiring the client to remember to read the entire + * contents of the response. + * + * + * @since 4.0 + */ +@NotThreadSafe +public class ContentLengthInputStream extends InputStream { + + private static final int BUFFER_SIZE = 2048; + /** + * The maximum number of bytes that can be read from the stream. Subsequent + * read operations will return -1. + */ + private final long contentLength; + + /** The current position */ + private long pos = 0; + + /** True if the stream is closed. */ + private boolean closed = false; + + /** + * Wrapped input stream that all calls are delegated to. + */ + private SessionInputBuffer in = null; + + /** + * Wraps a session input buffer and cuts off output after a defined number + * of bytes. + * + * @param in The session input buffer + * @param contentLength The maximum number of bytes that can be read from + * the stream. Subsequent read operations will return -1. + */ + public ContentLengthInputStream(final SessionInputBuffer in, final long contentLength) { + super(); + this.in = Args.notNull(in, "Session input buffer"); + this.contentLength = Args.notNegative(contentLength, "Content length"); + } + + /** + *

    Reads until the end of the known length of content.

    + * + *

    Does not close the underlying socket input, but instead leaves it + * primed to parse the next response.

    + * @throws IOException If an IO problem occurs. + */ + @Override + public void close() throws IOException { + if (!closed) { + try { + if (pos < contentLength) { + final byte buffer[] = new byte[BUFFER_SIZE]; + while (read(buffer) >= 0) { + } + } + } finally { + // close after above so that we don't throw an exception trying + // to read after closed! + closed = true; + } + } + } + + @Override + public int available() throws IOException { + if (this.in instanceof BufferInfo) { + final int len = ((BufferInfo) this.in).length(); + return Math.min(len, (int) (this.contentLength - this.pos)); + } else { + return 0; + } + } + + /** + * Read the next byte from the stream + * @return The next byte or -1 if the end of stream has been reached. + * @throws IOException If an IO problem occurs + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException { + if (closed) { + throw new IOException("Attempted read from closed stream."); + } + + if (pos >= contentLength) { + return -1; + } + final int b = this.in.read(); + if (b == -1) { + if (pos < contentLength) { + throw new ConnectionClosedException( + "Premature end of Content-Length delimited message body (expected: " + + contentLength + "; received: " + pos); + } + } else { + pos++; + } + return b; + } + + /** + * Does standard {@link InputStream#read(byte[], int, int)} behavior, but + * also notifies the watcher when the contents have been consumed. + * + * @param b The byte array to fill. + * @param off Start filling at this position. + * @param len The number of bytes to attempt to read. + * @return The number of bytes read, or -1 if the end of content has been + * reached. + * + * @throws java.io.IOException Should an error occur on the wrapped stream. + */ + @Override + public int read (final byte[] b, final int off, final int len) throws java.io.IOException { + if (closed) { + throw new IOException("Attempted read from closed stream."); + } + + if (pos >= contentLength) { + return -1; + } + + int chunk = len; + if (pos + len > contentLength) { + chunk = (int) (contentLength - pos); + } + final int count = this.in.read(b, off, chunk); + if (count == -1 && pos < contentLength) { + throw new ConnectionClosedException( + "Premature end of Content-Length delimited message body (expected: " + + contentLength + "; received: " + pos); + } + if (count > 0) { + pos += count; + } + return count; + } + + + /** + * Read more bytes from the stream. + * @param b The byte array to put the new data in. + * @return The number of bytes read into the buffer. + * @throws IOException If an IO problem occurs + * @see java.io.InputStream#read(byte[]) + */ + @Override + public int read(final byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** + * Skips and discards a number of bytes from the input stream. + * @param n The number of bytes to skip. + * @return The actual number of bytes skipped. <= 0 if no bytes + * are skipped. + * @throws IOException If an error occurs while skipping bytes. + * @see InputStream#skip(long) + */ + @Override + public long skip(final long n) throws IOException { + if (n <= 0) { + return 0; + } + final byte[] buffer = new byte[BUFFER_SIZE]; + // make sure we don't skip more bytes than are + // still available + long remaining = Math.min(n, this.contentLength - this.pos); + // skip and keep track of the bytes actually skipped + long count = 0; + while (remaining > 0) { + final int l = read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining)); + if (l == -1) { + break; + } + count += l; + remaining -= l; + } + return count; + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthOutputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthOutputStream.java new file mode 100644 index 000000000..1c4e588d0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthOutputStream.java @@ -0,0 +1,136 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Output stream that cuts off after a defined number of bytes. This class + * is used to send content of HTTP messages where the end of the content entity + * is determined by the value of the Content-Length header. + * Entities transferred using this stream can be maximum {@link Long#MAX_VALUE} + * long. + *

    + * Note that this class NEVER closes the underlying stream, even when close + * gets called. Instead, the stream will be marked as closed and no further + * output will be permitted. + * + * @since 4.0 + */ +@NotThreadSafe +public class ContentLengthOutputStream extends OutputStream { + + /** + * Wrapped session output buffer. + */ + private final SessionOutputBuffer out; + + /** + * The maximum number of bytes that can be written the stream. Subsequent + * write operations will be ignored. + */ + private final long contentLength; + + /** Total bytes written */ + private long total = 0; + + /** True if the stream is closed. */ + private boolean closed = false; + + /** + * Wraps a session output buffer and cuts off output after a defined number + * of bytes. + * + * @param out The session output buffer + * @param contentLength The maximum number of bytes that can be written to + * the stream. Subsequent write operations will be ignored. + * + * @since 4.0 + */ + public ContentLengthOutputStream(final SessionOutputBuffer out, final long contentLength) { + super(); + this.out = Args.notNull(out, "Session output buffer"); + this.contentLength = Args.notNegative(contentLength, "Content length"); + } + + /** + *

    Does not close the underlying socket output.

    + * + * @throws IOException If an I/O problem occurs. + */ + @Override + public void close() throws IOException { + if (!this.closed) { + this.closed = true; + this.out.flush(); + } + } + + @Override + public void flush() throws IOException { + this.out.flush(); + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + if (this.closed) { + throw new IOException("Attempted write to closed stream."); + } + if (this.total < this.contentLength) { + final long max = this.contentLength - this.total; + int chunk = len; + if (chunk > max) { + chunk = (int) max; + } + this.out.write(b, off, chunk); + this.total += chunk; + } + } + + @Override + public void write(final byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(final int b) throws IOException { + if (this.closed) { + throw new IOException("Attempted write to closed stream."); + } + if (this.total < this.contentLength) { + this.out.write(b); + this.total++; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParser.java new file mode 100644 index 000000000..70383a22e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParser.java @@ -0,0 +1,140 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.ConnectionClosedException; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestFactory; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpRequestFactory; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * HTTP request parser that obtain its input from an instance + * of {@link SessionInputBuffer}. + * + * @since 4.2 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public class DefaultHttpRequestParser extends AbstractMessageParser { + + private final HttpRequestFactory requestFactory; + private final CharArrayBuffer lineBuf; + + /** + * Creates an instance of this class. + * + * @param buffer the session input buffer. + * @param lineParser the line parser. + * @param requestFactory the factory to use to create + * {@link HttpRequest}s. + * @param params HTTP parameters. + * + * @deprecated (4.3) use + * {@link DefaultHttpRequestParser#DefaultHttpRequestParser(SessionInputBuffer, LineParser, + * HttpRequestFactory, MessageConstraints)} + */ + @Deprecated + public DefaultHttpRequestParser( + final SessionInputBuffer buffer, + final LineParser lineParser, + final HttpRequestFactory requestFactory, + final HttpParams params) { + super(buffer, lineParser, params); + this.requestFactory = Args.notNull(requestFactory, "Request factory"); + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * Creates new instance of DefaultHttpRequestParser. + * + * @param buffer the session input buffer. + * @param lineParser the line parser. If null + * {@link ch.boye.httpclientandroidlib.message.BasicLineParser#INSTANCE} will be used. + * @param requestFactory the response factory. If null + * {@link DefaultHttpRequestFactory#INSTANCE} will be used. + * @param constraints the message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.3 + */ + public DefaultHttpRequestParser( + final SessionInputBuffer buffer, + final LineParser lineParser, + final HttpRequestFactory requestFactory, + final MessageConstraints constraints) { + super(buffer, lineParser, constraints); + this.requestFactory = requestFactory != null ? requestFactory : + DefaultHttpRequestFactory.INSTANCE; + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * @since 4.3 + */ + public DefaultHttpRequestParser( + final SessionInputBuffer buffer, + final MessageConstraints constraints) { + this(buffer, null, null, constraints); + } + + /** + * @since 4.3 + */ + public DefaultHttpRequestParser(final SessionInputBuffer buffer) { + this(buffer, null, null, MessageConstraints.DEFAULT); + } + + @Override + protected HttpRequest parseHead( + final SessionInputBuffer sessionBuffer) + throws IOException, HttpException, ParseException { + + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1) { + throw new ConnectionClosedException("Client closed connection"); + } + final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length()); + final RequestLine requestline = this.lineParser.parseRequestLine(this.lineBuf, cursor); + return this.requestFactory.newHttpRequest(requestline); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParserFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParserFactory.java new file mode 100644 index 000000000..81e886059 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestParserFactory.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestFactory; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpRequestFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineParser; +import ch.boye.httpclientandroidlib.message.LineParser; + +/** + * Default factory for request message parsers. + * + * @since 4.3 + */ +@Immutable +public class DefaultHttpRequestParserFactory implements HttpMessageParserFactory { + + public static final DefaultHttpRequestParserFactory INSTANCE = new DefaultHttpRequestParserFactory(); + + private final LineParser lineParser; + private final HttpRequestFactory requestFactory; + + public DefaultHttpRequestParserFactory(final LineParser lineParser, + final HttpRequestFactory requestFactory) { + super(); + this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE; + this.requestFactory = requestFactory != null ? requestFactory + : DefaultHttpRequestFactory.INSTANCE; + } + + public DefaultHttpRequestParserFactory() { + this(null, null); + } + + public HttpMessageParser create(final SessionInputBuffer buffer, + final MessageConstraints constraints) { + return new DefaultHttpRequestParser(buffer, lineParser, requestFactory, constraints); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriter.java new file mode 100644 index 000000000..0f3b31e18 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriter.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.message.LineFormatter; + +/** + * HTTP request writer that serializes its output to an instance of {@link SessionOutputBuffer}. + * + * @since 4.3 + */ +@NotThreadSafe +public class DefaultHttpRequestWriter extends AbstractMessageWriter { + + /** + * Creates an instance of DefaultHttpRequestWriter. + * + * @param buffer the session output buffer. + * @param formatter the line formatter If null + * {@link ch.boye.httpclientandroidlib.message.BasicLineFormatter#INSTANCE} + * will be used. + */ + public DefaultHttpRequestWriter( + final SessionOutputBuffer buffer, + final LineFormatter formatter) { + super(buffer, formatter); + } + + public DefaultHttpRequestWriter(final SessionOutputBuffer buffer) { + this(buffer, null); + } + + @Override + protected void writeHeadLine(final HttpRequest message) throws IOException { + lineFormatter.formatRequestLine(this.lineBuf, message.getRequestLine()); + this.sessionBuffer.writeLine(this.lineBuf); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriterFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriterFactory.java new file mode 100644 index 000000000..410cc2ac3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpRequestWriterFactory.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.io.HttpMessageWriter; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineFormatter; +import ch.boye.httpclientandroidlib.message.LineFormatter; + +/** + * Default factory for request message writers. + * + * @since 4.3 + */ +@Immutable +public class DefaultHttpRequestWriterFactory implements HttpMessageWriterFactory { + + public static final DefaultHttpRequestWriterFactory INSTANCE = new DefaultHttpRequestWriterFactory(); + + private final LineFormatter lineFormatter; + + public DefaultHttpRequestWriterFactory(final LineFormatter lineFormatter) { + super(); + this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE; + } + + public DefaultHttpRequestWriterFactory() { + this(null); + } + + public HttpMessageWriter create(final SessionOutputBuffer buffer) { + return new DefaultHttpRequestWriter(buffer, lineFormatter); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParser.java new file mode 100644 index 000000000..a8428e812 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParser.java @@ -0,0 +1,141 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * HTTP response parser that obtain its input from an instance + * of {@link SessionInputBuffer}. + * + * @since 4.2 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public class DefaultHttpResponseParser extends AbstractMessageParser { + + private final HttpResponseFactory responseFactory; + private final CharArrayBuffer lineBuf; + + /** + * Creates an instance of this class. + * + * @param buffer the session input buffer. + * @param lineParser the line parser. + * @param responseFactory the factory to use to create + * {@link HttpResponse}s. + * @param params HTTP parameters. + * + * @deprecated (4.3) use + * {@link DefaultHttpResponseParser#DefaultHttpResponseParser(SessionInputBuffer, LineParser, + * HttpResponseFactory, MessageConstraints)} + */ + @Deprecated + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, + final LineParser lineParser, + final HttpResponseFactory responseFactory, + final HttpParams params) { + super(buffer, lineParser, params); + this.responseFactory = Args.notNull(responseFactory, "Response factory"); + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * Creates new instance of DefaultHttpResponseParser. + * + * @param buffer the session input buffer. + * @param lineParser the line parser. If null + * {@link ch.boye.httpclientandroidlib.message.BasicLineParser#INSTANCE} will be used + * @param responseFactory the response factory. If null + * {@link DefaultHttpResponseFactory#INSTANCE} will be used. + * @param constraints the message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.3 + */ + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, + final LineParser lineParser, + final HttpResponseFactory responseFactory, + final MessageConstraints constraints) { + super(buffer, lineParser, constraints); + this.responseFactory = responseFactory != null ? responseFactory : + DefaultHttpResponseFactory.INSTANCE; + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * @since 4.3 + */ + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, + final MessageConstraints constraints) { + this(buffer, null, null, constraints); + } + + /** + * @since 4.3 + */ + public DefaultHttpResponseParser(final SessionInputBuffer buffer) { + this(buffer, null, null, MessageConstraints.DEFAULT); + } + + @Override + protected HttpResponse parseHead( + final SessionInputBuffer sessionBuffer) + throws IOException, HttpException, ParseException { + + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1) { + throw new NoHttpResponseException("The target server failed to respond"); + } + //create the status line from the status string + final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length()); + final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor); + return this.responseFactory.newHttpResponse(statusline, null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParserFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParserFactory.java new file mode 100644 index 000000000..318abcf70 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseParserFactory.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineParser; +import ch.boye.httpclientandroidlib.message.LineParser; + +/** + * Default factory for response message parsers. + * + * @since 4.3 + */ +@Immutable +public class DefaultHttpResponseParserFactory implements HttpMessageParserFactory { + + public static final DefaultHttpResponseParserFactory INSTANCE = new DefaultHttpResponseParserFactory(); + + private final LineParser lineParser; + private final HttpResponseFactory responseFactory; + + public DefaultHttpResponseParserFactory(final LineParser lineParser, + final HttpResponseFactory responseFactory) { + super(); + this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE; + this.responseFactory = responseFactory != null ? responseFactory + : DefaultHttpResponseFactory.INSTANCE; + } + + public DefaultHttpResponseParserFactory() { + this(null, null); + } + + public HttpMessageParser create(final SessionInputBuffer buffer, + final MessageConstraints constraints) { + return new DefaultHttpResponseParser(buffer, lineParser, responseFactory, constraints); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriter.java new file mode 100644 index 000000000..8cdef00a5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriter.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.message.LineFormatter; + +/** + * HTTP response writer that serializes its output to an instance of {@link SessionOutputBuffer}. + * + * @since 4.3 + */ +@NotThreadSafe +public class DefaultHttpResponseWriter extends AbstractMessageWriter { + + /** + * Creates an instance of DefaultHttpResponseWriter. + * + * @param buffer the session output buffer. + * @param formatter the line formatter If null + * {@link ch.boye.httpclientandroidlib.message.BasicLineFormatter#INSTANCE} + * will be used. + */ + public DefaultHttpResponseWriter( + final SessionOutputBuffer buffer, + final LineFormatter formatter) { + super(buffer, formatter); + } + + public DefaultHttpResponseWriter(final SessionOutputBuffer buffer) { + super(buffer, null); + } + + @Override + protected void writeHeadLine(final HttpResponse message) throws IOException { + lineFormatter.formatStatusLine(this.lineBuf, message.getStatusLine()); + this.sessionBuffer.writeLine(this.lineBuf); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriterFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriterFactory.java new file mode 100644 index 000000000..9dc29606d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/DefaultHttpResponseWriterFactory.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.io.HttpMessageWriter; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineFormatter; +import ch.boye.httpclientandroidlib.message.LineFormatter; + +/** + * Default factory for response message writers. + * + * @since 4.3 + */ +@Immutable +public class DefaultHttpResponseWriterFactory implements HttpMessageWriterFactory { + + public static final DefaultHttpResponseWriterFactory INSTANCE = new DefaultHttpResponseWriterFactory(); + + private final LineFormatter lineFormatter; + + public DefaultHttpResponseWriterFactory(final LineFormatter lineFormatter) { + super(); + this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE; + } + + public DefaultHttpResponseWriterFactory() { + this(null); + } + + public HttpMessageWriter create(final SessionOutputBuffer buffer) { + return new DefaultHttpResponseWriter(buffer, lineFormatter); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestParser.java new file mode 100644 index 000000000..9c68b6938 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestParser.java @@ -0,0 +1,102 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.ConnectionClosedException; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.HttpRequestFactory; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * HTTP request parser that obtain its input from an instance + * of {@link SessionInputBuffer}. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.2) use {@link DefaultHttpRequestParser} + */ +@Deprecated +@NotThreadSafe +public class HttpRequestParser extends AbstractMessageParser { + + private final HttpRequestFactory requestFactory; + private final CharArrayBuffer lineBuf; + + /** + * Creates an instance of this class. + * + * @param buffer the session input buffer. + * @param parser the line parser. + * @param requestFactory the factory to use to create + * {@link ch.boye.httpclientandroidlib.HttpRequest}s. + * @param params HTTP parameters. + */ + public HttpRequestParser( + final SessionInputBuffer buffer, + final LineParser parser, + final HttpRequestFactory requestFactory, + final HttpParams params) { + super(buffer, parser, params); + this.requestFactory = Args.notNull(requestFactory, "Request factory"); + this.lineBuf = new CharArrayBuffer(128); + } + + @Override + protected HttpMessage parseHead( + final SessionInputBuffer sessionBuffer) + throws IOException, HttpException, ParseException { + + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1) { + throw new ConnectionClosedException("Client closed connection"); + } + final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length()); + final RequestLine requestline = this.lineParser.parseRequestLine(this.lineBuf, cursor); + return this.requestFactory.newHttpRequest(requestline); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestWriter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestWriter.java new file mode 100644 index 000000000..3603ef573 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpRequestWriter.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.message.LineFormatter; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * HTTP request writer that serializes its output to an instance + * of {@link SessionOutputBuffer}. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultHttpRequestWriter} + */ +@NotThreadSafe +@Deprecated +public class HttpRequestWriter extends AbstractMessageWriter { + + public HttpRequestWriter(final SessionOutputBuffer buffer, + final LineFormatter formatter, + final HttpParams params) { + super(buffer, formatter, params); + } + + @Override + protected void writeHeadLine(final HttpRequest message) throws IOException { + lineFormatter.formatRequestLine(this.lineBuf, message.getRequestLine()); + this.sessionBuffer.writeLine(this.lineBuf); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseParser.java new file mode 100644 index 000000000..a94eae36c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseParser.java @@ -0,0 +1,103 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * HTTP response parser that obtain its input from an instance + * of {@link SessionInputBuffer}. + *

    + * The following parameters can be used to customize the behavior of this + * class: + *

      + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}
    • + *
    • {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}
    • + *
    + * + * @since 4.0 + * + * @deprecated (4.2) use {@link DefaultHttpResponseParser} + */ +@Deprecated +@NotThreadSafe +public class HttpResponseParser extends AbstractMessageParser { + + private final HttpResponseFactory responseFactory; + private final CharArrayBuffer lineBuf; + + /** + * Creates an instance of this class. + * + * @param buffer the session input buffer. + * @param parser the line parser. + * @param responseFactory the factory to use to create + * {@link ch.boye.httpclientandroidlib.HttpResponse}s. + * @param params HTTP parameters. + */ + public HttpResponseParser( + final SessionInputBuffer buffer, + final LineParser parser, + final HttpResponseFactory responseFactory, + final HttpParams params) { + super(buffer, parser, params); + this.responseFactory = Args.notNull(responseFactory, "Response factory"); + this.lineBuf = new CharArrayBuffer(128); + } + + @Override + protected HttpMessage parseHead( + final SessionInputBuffer sessionBuffer) + throws IOException, HttpException, ParseException { + + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1) { + throw new NoHttpResponseException("The target server failed to respond"); + } + //create the status line from the status string + final ParserCursor cursor = new ParserCursor(0, this.lineBuf.length()); + final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor); + return this.responseFactory.newHttpResponse(statusline, null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseWriter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseWriter.java new file mode 100644 index 000000000..2e82ade18 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpResponseWriter.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.message.LineFormatter; +import ch.boye.httpclientandroidlib.params.HttpParams; + +/** + * HTTP response writer that serializes its output to an instance + * of {@link SessionOutputBuffer}. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultHttpResponseWriter} + */ +@NotThreadSafe +@Deprecated +public class HttpResponseWriter extends AbstractMessageWriter { + + public HttpResponseWriter(final SessionOutputBuffer buffer, + final LineFormatter formatter, + final HttpParams params) { + super(buffer, formatter, params); + } + + @Override + protected void writeHeadLine(final HttpResponse message) throws IOException { + lineFormatter.formatStatusLine(this.lineBuf, message.getStatusLine()); + this.sessionBuffer.writeLine(this.lineBuf); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpTransportMetricsImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpTransportMetricsImpl.java new file mode 100644 index 000000000..8bd4eae5f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/HttpTransportMetricsImpl.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; + +/** + * Default implementation of {@link HttpTransportMetrics}. + * + * @since 4.0 + */ +@NotThreadSafe +public class HttpTransportMetricsImpl implements HttpTransportMetrics { + + private long bytesTransferred = 0; + + public HttpTransportMetricsImpl() { + super(); + } + + public long getBytesTransferred() { + return this.bytesTransferred; + } + + public void setBytesTransferred(final long count) { + this.bytesTransferred = count; + } + + public void incrementBytesTransferred(final long count) { + this.bytesTransferred += count; + } + + public void reset() { + this.bytesTransferred = 0; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityInputStream.java new file mode 100644 index 000000000..98e1cf81a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityInputStream.java @@ -0,0 +1,99 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.BufferInfo; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Input stream that reads data without any transformation. The end of the + * content entity is demarcated by closing the underlying connection + * (EOF condition). Entities transferred using this input stream can be of + * unlimited length. + *

    + * Note that this class NEVER closes the underlying stream, even when close + * gets called. Instead, it will read until the end of the stream (until + * -1 is returned). + * + * @since 4.0 + */ +@NotThreadSafe +public class IdentityInputStream extends InputStream { + + private final SessionInputBuffer in; + + private boolean closed = false; + + /** + * Wraps session input stream and reads input until the the end of stream. + * + * @param in The session input buffer + */ + public IdentityInputStream(final SessionInputBuffer in) { + super(); + this.in = Args.notNull(in, "Session input buffer"); + } + + @Override + public int available() throws IOException { + if (this.in instanceof BufferInfo) { + return ((BufferInfo) this.in).length(); + } else { + return 0; + } + } + + @Override + public void close() throws IOException { + this.closed = true; + } + + @Override + public int read() throws IOException { + if (this.closed) { + return -1; + } else { + return this.in.read(); + } + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + if (this.closed) { + return -1; + } else { + return this.in.read(b, off, len); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityOutputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityOutputStream.java new file mode 100644 index 000000000..ac2f613b7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/IdentityOutputStream.java @@ -0,0 +1,104 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.OutputStream; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Output stream that writes data without any transformation. The end of + * the content entity is demarcated by closing the underlying connection + * (EOF condition). Entities transferred using this input stream can be of + * unlimited length. + *

    + * Note that this class NEVER closes the underlying stream, even when close + * gets called. Instead, the stream will be marked as closed and no further + * output will be permitted. + * + * @since 4.0 + */ +@NotThreadSafe +public class IdentityOutputStream extends OutputStream { + + /** + * Wrapped session output buffer. + */ + private final SessionOutputBuffer out; + + /** True if the stream is closed. */ + private boolean closed = false; + + public IdentityOutputStream(final SessionOutputBuffer out) { + super(); + this.out = Args.notNull(out, "Session output buffer"); + } + + /** + *

    Does not close the underlying socket output.

    + * + * @throws IOException If an I/O problem occurs. + */ + @Override + public void close() throws IOException { + if (!this.closed) { + this.closed = true; + this.out.flush(); + } + } + + @Override + public void flush() throws IOException { + this.out.flush(); + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + if (this.closed) { + throw new IOException("Attempted write to closed stream."); + } + this.out.write(b, off, len); + } + + @Override + public void write(final byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(final int b) throws IOException { + if (this.closed) { + throw new IOException("Attempted write to closed stream."); + } + this.out.write(b); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionInputBufferImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionInputBufferImpl.java new file mode 100644 index 000000000..ed86cb94b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionInputBufferImpl.java @@ -0,0 +1,399 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; + +import ch.boye.httpclientandroidlib.MessageConstraintException; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.io.BufferInfo; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.ByteArrayBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract base class for session input buffers that stream data from + * an arbitrary {@link InputStream}. This class buffers input data in + * an internal byte array for optimal input performance. + *

    + * {@link #readLine(CharArrayBuffer)} and {@link #readLine()} methods of this + * class treat a lone LF as valid line delimiters in addition to CR-LF required + * by the HTTP specification. + * + * @since 4.3 + */ +@NotThreadSafe +public class SessionInputBufferImpl implements SessionInputBuffer, BufferInfo { + + private final HttpTransportMetricsImpl metrics; + private final byte[] buffer; + private final ByteArrayBuffer linebuffer; + private final int minChunkLimit; + private final MessageConstraints constraints; + private final CharsetDecoder decoder; + + private InputStream instream; + private int bufferpos; + private int bufferlen; + private CharBuffer cbuf; + + /** + * Creates new instance of SessionInputBufferImpl. + * + * @param metrics HTTP transport metrics. + * @param buffersize buffer size. Must be a positive number. + * @param minChunkLimit size limit below which data chunks should be buffered in memory + * in order to minimize native method invocations on the underlying network socket. + * The optimal value of this parameter can be platform specific and defines a trade-off + * between performance of memory copy operations and that of native method invocation. + * If negative default chunk limited will be used. + * @param constraints Message constraints. If null + * {@link MessageConstraints#DEFAULT} will be used. + * @param chardecoder chardecoder to be used for decoding HTTP protocol elements. + * If null simple type cast will be used for byte to char conversion. + */ + public SessionInputBufferImpl( + final HttpTransportMetricsImpl metrics, + final int buffersize, + final int minChunkLimit, + final MessageConstraints constraints, + final CharsetDecoder chardecoder) { + Args.notNull(metrics, "HTTP transport metrcis"); + Args.positive(buffersize, "Buffer size"); + this.metrics = metrics; + this.buffer = new byte[buffersize]; + this.bufferpos = 0; + this.bufferlen = 0; + this.minChunkLimit = minChunkLimit >= 0 ? minChunkLimit : 512; + this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT; + this.linebuffer = new ByteArrayBuffer(buffersize); + this.decoder = chardecoder; + } + + public SessionInputBufferImpl( + final HttpTransportMetricsImpl metrics, + final int buffersize) { + this(metrics, buffersize, buffersize, null, null); + } + + public void bind(final InputStream instream) { + this.instream = instream; + } + + public boolean isBound() { + return this.instream != null; + } + + public int capacity() { + return this.buffer.length; + } + + public int length() { + return this.bufferlen - this.bufferpos; + } + + public int available() { + return capacity() - length(); + } + + private int streamRead(final byte[] b, final int off, final int len) throws IOException { + Asserts.notNull(this.instream, "Input stream"); + return this.instream.read(b, off, len); + } + + public int fillBuffer() throws IOException { + // compact the buffer if necessary + if (this.bufferpos > 0) { + final int len = this.bufferlen - this.bufferpos; + if (len > 0) { + System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len); + } + this.bufferpos = 0; + this.bufferlen = len; + } + final int l; + final int off = this.bufferlen; + final int len = this.buffer.length - off; + l = streamRead(this.buffer, off, len); + if (l == -1) { + return -1; + } else { + this.bufferlen = off + l; + this.metrics.incrementBytesTransferred(l); + return l; + } + } + + public boolean hasBufferedData() { + return this.bufferpos < this.bufferlen; + } + + public void clear() { + this.bufferpos = 0; + this.bufferlen = 0; + } + + public int read() throws IOException { + int noRead; + while (!hasBufferedData()) { + noRead = fillBuffer(); + if (noRead == -1) { + return -1; + } + } + return this.buffer[this.bufferpos++] & 0xff; + } + + public int read(final byte[] b, final int off, final int len) throws IOException { + if (b == null) { + return 0; + } + if (hasBufferedData()) { + final int chunk = Math.min(len, this.bufferlen - this.bufferpos); + System.arraycopy(this.buffer, this.bufferpos, b, off, chunk); + this.bufferpos += chunk; + return chunk; + } + // If the remaining capacity is big enough, read directly from the + // underlying input stream bypassing the buffer. + if (len > this.minChunkLimit) { + final int read = streamRead(b, off, len); + if (read > 0) { + this.metrics.incrementBytesTransferred(read); + } + return read; + } else { + // otherwise read to the buffer first + while (!hasBufferedData()) { + final int noRead = fillBuffer(); + if (noRead == -1) { + return -1; + } + } + final int chunk = Math.min(len, this.bufferlen - this.bufferpos); + System.arraycopy(this.buffer, this.bufferpos, b, off, chunk); + this.bufferpos += chunk; + return chunk; + } + } + + public int read(final byte[] b) throws IOException { + if (b == null) { + return 0; + } + return read(b, 0, b.length); + } + + private int locateLF() { + for (int i = this.bufferpos; i < this.bufferlen; i++) { + if (this.buffer[i] == HTTP.LF) { + return i; + } + } + return -1; + } + + /** + * Reads a complete line of characters up to a line delimiter from this + * session buffer into the given line buffer. The number of chars actually + * read is returned as an integer. The line delimiter itself is discarded. + * If no char is available because the end of the stream has been reached, + * the value -1 is returned. This method blocks until input + * data is available, end of file is detected, or an exception is thrown. + *

    + * This method treats a lone LF as a valid line delimiters in addition + * to CR-LF required by the HTTP specification. + * + * @param charbuffer the line buffer. + * @return one line of characters + * @exception IOException if an I/O error occurs. + */ + public int readLine(final CharArrayBuffer charbuffer) throws IOException { + Args.notNull(charbuffer, "Char array buffer"); + int noRead = 0; + boolean retry = true; + while (retry) { + // attempt to find end of line (LF) + final int i = locateLF(); + if (i != -1) { + // end of line found. + if (this.linebuffer.isEmpty()) { + // the entire line is preset in the read buffer + return lineFromReadBuffer(charbuffer, i); + } + retry = false; + final int len = i + 1 - this.bufferpos; + this.linebuffer.append(this.buffer, this.bufferpos, len); + this.bufferpos = i + 1; + } else { + // end of line not found + if (hasBufferedData()) { + final int len = this.bufferlen - this.bufferpos; + this.linebuffer.append(this.buffer, this.bufferpos, len); + this.bufferpos = this.bufferlen; + } + noRead = fillBuffer(); + if (noRead == -1) { + retry = false; + } + } + final int maxLineLen = this.constraints.getMaxLineLength(); + if (maxLineLen > 0 && this.linebuffer.length() >= maxLineLen) { + throw new MessageConstraintException("Maximum line length limit exceeded"); + } + } + if (noRead == -1 && this.linebuffer.isEmpty()) { + // indicate the end of stream + return -1; + } + return lineFromLineBuffer(charbuffer); + } + + /** + * Reads a complete line of characters up to a line delimiter from this + * session buffer. The line delimiter itself is discarded. If no char is + * available because the end of the stream has been reached, + * null is returned. This method blocks until input data is + * available, end of file is detected, or an exception is thrown. + *

    + * This method treats a lone LF as a valid line delimiters in addition + * to CR-LF required by the HTTP specification. + * + * @return HTTP line as a string + * @exception IOException if an I/O error occurs. + */ + private int lineFromLineBuffer(final CharArrayBuffer charbuffer) + throws IOException { + // discard LF if found + int len = this.linebuffer.length(); + if (len > 0) { + if (this.linebuffer.byteAt(len - 1) == HTTP.LF) { + len--; + } + // discard CR if found + if (len > 0) { + if (this.linebuffer.byteAt(len - 1) == HTTP.CR) { + len--; + } + } + } + if (this.decoder == null) { + charbuffer.append(this.linebuffer, 0, len); + } else { + final ByteBuffer bbuf = ByteBuffer.wrap(this.linebuffer.buffer(), 0, len); + len = appendDecoded(charbuffer, bbuf); + } + this.linebuffer.clear(); + return len; + } + + private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position) + throws IOException { + int pos = position; + final int off = this.bufferpos; + int len; + this.bufferpos = pos + 1; + if (pos > off && this.buffer[pos - 1] == HTTP.CR) { + // skip CR if found + pos--; + } + len = pos - off; + if (this.decoder == null) { + charbuffer.append(this.buffer, off, len); + } else { + final ByteBuffer bbuf = ByteBuffer.wrap(this.buffer, off, len); + len = appendDecoded(charbuffer, bbuf); + } + return len; + } + + private int appendDecoded( + final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException { + if (!bbuf.hasRemaining()) { + return 0; + } + if (this.cbuf == null) { + this.cbuf = CharBuffer.allocate(1024); + } + this.decoder.reset(); + int len = 0; + while (bbuf.hasRemaining()) { + final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true); + len += handleDecodingResult(result, charbuffer, bbuf); + } + final CoderResult result = this.decoder.flush(this.cbuf); + len += handleDecodingResult(result, charbuffer, bbuf); + this.cbuf.clear(); + return len; + } + + private int handleDecodingResult( + final CoderResult result, + final CharArrayBuffer charbuffer, + final ByteBuffer bbuf) throws IOException { + if (result.isError()) { + result.throwException(); + } + this.cbuf.flip(); + final int len = this.cbuf.remaining(); + while (this.cbuf.hasRemaining()) { + charbuffer.append(this.cbuf.get()); + } + this.cbuf.compact(); + return len; + } + + public String readLine() throws IOException { + final CharArrayBuffer charbuffer = new CharArrayBuffer(64); + final int l = readLine(charbuffer); + if (l != -1) { + return charbuffer.toString(); + } else { + return null; + } + } + + public boolean isDataAvailable(final int timeout) throws IOException { + return hasBufferedData(); + } + + public HttpTransportMetrics getMetrics() { + return this.metrics; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionOutputBufferImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionOutputBufferImpl.java new file mode 100644 index 000000000..99ca871ce --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SessionOutputBufferImpl.java @@ -0,0 +1,283 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.BufferInfo; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.ByteArrayBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Abstract base class for session output buffers that stream data to + * an arbitrary {@link OutputStream}. This class buffers small chunks of + * output data in an internal byte array for optimal output performance. + *

    + * {@link #writeLine(CharArrayBuffer)} and {@link #writeLine(String)} methods + * of this class use CR-LF as a line delimiter. + * + * @since 4.3 + */ +@NotThreadSafe +public class SessionOutputBufferImpl implements SessionOutputBuffer, BufferInfo { + + private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF}; + + private final HttpTransportMetricsImpl metrics; + private final ByteArrayBuffer buffer; + private final int fragementSizeHint; + private final CharsetEncoder encoder; + + private OutputStream outstream; + private ByteBuffer bbuf; + + /** + * Creates new instance of SessionOutputBufferImpl. + * + * @param metrics HTTP transport metrics. + * @param buffersize buffer size. Must be a positive number. + * @param fragementSizeHint fragment size hint defining a minimal size of a fragment + * that should be written out directly to the socket bypassing the session buffer. + * Value 0 disables fragment buffering. + * @param charencoder charencoder to be used for encoding HTTP protocol elements. + * If null simple type cast will be used for char to byte conversion. + */ + public SessionOutputBufferImpl( + final HttpTransportMetricsImpl metrics, + final int buffersize, + final int fragementSizeHint, + final CharsetEncoder charencoder) { + super(); + Args.positive(buffersize, "Buffer size"); + Args.notNull(metrics, "HTTP transport metrcis"); + this.metrics = metrics; + this.buffer = new ByteArrayBuffer(buffersize); + this.fragementSizeHint = fragementSizeHint >= 0 ? fragementSizeHint : 0; + this.encoder = charencoder; + } + + public SessionOutputBufferImpl( + final HttpTransportMetricsImpl metrics, + final int buffersize) { + this(metrics, buffersize, buffersize, null); + } + + public void bind(final OutputStream outstream) { + this.outstream = outstream; + } + + public boolean isBound() { + return this.outstream != null; + } + + public int capacity() { + return this.buffer.capacity(); + } + + public int length() { + return this.buffer.length(); + } + + public int available() { + return capacity() - length(); + } + + private void streamWrite(final byte[] b, final int off, final int len) throws IOException { + Asserts.notNull(outstream, "Output stream"); + this.outstream.write(b, off, len); + } + + private void flushStream() throws IOException { + if (this.outstream != null) { + this.outstream.flush(); + } + } + + private void flushBuffer() throws IOException { + final int len = this.buffer.length(); + if (len > 0) { + streamWrite(this.buffer.buffer(), 0, len); + this.buffer.clear(); + this.metrics.incrementBytesTransferred(len); + } + } + + public void flush() throws IOException { + flushBuffer(); + flushStream(); + } + + public void write(final byte[] b, final int off, final int len) throws IOException { + if (b == null) { + return; + } + // Do not want to buffer large-ish chunks + // if the byte array is larger then MIN_CHUNK_LIMIT + // write it directly to the output stream + if (len > this.fragementSizeHint || len > this.buffer.capacity()) { + // flush the buffer + flushBuffer(); + // write directly to the out stream + streamWrite(b, off, len); + this.metrics.incrementBytesTransferred(len); + } else { + // Do not let the buffer grow unnecessarily + final int freecapacity = this.buffer.capacity() - this.buffer.length(); + if (len > freecapacity) { + // flush the buffer + flushBuffer(); + } + // buffer + this.buffer.append(b, off, len); + } + } + + public void write(final byte[] b) throws IOException { + if (b == null) { + return; + } + write(b, 0, b.length); + } + + public void write(final int b) throws IOException { + if (this.fragementSizeHint > 0) { + if (this.buffer.isFull()) { + flushBuffer(); + } + this.buffer.append(b); + } else { + flushBuffer(); + this.outstream.write(b); + } + } + + /** + * Writes characters from the specified string followed by a line delimiter + * to this session buffer. + *

    + * This method uses CR-LF as a line delimiter. + * + * @param s the line. + * @exception IOException if an I/O error occurs. + */ + public void writeLine(final String s) throws IOException { + if (s == null) { + return; + } + if (s.length() > 0) { + if (this.encoder == null) { + for (int i = 0; i < s.length(); i++) { + write(s.charAt(i)); + } + } else { + final CharBuffer cbuf = CharBuffer.wrap(s); + writeEncoded(cbuf); + } + } + write(CRLF); + } + + /** + * Writes characters from the specified char array followed by a line + * delimiter to this session buffer. + *

    + * This method uses CR-LF as a line delimiter. + * + * @param charbuffer the buffer containing chars of the line. + * @exception IOException if an I/O error occurs. + */ + public void writeLine(final CharArrayBuffer charbuffer) throws IOException { + if (charbuffer == null) { + return; + } + if (this.encoder == null) { + int off = 0; + int remaining = charbuffer.length(); + while (remaining > 0) { + int chunk = this.buffer.capacity() - this.buffer.length(); + chunk = Math.min(chunk, remaining); + if (chunk > 0) { + this.buffer.append(charbuffer, off, chunk); + } + if (this.buffer.isFull()) { + flushBuffer(); + } + off += chunk; + remaining -= chunk; + } + } else { + final CharBuffer cbuf = CharBuffer.wrap(charbuffer.buffer(), 0, charbuffer.length()); + writeEncoded(cbuf); + } + write(CRLF); + } + + private void writeEncoded(final CharBuffer cbuf) throws IOException { + if (!cbuf.hasRemaining()) { + return; + } + if (this.bbuf == null) { + this.bbuf = ByteBuffer.allocate(1024); + } + this.encoder.reset(); + while (cbuf.hasRemaining()) { + final CoderResult result = this.encoder.encode(cbuf, this.bbuf, true); + handleEncodingResult(result); + } + final CoderResult result = this.encoder.flush(this.bbuf); + handleEncodingResult(result); + this.bbuf.clear(); + } + + private void handleEncodingResult(final CoderResult result) throws IOException { + if (result.isError()) { + result.throwException(); + } + this.bbuf.flip(); + while (this.bbuf.hasRemaining()) { + write(this.bbuf.get()); + } + this.bbuf.compact(); + } + + public HttpTransportMetrics getMetrics() { + return this.metrics; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketInputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketInputBuffer.java new file mode 100644 index 000000000..4bba8f894 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketInputBuffer.java @@ -0,0 +1,108 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.io.EofSensor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * {@link ch.boye.httpclientandroidlib.io.SessionInputBuffer} implementation + * bound to a {@link Socket}. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link SessionInputBufferImpl} + */ +@NotThreadSafe +@Deprecated +public class SocketInputBuffer extends AbstractSessionInputBuffer implements EofSensor { + + private final Socket socket; + + private boolean eof; + + /** + * Creates an instance of this class. + * + * @param socket the socket to read data from. + * @param buffersize the size of the internal buffer. If this number is less + * than 0 it is set to the value of + * {@link Socket#getReceiveBufferSize()}. If resultant number is less + * than 1024 it is set to 1024. + * @param params HTTP parameters. + */ + public SocketInputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + super(); + Args.notNull(socket, "Socket"); + this.socket = socket; + this.eof = false; + int n = buffersize; + if (n < 0) { + n = socket.getReceiveBufferSize(); + } + if (n < 1024) { + n = 1024; + } + init(socket.getInputStream(), n, params); + } + + @Override + protected int fillBuffer() throws IOException { + final int i = super.fillBuffer(); + this.eof = i == -1; + return i; + } + + public boolean isDataAvailable(final int timeout) throws IOException { + boolean result = hasBufferedData(); + if (!result) { + final int oldtimeout = this.socket.getSoTimeout(); + try { + this.socket.setSoTimeout(timeout); + fillBuffer(); + result = hasBufferedData(); + } finally { + socket.setSoTimeout(oldtimeout); + } + } + return result; + } + + public boolean isEof() { + return this.eof; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketOutputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketOutputBuffer.java new file mode 100644 index 000000000..9c9ff2d9f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/SocketOutputBuffer.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.impl.io; + +import java.io.IOException; +import java.net.Socket; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * {@link ch.boye.httpclientandroidlib.io.SessionOutputBuffer} implementation + * bound to a {@link Socket}. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link SessionOutputBufferImpl} + */ +@NotThreadSafe +@Deprecated +public class SocketOutputBuffer extends AbstractSessionOutputBuffer { + + /** + * Creates an instance of this class. + * + * @param socket the socket to write data to. + * @param buffersize the size of the internal buffer. If this number is less + * than 0 it is set to the value of + * {@link Socket#getSendBufferSize()}. If resultant number is less + * than 1024 it is set to 1024. + * @param params HTTP parameters. + */ + public SocketOutputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + super(); + Args.notNull(socket, "Socket"); + int n = buffersize; + if (n < 0) { + n = socket.getSendBufferSize(); + } + if (n < 1024) { + n = 1024; + } + init(socket.getOutputStream(), n, params); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/package-info.java new file mode 100644 index 000000000..24c7bee79 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default implementations of message parses and writers + * for synchronous, blocking communication. + */ +package ch.boye.httpclientandroidlib.impl.io; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/package-info.java new file mode 100644 index 000000000..4cf3cdb3a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default implementations of HTTP connections for synchronous, + * blocking communication. + */ +package ch.boye.httpclientandroidlib.impl; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnFactory.java new file mode 100644 index 000000000..a9e26a435 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnFactory.java @@ -0,0 +1,177 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.pool; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.impl.DefaultBHttpClientConnection; +import ch.boye.httpclientandroidlib.impl.DefaultBHttpClientConnectionFactory; +import ch.boye.httpclientandroidlib.params.CoreConnectionPNames; +import ch.boye.httpclientandroidlib.params.HttpParamConfig; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.pool.ConnFactory; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A very basic {@link ConnFactory} implementation that creates + * {@link HttpClientConnection} instances given a {@link HttpHost} instance. + * + * @see HttpHost + * @since 4.2 + */ +@SuppressWarnings("deprecation") +@Immutable +public class BasicConnFactory implements ConnFactory { + + private final SocketFactory plainfactory; + private final SSLSocketFactory sslfactory; + private final int connectTimeout; + private final SocketConfig sconfig; + private final HttpConnectionFactory connFactory; + + /** + * @deprecated (4.3) use + * {@link BasicConnFactory#BasicConnFactory(SocketFactory, SSLSocketFactory, int, + * SocketConfig, ConnectionConfig)}. + */ + @Deprecated + public BasicConnFactory(final SSLSocketFactory sslfactory, final HttpParams params) { + super(); + Args.notNull(params, "HTTP params"); + this.plainfactory = null; + this.sslfactory = sslfactory; + this.connectTimeout = params.getIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 0); + this.sconfig = HttpParamConfig.getSocketConfig(params); + this.connFactory = new DefaultBHttpClientConnectionFactory( + HttpParamConfig.getConnectionConfig(params)); + } + + /** + * @deprecated (4.3) use + * {@link BasicConnFactory#BasicConnFactory(int, SocketConfig, ConnectionConfig)}. + */ + @Deprecated + public BasicConnFactory(final HttpParams params) { + this(null, params); + } + + /** + * @since 4.3 + */ + public BasicConnFactory( + final SocketFactory plainfactory, + final SSLSocketFactory sslfactory, + final int connectTimeout, + final SocketConfig sconfig, + final ConnectionConfig cconfig) { + super(); + this.plainfactory = plainfactory; + this.sslfactory = sslfactory; + this.connectTimeout = connectTimeout; + this.sconfig = sconfig != null ? sconfig : SocketConfig.DEFAULT; + this.connFactory = new DefaultBHttpClientConnectionFactory( + cconfig != null ? cconfig : ConnectionConfig.DEFAULT); + } + + /** + * @since 4.3 + */ + public BasicConnFactory( + final int connectTimeout, final SocketConfig sconfig, final ConnectionConfig cconfig) { + this(null, null, connectTimeout, sconfig, cconfig); + } + + /** + * @since 4.3 + */ + public BasicConnFactory(final SocketConfig sconfig, final ConnectionConfig cconfig) { + this(null, null, 0, sconfig, cconfig); + } + + /** + * @since 4.3 + */ + public BasicConnFactory() { + this(null, null, 0, SocketConfig.DEFAULT, ConnectionConfig.DEFAULT); + } + + /** + * @deprecated (4.3) no longer used. + */ + @Deprecated + protected HttpClientConnection create(final Socket socket, final HttpParams params) throws IOException { + final int bufsize = params.getIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024); + final DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection(bufsize); + conn.bind(socket); + return conn; + } + + public HttpClientConnection create(final HttpHost host) throws IOException { + final String scheme = host.getSchemeName(); + Socket socket = null; + if ("http".equalsIgnoreCase(scheme)) { + socket = this.plainfactory != null ? this.plainfactory.createSocket() : + new Socket(); + } if ("https".equalsIgnoreCase(scheme)) { + socket = (this.sslfactory != null ? this.sslfactory : + SSLSocketFactory.getDefault()).createSocket(); + } + if (socket == null) { + throw new IOException(scheme + " scheme is not supported"); + } + final String hostname = host.getHostName(); + int port = host.getPort(); + if (port == -1) { + if (host.getSchemeName().equalsIgnoreCase("http")) { + port = 80; + } else if (host.getSchemeName().equalsIgnoreCase("https")) { + port = 443; + } + } + socket.setSoTimeout(this.sconfig.getSoTimeout()); + socket.connect(new InetSocketAddress(hostname, port), this.connectTimeout); + socket.setTcpNoDelay(this.sconfig.isTcpNoDelay()); + final int linger = this.sconfig.getSoLinger(); + if (linger >= 0) { + socket.setSoLinger(linger > 0, linger); + } + socket.setKeepAlive(this.sconfig.isSoKeepAlive()); + return this.connFactory.createConnection(socket); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnPool.java new file mode 100644 index 000000000..c99badd3e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicConnPool.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.pool; + +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.pool.AbstractConnPool; +import ch.boye.httpclientandroidlib.pool.ConnFactory; + +/** + * A very basic {@link ch.boye.httpclientandroidlib.pool.ConnPool} implementation that + * represents a pool of blocking {@link HttpClientConnection} connections + * identified by an {@link HttpHost} instance. Please note this pool + * implementation does not support complex routes via a proxy cannot + * differentiate between direct and proxied connections. + * + * @see HttpHost + * @since 4.2 + */ +@SuppressWarnings("deprecation") +@ThreadSafe +public class BasicConnPool extends AbstractConnPool { + + private static final AtomicLong COUNTER = new AtomicLong(); + + public BasicConnPool(final ConnFactory connFactory) { + super(connFactory, 2, 20); + } + + /** + * @deprecated (4.3) use {@link BasicConnPool#BasicConnPool(SocketConfig, ConnectionConfig)} + */ + @Deprecated + public BasicConnPool(final HttpParams params) { + super(new BasicConnFactory(params), 2, 20); + } + + /** + * @since 4.3 + */ + public BasicConnPool(final SocketConfig sconfig, final ConnectionConfig cconfig) { + super(new BasicConnFactory(sconfig, cconfig), 2, 20); + } + + /** + * @since 4.3 + */ + public BasicConnPool() { + super(new BasicConnFactory(SocketConfig.DEFAULT, ConnectionConfig.DEFAULT), 2, 20); + } + + @Override + protected BasicPoolEntry createEntry( + final HttpHost host, + final HttpClientConnection conn) { + return new BasicPoolEntry(Long.toString(COUNTER.getAndIncrement()), host, conn); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicPoolEntry.java new file mode 100644 index 000000000..465525c33 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/BasicPoolEntry.java @@ -0,0 +1,64 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.impl.pool; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.pool.PoolEntry; + +/** + * A very basic {@link PoolEntry} implementation that represents an entry + * in a pool of blocking {@link HttpClientConnection}s identified by + * an {@link HttpHost} instance. + * + * @see HttpHost + * @since 4.2 + */ +@ThreadSafe +public class BasicPoolEntry extends PoolEntry { + + public BasicPoolEntry(final String id, final HttpHost route, final HttpClientConnection conn) { + super(id, route, conn); + } + + @Override + public void close() { + try { + this.getConnection().close(); + } catch (final IOException ignore) { + } + } + + @Override + public boolean isClosed() { + return !this.getConnection().isOpen(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/package-info.java new file mode 100644 index 000000000..534e1885c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/pool/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Default implementations of client side connection pools + * for synchronous, blocking communication. + */ +package ch.boye.httpclientandroidlib.impl.pool; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/BufferInfo.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/BufferInfo.java new file mode 100644 index 000000000..b75ddd8fc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/BufferInfo.java @@ -0,0 +1,58 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +/** + * Basic buffer properties. + * + * @since 4.1 + */ +public interface BufferInfo { + + /** + * Return length data stored in the buffer + * + * @return data length + */ + int length(); + + /** + * Returns total capacity of the buffer + * + * @return total capacity + */ + int capacity(); + + /** + * Returns available space in the buffer. + * + * @return available space. + */ + int available(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/EofSensor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/EofSensor.java new file mode 100644 index 000000000..bd577b7e4 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/EofSensor.java @@ -0,0 +1,42 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +/** + * EOF sensor. + * + * @since 4.0 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +public interface EofSensor { + + boolean isEof(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParser.java new file mode 100644 index 000000000..2a6f4f3f8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParser.java @@ -0,0 +1,56 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; + +/** + * Abstract message parser intended to build HTTP messages from an arbitrary data source. + * + * @param + * {@link HttpMessage} or a subclass + * + * @since 4.0 + */ +public interface HttpMessageParser { + + /** + * Generates an instance of {@link HttpMessage} from the underlying data + * source. + * + * @return HTTP message + * @throws IOException in case of an I/O error + * @throws HttpException in case of HTTP protocol violation + */ + T parse() + throws IOException, HttpException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParserFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParserFactory.java new file mode 100644 index 000000000..c550120a5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageParserFactory.java @@ -0,0 +1,42 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.config.MessageConstraints; + +/** + * Factory for {@link HttpMessageParser} instances. + * + * @since 4.3 + */ +public interface HttpMessageParserFactory { + + HttpMessageParser create(SessionInputBuffer buffer, MessageConstraints constraints); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriter.java new file mode 100644 index 000000000..7b4557e92 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriter.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; + +/** + * Abstract message writer intended to serialize HTTP messages to an arbitrary + * data sink. + * + * @since 4.0 + */ +public interface HttpMessageWriter { + + /** + * Serializes an instance of {@link HttpMessage} to the underlying data + * sink. + * + * @param message HTTP message + * @throws IOException in case of an I/O error + * @throws HttpException in case of HTTP protocol violation + */ + void write(T message) + throws IOException, HttpException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriterFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriterFactory.java new file mode 100644 index 000000000..3e8ad0552 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpMessageWriterFactory.java @@ -0,0 +1,41 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +import ch.boye.httpclientandroidlib.HttpMessage; + +/** + * Factory for {@link HttpMessageWriter} instances. + * + * @since 4.3 + */ +public interface HttpMessageWriterFactory { + + HttpMessageWriter create(SessionOutputBuffer buffer); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpTransportMetrics.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpTransportMetrics.java new file mode 100644 index 000000000..6eeb6ede1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/HttpTransportMetrics.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +/** + * The point of access to the statistics of {@link SessionInputBuffer} or + * {@link SessionOutputBuffer}. + * + * @since 4.0 + */ +public interface HttpTransportMetrics { + + /** + * Returns the number of bytes transferred. + */ + long getBytesTransferred(); + + /** + * Resets the counts + */ + void reset(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionInputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionInputBuffer.java new file mode 100644 index 000000000..80417801d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionInputBuffer.java @@ -0,0 +1,152 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Session input buffer for blocking connections. This interface is similar to + * InputStream class, but it also provides methods for reading lines of text. + *

    + * Implementing classes are also expected to manage intermediate data buffering + * for optimal input performance. + * + * @since 4.0 + */ +public interface SessionInputBuffer { + + /** + * Reads up to len bytes of data from the session buffer into + * an array of bytes. An attempt is made to read as many as + * len bytes, but a smaller number may be read, possibly + * zero. The number of bytes actually read is returned as an integer. + * + *

    This method blocks until input data is available, end of file is + * detected, or an exception is thrown. + * + *

    If off is negative, or len is negative, or + * off+len is greater than the length of the array + * b, then an IndexOutOfBoundsException is + * thrown. + * + * @param b the buffer into which the data is read. + * @param off the start offset in array b + * at which the data is written. + * @param len the maximum number of bytes to read. + * @return the total number of bytes read into the buffer, or + * -1 if there is no more data because the end of + * the stream has been reached. + * @exception IOException if an I/O error occurs. + */ + int read(byte[] b, int off, int len) throws IOException; + + /** + * Reads some number of bytes from the session buffer and stores them into + * the buffer array b. The number of bytes actually read is + * returned as an integer. This method blocks until input data is + * available, end of file is detected, or an exception is thrown. + * + * @param b the buffer into which the data is read. + * @return the total number of bytes read into the buffer, or + * -1 is there is no more data because the end of + * the stream has been reached. + * @exception IOException if an I/O error occurs. + */ + int read(byte[] b) throws IOException; + + /** + * Reads the next byte of data from this session buffer. The value byte is + * returned as an int in the range 0 to + * 255. If no byte is available because the end of the stream + * has been reached, the value -1 is returned. This method + * blocks until input data is available, the end of the stream is detected, + * or an exception is thrown. + * + * @return the next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + */ + int read() throws IOException; + + /** + * Reads a complete line of characters up to a line delimiter from this + * session buffer into the given line buffer. The number of chars actually + * read is returned as an integer. The line delimiter itself is discarded. + * If no char is available because the end of the stream has been reached, + * the value -1 is returned. This method blocks until input + * data is available, end of file is detected, or an exception is thrown. + *

    + * The choice of a char encoding and line delimiter sequence is up to the + * specific implementations of this interface. + * + * @param buffer the line buffer. + * @return one line of characters + * @exception IOException if an I/O error occurs. + */ + int readLine(CharArrayBuffer buffer) throws IOException; + + /** + * Reads a complete line of characters up to a line delimiter from this + * session buffer. The line delimiter itself is discarded. If no char is + * available because the end of the stream has been reached, + * null is returned. This method blocks until input data is + * available, end of file is detected, or an exception is thrown. + *

    + * The choice of a char encoding and line delimiter sequence is up to the + * specific implementations of this interface. + * + * @return HTTP line as a string + * @exception IOException if an I/O error occurs. + */ + String readLine() throws IOException; + + /** Blocks until some data becomes available in the session buffer or the + * given timeout period in milliseconds elapses. If the timeout value is + * 0 this method blocks indefinitely. + * + * @param timeout in milliseconds. + * @return true if some data is available in the session + * buffer or false otherwise. + * @exception IOException if an I/O error occurs. + * + * @deprecated (4.3) do not use. This function should be provided at the + * connection level + */ + @Deprecated + boolean isDataAvailable(int timeout) throws IOException; + + /** + * Returns {@link HttpTransportMetrics} for this session buffer. + * + * @return transport metrics. + */ + HttpTransportMetrics getMetrics(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionOutputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionOutputBuffer.java new file mode 100644 index 000000000..544d17d5a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/SessionOutputBuffer.java @@ -0,0 +1,120 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.io; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Session output buffer for blocking connections. This interface is similar to + * OutputStream class, but it also provides methods for writing lines of text. + *

    + * Implementing classes are also expected to manage intermediate data buffering + * for optimal output performance. + * + * @since 4.0 + */ +public interface SessionOutputBuffer { + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this session buffer. + *

    + * If off is negative, or len is negative, or + * off+len is greater than the length of the array + * b, then an IndexOutOfBoundsException is thrown. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + */ + void write(byte[] b, int off, int len) throws IOException; + + /** + * Writes b.length bytes from the specified byte array + * to this session buffer. + * + * @param b the data. + * @exception IOException if an I/O error occurs. + */ + void write(byte[] b) throws IOException; + + /** + * Writes the specified byte to this session buffer. + * + * @param b the byte. + * @exception IOException if an I/O error occurs. + */ + void write(int b) throws IOException; + + /** + * Writes characters from the specified string followed by a line delimiter + * to this session buffer. + *

    + * The choice of a char encoding and line delimiter sequence is up to the + * specific implementations of this interface. + * + * @param s the line. + * @exception IOException if an I/O error occurs. + */ + void writeLine(String s) throws IOException; + + /** + * Writes characters from the specified char array followed by a line + * delimiter to this session buffer. + *

    + * The choice of a char encoding and line delimiter sequence is up to the + * specific implementations of this interface. + * + * @param buffer the buffer containing chars of the line. + * @exception IOException if an I/O error occurs. + */ + void writeLine(CharArrayBuffer buffer) throws IOException; + + /** + * Flushes this session buffer and forces any buffered output bytes + * to be written out. The general contract of flush is + * that calling it is an indication that, if any bytes previously + * written have been buffered by the implementation of the output + * stream, such bytes should immediately be written to their + * intended destination. + * + * @exception IOException if an I/O error occurs. + */ + void flush() throws IOException; + + /** + * Returns {@link HttpTransportMetrics} for this session buffer. + * + * @return transport metrics. + */ + HttpTransportMetrics getMetrics(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/package-info.java new file mode 100644 index 000000000..8899c5ad6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/io/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * HTTP message parser and writer APIs for synchronous, blocking + * communication. + */ +package ch.boye.httpclientandroidlib.io; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/AbstractHttpMessage.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/AbstractHttpMessage.java new file mode 100644 index 000000000..24f7aef4e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/AbstractHttpMessage.java @@ -0,0 +1,164 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of {@link HttpMessage}. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public abstract class AbstractHttpMessage implements HttpMessage { + + protected HeaderGroup headergroup; + + @Deprecated + protected HttpParams params; + + /** + * @deprecated (4.3) use {@link AbstractHttpMessage#AbstractHttpMessage()} + */ + @Deprecated + protected AbstractHttpMessage(final HttpParams params) { + super(); + this.headergroup = new HeaderGroup(); + this.params = params; + } + + protected AbstractHttpMessage() { + this(null); + } + + // non-javadoc, see interface HttpMessage + public boolean containsHeader(final String name) { + return this.headergroup.containsHeader(name); + } + + // non-javadoc, see interface HttpMessage + public Header[] getHeaders(final String name) { + return this.headergroup.getHeaders(name); + } + + // non-javadoc, see interface HttpMessage + public Header getFirstHeader(final String name) { + return this.headergroup.getFirstHeader(name); + } + + // non-javadoc, see interface HttpMessage + public Header getLastHeader(final String name) { + return this.headergroup.getLastHeader(name); + } + + // non-javadoc, see interface HttpMessage + public Header[] getAllHeaders() { + return this.headergroup.getAllHeaders(); + } + + // non-javadoc, see interface HttpMessage + public void addHeader(final Header header) { + this.headergroup.addHeader(header); + } + + // non-javadoc, see interface HttpMessage + public void addHeader(final String name, final String value) { + Args.notNull(name, "Header name"); + this.headergroup.addHeader(new BasicHeader(name, value)); + } + + // non-javadoc, see interface HttpMessage + public void setHeader(final Header header) { + this.headergroup.updateHeader(header); + } + + // non-javadoc, see interface HttpMessage + public void setHeader(final String name, final String value) { + Args.notNull(name, "Header name"); + this.headergroup.updateHeader(new BasicHeader(name, value)); + } + + // non-javadoc, see interface HttpMessage + public void setHeaders(final Header[] headers) { + this.headergroup.setHeaders(headers); + } + + // non-javadoc, see interface HttpMessage + public void removeHeader(final Header header) { + this.headergroup.removeHeader(header); + } + + // non-javadoc, see interface HttpMessage + public void removeHeaders(final String name) { + if (name == null) { + return; + } + for (final HeaderIterator i = this.headergroup.iterator(); i.hasNext(); ) { + final Header header = i.nextHeader(); + if (name.equalsIgnoreCase(header.getName())) { + i.remove(); + } + } + } + + // non-javadoc, see interface HttpMessage + public HeaderIterator headerIterator() { + return this.headergroup.iterator(); + } + + // non-javadoc, see interface HttpMessage + public HeaderIterator headerIterator(final String name) { + return this.headergroup.iterator(name); + } + + /** + * @deprecated (4.3) use constructor parameters of configuration API provided by HttpClient + */ + @Deprecated + public HttpParams getParams() { + if (this.params == null) { + this.params = new BasicHttpParams(); + } + return this.params; + } + + /** + * @deprecated (4.3) use constructor parameters of configuration API provided by HttpClient + */ + @Deprecated + public void setParams(final HttpParams params) { + this.params = Args.notNull(params, "HTTP parameters"); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeader.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeader.java new file mode 100644 index 000000000..170bf6bfe --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeader.java @@ -0,0 +1,91 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of {@link Header}. + * + * @since 4.0 + */ +@Immutable +public class BasicHeader implements Header, Cloneable, Serializable { + + private static final long serialVersionUID = -5427236326487562174L; + + private final String name; + private final String value; + + /** + * Constructor with name and value + * + * @param name the header name + * @param value the header value + */ + public BasicHeader(final String name, final String value) { + super(); + this.name = Args.notNull(name, "Name"); + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + @Override + public String toString() { + // no need for non-default formatting in toString() + return BasicLineFormatter.INSTANCE.formatHeader(null, this).toString(); + } + + public HeaderElement[] getElements() throws ParseException { + if (this.value != null) { + // result intentionally not cached, it's probably not used again + return BasicHeaderValueParser.parseElements(this.value, null); + } else { + return new HeaderElement[] {}; + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElement.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElement.java new file mode 100644 index 000000000..e815f2cb1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElement.java @@ -0,0 +1,162 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Basic implementation of {@link HeaderElement} + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicHeaderElement implements HeaderElement, Cloneable { + + private final String name; + private final String value; + private final NameValuePair[] parameters; + + /** + * Constructor with name, value and parameters. + * + * @param name header element name + * @param value header element value. May be null + * @param parameters header element parameters. May be null. + * Parameters are copied by reference, not by value + */ + public BasicHeaderElement( + final String name, + final String value, + final NameValuePair[] parameters) { + super(); + this.name = Args.notNull(name, "Name"); + this.value = value; + if (parameters != null) { + this.parameters = parameters; + } else { + this.parameters = new NameValuePair[] {}; + } + } + + /** + * Constructor with name and value. + * + * @param name header element name + * @param value header element value. May be null + */ + public BasicHeaderElement(final String name, final String value) { + this(name, value, null); + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + public NameValuePair[] getParameters() { + return this.parameters.clone(); + } + + public int getParameterCount() { + return this.parameters.length; + } + + public NameValuePair getParameter(final int index) { + // ArrayIndexOutOfBoundsException is appropriate + return this.parameters[index]; + } + + public NameValuePair getParameterByName(final String name) { + Args.notNull(name, "Name"); + NameValuePair found = null; + for (final NameValuePair current : this.parameters) { + if (current.getName().equalsIgnoreCase(name)) { + found = current; + break; + } + } + return found; + } + + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (object instanceof HeaderElement) { + final BasicHeaderElement that = (BasicHeaderElement) object; + return this.name.equals(that.name) + && LangUtils.equals(this.value, that.value) + && LangUtils.equals(this.parameters, that.parameters); + } else { + return false; + } + } + + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.name); + hash = LangUtils.hashCode(hash, this.value); + for (final NameValuePair parameter : this.parameters) { + hash = LangUtils.hashCode(hash, parameter); + } + return hash; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append(this.name); + if (this.value != null) { + buffer.append("="); + buffer.append(this.value); + } + for (final NameValuePair parameter : this.parameters) { + buffer.append("; "); + buffer.append(parameter); + } + return buffer.toString(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + // parameters array is considered immutable + // no need to make a copy of it + return super.clone(); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElementIterator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElementIterator.java new file mode 100644 index 000000000..f14f00f0d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderElementIterator.java @@ -0,0 +1,151 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.util.NoSuchElementException; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HeaderElementIterator; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Basic implementation of a {@link HeaderElementIterator}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicHeaderElementIterator implements HeaderElementIterator { + + private final HeaderIterator headerIt; + private final HeaderValueParser parser; + + private HeaderElement currentElement = null; + private CharArrayBuffer buffer = null; + private ParserCursor cursor = null; + + /** + * Creates a new instance of BasicHeaderElementIterator + */ + public BasicHeaderElementIterator( + final HeaderIterator headerIterator, + final HeaderValueParser parser) { + this.headerIt = Args.notNull(headerIterator, "Header iterator"); + this.parser = Args.notNull(parser, "Parser"); + } + + + public BasicHeaderElementIterator(final HeaderIterator headerIterator) { + this(headerIterator, BasicHeaderValueParser.INSTANCE); + } + + + private void bufferHeaderValue() { + this.cursor = null; + this.buffer = null; + while (this.headerIt.hasNext()) { + final Header h = this.headerIt.nextHeader(); + if (h instanceof FormattedHeader) { + this.buffer = ((FormattedHeader) h).getBuffer(); + this.cursor = new ParserCursor(0, this.buffer.length()); + this.cursor.updatePos(((FormattedHeader) h).getValuePos()); + break; + } else { + final String value = h.getValue(); + if (value != null) { + this.buffer = new CharArrayBuffer(value.length()); + this.buffer.append(value); + this.cursor = new ParserCursor(0, this.buffer.length()); + break; + } + } + } + } + + private void parseNextElement() { + // loop while there are headers left to parse + while (this.headerIt.hasNext() || this.cursor != null) { + if (this.cursor == null || this.cursor.atEnd()) { + // get next header value + bufferHeaderValue(); + } + // Anything buffered? + if (this.cursor != null) { + // loop while there is data in the buffer + while (!this.cursor.atEnd()) { + final HeaderElement e = this.parser.parseHeaderElement(this.buffer, this.cursor); + if (!(e.getName().length() == 0 && e.getValue() == null)) { + // Found something + this.currentElement = e; + return; + } + } + // if at the end of the buffer + if (this.cursor.atEnd()) { + // discard it + this.cursor = null; + this.buffer = null; + } + } + } + } + + public boolean hasNext() { + if (this.currentElement == null) { + parseNextElement(); + } + return this.currentElement != null; + } + + public HeaderElement nextElement() throws NoSuchElementException { + if (this.currentElement == null) { + parseNextElement(); + } + + if (this.currentElement == null) { + throw new NoSuchElementException("No more header elements available"); + } + + final HeaderElement element = this.currentElement; + this.currentElement = null; + return element; + } + + public final Object next() throws NoSuchElementException { + return nextElement(); + } + + public void remove() throws UnsupportedOperationException { + throw new UnsupportedOperationException("Remove not supported"); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderIterator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderIterator.java new file mode 100644 index 000000000..5c8c87fbf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderIterator.java @@ -0,0 +1,175 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.util.NoSuchElementException; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of a {@link HeaderIterator}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicHeaderIterator implements HeaderIterator { + + /** + * An array of headers to iterate over. + * Not all elements of this array are necessarily part of the iteration. + * This array will never be modified by the iterator. + * Derived implementations are expected to adhere to this restriction. + */ + protected final Header[] allHeaders; + + + /** + * The position of the next header in {@link #allHeaders allHeaders}. + * Negative if the iteration is over. + */ + protected int currentIndex; + + + /** + * The header name to filter by. + * null to iterate over all headers in the array. + */ + protected String headerName; + + + + /** + * Creates a new header iterator. + * + * @param headers an array of headers over which to iterate + * @param name the name of the headers over which to iterate, or + * null for any + */ + public BasicHeaderIterator(final Header[] headers, final String name) { + super(); + this.allHeaders = Args.notNull(headers, "Header array"); + this.headerName = name; + this.currentIndex = findNext(-1); + } + + + /** + * Determines the index of the next header. + * + * @param pos one less than the index to consider first, + * -1 to search for the first header + * + * @return the index of the next header that matches the filter name, + * or negative if there are no more headers + */ + protected int findNext(final int pos) { + int from = pos; + if (from < -1) { + return -1; + } + + final int to = this.allHeaders.length-1; + boolean found = false; + while (!found && (from < to)) { + from++; + found = filterHeader(from); + } + return found ? from : -1; + } + + + /** + * Checks whether a header is part of the iteration. + * + * @param index the index of the header to check + * + * @return true if the header should be part of the + * iteration, false to skip + */ + protected boolean filterHeader(final int index) { + return (this.headerName == null) || + this.headerName.equalsIgnoreCase(this.allHeaders[index].getName()); + } + + + // non-javadoc, see interface HeaderIterator + public boolean hasNext() { + return (this.currentIndex >= 0); + } + + + /** + * Obtains the next header from this iteration. + * + * @return the next header in this iteration + * + * @throws NoSuchElementException if there are no more headers + */ + public Header nextHeader() + throws NoSuchElementException { + + final int current = this.currentIndex; + if (current < 0) { + throw new NoSuchElementException("Iteration already finished."); + } + + this.currentIndex = findNext(current); + + return this.allHeaders[current]; + } + + + /** + * Returns the next header. + * Same as {@link #nextHeader nextHeader}, but not type-safe. + * + * @return the next header in this iteration + * + * @throws NoSuchElementException if there are no more headers + */ + public final Object next() + throws NoSuchElementException { + return nextHeader(); + } + + + /** + * Removing headers is not supported. + * + * @throws UnsupportedOperationException always + */ + public void remove() + throws UnsupportedOperationException { + + throw new UnsupportedOperationException + ("Removing headers is not supported."); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueFormatter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueFormatter.java new file mode 100644 index 000000000..b7e731759 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueFormatter.java @@ -0,0 +1,422 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Basic implementation for formatting header value elements. + * Instances of this class are stateless and thread-safe. + * Derived classes are expected to maintain these properties. + * + * @since 4.0 + */ +@Immutable +public class BasicHeaderValueFormatter implements HeaderValueFormatter { + + /** + * A default instance of this class, for use as default or fallback. + * Note that {@link BasicHeaderValueFormatter} is not a singleton, there + * can be many instances of the class itself and of derived classes. + * The instance here provides non-customized, default behavior. + * + * @deprecated (4.3) use {@link #INSTANCE} + */ + @Deprecated + public final static + BasicHeaderValueFormatter DEFAULT = new BasicHeaderValueFormatter(); + + public final static BasicHeaderValueFormatter INSTANCE = new BasicHeaderValueFormatter(); + + /** + * Special characters that can be used as separators in HTTP parameters. + * These special characters MUST be in a quoted string to be used within + * a parameter value . + */ + public final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t"; + + /** + * Unsafe special characters that must be escaped using the backslash + * character + */ + public final static String UNSAFE_CHARS = "\"\\"; + + public BasicHeaderValueFormatter() { + super(); + } + + /** + * Formats an array of header elements. + * + * @param elems the header elements to format + * @param quote true to always format with quoted values, + * false to use quotes only when necessary + * @param formatter the formatter to use, or null + * for the {@link #INSTANCE default} + * + * @return the formatted header elements + */ + public static + String formatElements(final HeaderElement[] elems, + final boolean quote, + final HeaderValueFormatter formatter) { + return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) + .formatElements(null, elems, quote).toString(); + } + + + // non-javadoc, see interface HeaderValueFormatter + public CharArrayBuffer formatElements(final CharArrayBuffer charBuffer, + final HeaderElement[] elems, + final boolean quote) { + Args.notNull(elems, "Header element array"); + final int len = estimateElementsLen(elems); + CharArrayBuffer buffer = charBuffer; + if (buffer == null) { + buffer = new CharArrayBuffer(len); + } else { + buffer.ensureCapacity(len); + } + + for (int i=0; i 0) { + buffer.append(", "); + } + formatHeaderElement(buffer, elems[i], quote); + } + + return buffer; + } + + + /** + * Estimates the length of formatted header elements. + * + * @param elems the header elements to format, or null + * + * @return a length estimate, in number of characters + */ + protected int estimateElementsLen(final HeaderElement[] elems) { + if ((elems == null) || (elems.length < 1)) { + return 0; + } + + int result = (elems.length-1) * 2; // elements separated by ", " + for (final HeaderElement elem : elems) { + result += estimateHeaderElementLen(elem); + } + + return result; + } + + + + /** + * Formats a header element. + * + * @param elem the header element to format + * @param quote true to always format with quoted values, + * false to use quotes only when necessary + * @param formatter the formatter to use, or null + * for the {@link #INSTANCE default} + * + * @return the formatted header element + */ + public static + String formatHeaderElement(final HeaderElement elem, + final boolean quote, + final HeaderValueFormatter formatter) { + return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) + .formatHeaderElement(null, elem, quote).toString(); + } + + + // non-javadoc, see interface HeaderValueFormatter + public CharArrayBuffer formatHeaderElement(final CharArrayBuffer charBuffer, + final HeaderElement elem, + final boolean quote) { + Args.notNull(elem, "Header element"); + final int len = estimateHeaderElementLen(elem); + CharArrayBuffer buffer = charBuffer; + if (buffer == null) { + buffer = new CharArrayBuffer(len); + } else { + buffer.ensureCapacity(len); + } + + buffer.append(elem.getName()); + final String value = elem.getValue(); + if (value != null) { + buffer.append('='); + doFormatValue(buffer, value, quote); + } + + final int parcnt = elem.getParameterCount(); + if (parcnt > 0) { + for (int i=0; inull + * + * @return a length estimate, in number of characters + */ + protected int estimateHeaderElementLen(final HeaderElement elem) { + if (elem == null) { + return 0; + } + + int result = elem.getName().length(); // name + final String value = elem.getValue(); + if (value != null) { + // assume quotes, but no escaped characters + result += 3 + value.length(); // ="value" + } + + final int parcnt = elem.getParameterCount(); + if (parcnt > 0) { + for (int i=0; i + estimateNameValuePairLen(elem.getParameter(i)); + } + } + + return result; + } + + + + + /** + * Formats a set of parameters. + * + * @param nvps the parameters to format + * @param quote true to always format with quoted values, + * false to use quotes only when necessary + * @param formatter the formatter to use, or null + * for the {@link #INSTANCE default} + * + * @return the formatted parameters + */ + public static + String formatParameters(final NameValuePair[] nvps, + final boolean quote, + final HeaderValueFormatter formatter) { + return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) + .formatParameters(null, nvps, quote).toString(); + } + + + // non-javadoc, see interface HeaderValueFormatter + public CharArrayBuffer formatParameters(final CharArrayBuffer charBuffer, + final NameValuePair[] nvps, + final boolean quote) { + Args.notNull(nvps, "Header parameter array"); + final int len = estimateParametersLen(nvps); + CharArrayBuffer buffer = charBuffer; + if (buffer == null) { + buffer = new CharArrayBuffer(len); + } else { + buffer.ensureCapacity(len); + } + + for (int i = 0; i < nvps.length; i++) { + if (i > 0) { + buffer.append("; "); + } + formatNameValuePair(buffer, nvps[i], quote); + } + + return buffer; + } + + + /** + * Estimates the length of formatted parameters. + * + * @param nvps the parameters to format, or null + * + * @return a length estimate, in number of characters + */ + protected int estimateParametersLen(final NameValuePair[] nvps) { + if ((nvps == null) || (nvps.length < 1)) { + return 0; + } + + int result = (nvps.length-1) * 2; // "; " between the parameters + for (final NameValuePair nvp : nvps) { + result += estimateNameValuePairLen(nvp); + } + + return result; + } + + + /** + * Formats a name-value pair. + * + * @param nvp the name-value pair to format + * @param quote true to always format with a quoted value, + * false to use quotes only when necessary + * @param formatter the formatter to use, or null + * for the {@link #INSTANCE default} + * + * @return the formatted name-value pair + */ + public static + String formatNameValuePair(final NameValuePair nvp, + final boolean quote, + final HeaderValueFormatter formatter) { + return (formatter != null ? formatter : BasicHeaderValueFormatter.INSTANCE) + .formatNameValuePair(null, nvp, quote).toString(); + } + + + // non-javadoc, see interface HeaderValueFormatter + public CharArrayBuffer formatNameValuePair(final CharArrayBuffer charBuffer, + final NameValuePair nvp, + final boolean quote) { + Args.notNull(nvp, "Name / value pair"); + final int len = estimateNameValuePairLen(nvp); + CharArrayBuffer buffer = charBuffer; + if (buffer == null) { + buffer = new CharArrayBuffer(len); + } else { + buffer.ensureCapacity(len); + } + + buffer.append(nvp.getName()); + final String value = nvp.getValue(); + if (value != null) { + buffer.append('='); + doFormatValue(buffer, value, quote); + } + + return buffer; + } + + + /** + * Estimates the length of a formatted name-value pair. + * + * @param nvp the name-value pair to format, or null + * + * @return a length estimate, in number of characters + */ + protected int estimateNameValuePairLen(final NameValuePair nvp) { + if (nvp == null) { + return 0; + } + + int result = nvp.getName().length(); // name + final String value = nvp.getValue(); + if (value != null) { + // assume quotes, but no escaped characters + result += 3 + value.length(); // ="value" + } + return result; + } + + + /** + * Actually formats the value of a name-value pair. + * This does not include a leading = character. + * Called from {@link #formatNameValuePair formatNameValuePair}. + * + * @param buffer the buffer to append to, never null + * @param value the value to append, never null + * @param quote true to always format with quotes, + * false to use quotes only when necessary + */ + protected void doFormatValue(final CharArrayBuffer buffer, + final String value, + final boolean quote) { + + boolean quoteFlag = quote; + if (!quoteFlag) { + for (int i = 0; (i < value.length()) && !quoteFlag; i++) { + quoteFlag = isSeparator(value.charAt(i)); + } + } + + if (quoteFlag) { + buffer.append('"'); + } + for (int i = 0; i < value.length(); i++) { + final char ch = value.charAt(i); + if (isUnsafe(ch)) { + buffer.append('\\'); + } + buffer.append(ch); + } + if (quoteFlag) { + buffer.append('"'); + } + } + + + /** + * Checks whether a character is a {@link #SEPARATORS separator}. + * + * @param ch the character to check + * + * @return true if the character is a separator, + * false otherwise + */ + protected boolean isSeparator(final char ch) { + return SEPARATORS.indexOf(ch) >= 0; + } + + + /** + * Checks whether a character is {@link #UNSAFE_CHARS unsafe}. + * + * @param ch the character to check + * + * @return true if the character is unsafe, + * false otherwise + */ + protected boolean isUnsafe(final char ch) { + return UNSAFE_CHARS.indexOf(ch) >= 0; + } + + +} // class BasicHeaderValueFormatter diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueParser.java new file mode 100644 index 000000000..2e23b23bf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHeaderValueParser.java @@ -0,0 +1,359 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.util.ArrayList; +import java.util.List; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Basic implementation for parsing header values into elements. + * Instances of this class are stateless and thread-safe. + * Derived classes are expected to maintain these properties. + * + * @since 4.0 + */ +@Immutable +public class BasicHeaderValueParser implements HeaderValueParser { + + /** + * A default instance of this class, for use as default or fallback. + * Note that {@link BasicHeaderValueParser} is not a singleton, there + * can be many instances of the class itself and of derived classes. + * The instance here provides non-customized, default behavior. + * + * @deprecated (4.3) use {@link #INSTANCE} + */ + @Deprecated + public final static + BasicHeaderValueParser DEFAULT = new BasicHeaderValueParser(); + + public final static BasicHeaderValueParser INSTANCE = new BasicHeaderValueParser(); + + private final static char PARAM_DELIMITER = ';'; + private final static char ELEM_DELIMITER = ','; + private final static char[] ALL_DELIMITERS = new char[] { + PARAM_DELIMITER, + ELEM_DELIMITER + }; + + public BasicHeaderValueParser() { + super(); + } + + /** + * Parses elements with the given parser. + * + * @param value the header value to parse + * @param parser the parser to use, or null for default + * + * @return array holding the header elements, never null + */ + public static + HeaderElement[] parseElements(final String value, + final HeaderValueParser parser) throws ParseException { + Args.notNull(value, "Value"); + + final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); + buffer.append(value); + final ParserCursor cursor = new ParserCursor(0, value.length()); + return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) + .parseElements(buffer, cursor); + } + + + // non-javadoc, see interface HeaderValueParser + public HeaderElement[] parseElements(final CharArrayBuffer buffer, + final ParserCursor cursor) { + Args.notNull(buffer, "Char array buffer"); + Args.notNull(cursor, "Parser cursor"); + final List elements = new ArrayList(); + while (!cursor.atEnd()) { + final HeaderElement element = parseHeaderElement(buffer, cursor); + if (!(element.getName().length() == 0 && element.getValue() == null)) { + elements.add(element); + } + } + return elements.toArray(new HeaderElement[elements.size()]); + } + + + /** + * Parses an element with the given parser. + * + * @param value the header element to parse + * @param parser the parser to use, or null for default + * + * @return the parsed header element + */ + public static + HeaderElement parseHeaderElement(final String value, + final HeaderValueParser parser) throws ParseException { + Args.notNull(value, "Value"); + + final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); + buffer.append(value); + final ParserCursor cursor = new ParserCursor(0, value.length()); + return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) + .parseHeaderElement(buffer, cursor); + } + + + // non-javadoc, see interface HeaderValueParser + public HeaderElement parseHeaderElement(final CharArrayBuffer buffer, + final ParserCursor cursor) { + Args.notNull(buffer, "Char array buffer"); + Args.notNull(cursor, "Parser cursor"); + final NameValuePair nvp = parseNameValuePair(buffer, cursor); + NameValuePair[] params = null; + if (!cursor.atEnd()) { + final char ch = buffer.charAt(cursor.getPos() - 1); + if (ch != ELEM_DELIMITER) { + params = parseParameters(buffer, cursor); + } + } + return createHeaderElement(nvp.getName(), nvp.getValue(), params); + } + + + /** + * Creates a header element. + * Called from {@link #parseHeaderElement}. + * + * @return a header element representing the argument + */ + protected HeaderElement createHeaderElement( + final String name, + final String value, + final NameValuePair[] params) { + return new BasicHeaderElement(name, value, params); + } + + + /** + * Parses parameters with the given parser. + * + * @param value the parameter list to parse + * @param parser the parser to use, or null for default + * + * @return array holding the parameters, never null + */ + public static + NameValuePair[] parseParameters(final String value, + final HeaderValueParser parser) throws ParseException { + Args.notNull(value, "Value"); + + final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); + buffer.append(value); + final ParserCursor cursor = new ParserCursor(0, value.length()); + return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) + .parseParameters(buffer, cursor); + } + + + + // non-javadoc, see interface HeaderValueParser + public NameValuePair[] parseParameters(final CharArrayBuffer buffer, + final ParserCursor cursor) { + Args.notNull(buffer, "Char array buffer"); + Args.notNull(cursor, "Parser cursor"); + int pos = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + + while (pos < indexTo) { + final char ch = buffer.charAt(pos); + if (HTTP.isWhitespace(ch)) { + pos++; + } else { + break; + } + } + cursor.updatePos(pos); + if (cursor.atEnd()) { + return new NameValuePair[] {}; + } + + final List params = new ArrayList(); + while (!cursor.atEnd()) { + final NameValuePair param = parseNameValuePair(buffer, cursor); + params.add(param); + final char ch = buffer.charAt(cursor.getPos() - 1); + if (ch == ELEM_DELIMITER) { + break; + } + } + + return params.toArray(new NameValuePair[params.size()]); + } + + /** + * Parses a name-value-pair with the given parser. + * + * @param value the NVP to parse + * @param parser the parser to use, or null for default + * + * @return the parsed name-value pair + */ + public static + NameValuePair parseNameValuePair(final String value, + final HeaderValueParser parser) throws ParseException { + Args.notNull(value, "Value"); + + final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); + buffer.append(value); + final ParserCursor cursor = new ParserCursor(0, value.length()); + return (parser != null ? parser : BasicHeaderValueParser.INSTANCE) + .parseNameValuePair(buffer, cursor); + } + + + // non-javadoc, see interface HeaderValueParser + public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, + final ParserCursor cursor) { + return parseNameValuePair(buffer, cursor, ALL_DELIMITERS); + } + + private static boolean isOneOf(final char ch, final char[] chs) { + if (chs != null) { + for (final char ch2 : chs) { + if (ch == ch2) { + return true; + } + } + } + return false; + } + + public NameValuePair parseNameValuePair(final CharArrayBuffer buffer, + final ParserCursor cursor, + final char[] delimiters) { + Args.notNull(buffer, "Char array buffer"); + Args.notNull(cursor, "Parser cursor"); + + boolean terminated = false; + + int pos = cursor.getPos(); + final int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + + // Find name + final String name; + while (pos < indexTo) { + final char ch = buffer.charAt(pos); + if (ch == '=') { + break; + } + if (isOneOf(ch, delimiters)) { + terminated = true; + break; + } + pos++; + } + + if (pos == indexTo) { + terminated = true; + name = buffer.substringTrimmed(indexFrom, indexTo); + } else { + name = buffer.substringTrimmed(indexFrom, pos); + pos++; + } + + if (terminated) { + cursor.updatePos(pos); + return createNameValuePair(name, null); + } + + // Find value + final String value; + int i1 = pos; + + boolean qouted = false; + boolean escaped = false; + while (pos < indexTo) { + final char ch = buffer.charAt(pos); + if (ch == '"' && !escaped) { + qouted = !qouted; + } + if (!qouted && !escaped && isOneOf(ch, delimiters)) { + terminated = true; + break; + } + if (escaped) { + escaped = false; + } else { + escaped = qouted && ch == '\\'; + } + pos++; + } + + int i2 = pos; + // Trim leading white spaces + while (i1 < i2 && (HTTP.isWhitespace(buffer.charAt(i1)))) { + i1++; + } + // Trim trailing white spaces + while ((i2 > i1) && (HTTP.isWhitespace(buffer.charAt(i2 - 1)))) { + i2--; + } + // Strip away quotes if necessary + if (((i2 - i1) >= 2) + && (buffer.charAt(i1) == '"') + && (buffer.charAt(i2 - 1) == '"')) { + i1++; + i2--; + } + value = buffer.substring(i1, i2); + if (terminated) { + pos++; + } + cursor.updatePos(pos); + return createNameValuePair(name, value); + } + + /** + * Creates a name-value pair. + * Called from {@link #parseNameValuePair}. + * + * @param name the name + * @param value the value, or null + * + * @return a name-value pair representing the arguments + */ + protected NameValuePair createNameValuePair(final String name, final String value) { + return new BasicNameValuePair(name, value); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpEntityEnclosingRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpEntityEnclosingRequest.java new file mode 100644 index 000000000..fcb560ccd --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpEntityEnclosingRequest.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * Basic implementation of {@link HttpEntityEnclosingRequest}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicHttpEntityEnclosingRequest + extends BasicHttpRequest implements HttpEntityEnclosingRequest { + + private HttpEntity entity; + + public BasicHttpEntityEnclosingRequest(final String method, final String uri) { + super(method, uri); + } + + public BasicHttpEntityEnclosingRequest(final String method, final String uri, + final ProtocolVersion ver) { + super(method, uri, ver); + } + + public BasicHttpEntityEnclosingRequest(final RequestLine requestline) { + super(requestline); + } + + public HttpEntity getEntity() { + return this.entity; + } + + public void setEntity(final HttpEntity entity) { + this.entity = entity; + } + + public boolean expectContinue() { + final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE); + return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue()); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpRequest.java new file mode 100644 index 000000000..be795a08e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpRequest.java @@ -0,0 +1,114 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of {@link HttpRequest}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicHttpRequest extends AbstractHttpMessage implements HttpRequest { + + private final String method; + private final String uri; + + private RequestLine requestline; + + /** + * Creates an instance of this class using the given request method + * and URI. + * + * @param method request method. + * @param uri request URI. + */ + public BasicHttpRequest(final String method, final String uri) { + super(); + this.method = Args.notNull(method, "Method name"); + this.uri = Args.notNull(uri, "Request URI"); + this.requestline = null; + } + + /** + * Creates an instance of this class using the given request method, URI + * and the HTTP protocol version. + * + * @param method request method. + * @param uri request URI. + * @param ver HTTP protocol version. + */ + public BasicHttpRequest(final String method, final String uri, final ProtocolVersion ver) { + this(new BasicRequestLine(method, uri, ver)); + } + + /** + * Creates an instance of this class using the given request line. + * + * @param requestline request line. + */ + public BasicHttpRequest(final RequestLine requestline) { + super(); + this.requestline = Args.notNull(requestline, "Request line"); + this.method = requestline.getMethod(); + this.uri = requestline.getUri(); + } + + /** + * Returns the HTTP protocol version to be used for this request. + * + * @see #BasicHttpRequest(String, String) + */ + public ProtocolVersion getProtocolVersion() { + return getRequestLine().getProtocolVersion(); + } + + /** + * Returns the request line of this request. + * + * @see #BasicHttpRequest(String, String) + */ + public RequestLine getRequestLine() { + if (this.requestline == null) { + this.requestline = new BasicRequestLine(this.method, this.uri, HttpVersion.HTTP_1_1); + } + return this.requestline; + } + + @Override + public String toString() { + return this.method + " " + this.uri + " " + this.headergroup; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpResponse.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpResponse.java new file mode 100644 index 000000000..f16b1ca59 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicHttpResponse.java @@ -0,0 +1,218 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.util.Locale; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.ReasonPhraseCatalog; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of {@link HttpResponse}. + * + * @see ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicHttpResponse extends AbstractHttpMessage implements HttpResponse { + + private StatusLine statusline; + private ProtocolVersion ver; + private int code; + private String reasonPhrase; + private HttpEntity entity; + private final ReasonPhraseCatalog reasonCatalog; + private Locale locale; + + /** + * Creates a new response. + * This is the constructor to which all others map. + * + * @param statusline the status line + * @param catalog the reason phrase catalog, or + * null to disable automatic + * reason phrase lookup + * @param locale the locale for looking up reason phrases, or + * null for the system locale + */ + public BasicHttpResponse(final StatusLine statusline, + final ReasonPhraseCatalog catalog, + final Locale locale) { + super(); + this.statusline = Args.notNull(statusline, "Status line"); + this.ver = statusline.getProtocolVersion(); + this.code = statusline.getStatusCode(); + this.reasonPhrase = statusline.getReasonPhrase(); + this.reasonCatalog = catalog; + this.locale = locale; + } + + /** + * Creates a response from a status line. + * The response will not have a reason phrase catalog and + * use the system default locale. + * + * @param statusline the status line + */ + public BasicHttpResponse(final StatusLine statusline) { + super(); + this.statusline = Args.notNull(statusline, "Status line"); + this.ver = statusline.getProtocolVersion(); + this.code = statusline.getStatusCode(); + this.reasonPhrase = statusline.getReasonPhrase(); + this.reasonCatalog = null; + this.locale = null; + } + + /** + * Creates a response from elements of a status line. + * The response will not have a reason phrase catalog and + * use the system default locale. + * + * @param ver the protocol version of the response + * @param code the status code of the response + * @param reason the reason phrase to the status code, or + * null + */ + public BasicHttpResponse(final ProtocolVersion ver, + final int code, + final String reason) { + super(); + Args.notNegative(code, "Status code"); + this.statusline = null; + this.ver = ver; + this.code = code; + this.reasonPhrase = reason; + this.reasonCatalog = null; + this.locale = null; + } + + + // non-javadoc, see interface HttpMessage + public ProtocolVersion getProtocolVersion() { + return this.ver; + } + + // non-javadoc, see interface HttpResponse + public StatusLine getStatusLine() { + if (this.statusline == null) { + this.statusline = new BasicStatusLine( + this.ver != null ? this.ver : HttpVersion.HTTP_1_1, + this.code, + this.reasonPhrase != null ? this.reasonPhrase : getReason(this.code)); + } + return this.statusline; + } + + // non-javadoc, see interface HttpResponse + public HttpEntity getEntity() { + return this.entity; + } + + public Locale getLocale() { + return this.locale; + } + + // non-javadoc, see interface HttpResponse + public void setStatusLine(final StatusLine statusline) { + this.statusline = Args.notNull(statusline, "Status line"); + this.ver = statusline.getProtocolVersion(); + this.code = statusline.getStatusCode(); + this.reasonPhrase = statusline.getReasonPhrase(); + } + + // non-javadoc, see interface HttpResponse + public void setStatusLine(final ProtocolVersion ver, final int code) { + Args.notNegative(code, "Status code"); + this.statusline = null; + this.ver = ver; + this.code = code; + this.reasonPhrase = null; + } + + // non-javadoc, see interface HttpResponse + public void setStatusLine( + final ProtocolVersion ver, final int code, final String reason) { + Args.notNegative(code, "Status code"); + this.statusline = null; + this.ver = ver; + this.code = code; + this.reasonPhrase = reason; + } + + // non-javadoc, see interface HttpResponse + public void setStatusCode(final int code) { + Args.notNegative(code, "Status code"); + this.statusline = null; + this.code = code; + this.reasonPhrase = null; + } + + // non-javadoc, see interface HttpResponse + public void setReasonPhrase(final String reason) { + this.statusline = null; + this.reasonPhrase = reason; + } + + // non-javadoc, see interface HttpResponse + public void setEntity(final HttpEntity entity) { + this.entity = entity; + } + + public void setLocale(final Locale locale) { + this.locale = Args.notNull(locale, "Locale"); + this.statusline = null; + } + + /** + * Looks up a reason phrase. + * This method evaluates the currently set catalog and locale. + * It also handles a missing catalog. + * + * @param code the status code for which to look up the reason + * + * @return the reason phrase, or null if there is none + */ + protected String getReason(final int code) { + return this.reasonCatalog != null ? this.reasonCatalog.getReason(code, + this.locale != null ? this.locale : Locale.getDefault()) : null; + } + + @Override + public String toString() { + return getStatusLine() + " " + this.headergroup; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineFormatter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineFormatter.java new file mode 100644 index 000000000..6eabfd48f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineFormatter.java @@ -0,0 +1,319 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Interface for formatting elements of the HEAD section of an HTTP message. + * This is the complement to {@link LineParser}. + * There are individual methods for formatting a request line, a + * status line, or a header line. The formatting does not include the + * trailing line break sequence CR-LF. + * The formatted lines are returned in memory, the formatter does not depend + * on any specific IO mechanism. + * Instances of this interface are expected to be stateless and thread-safe. + * + * @since 4.0 + */ +@Immutable +public class BasicLineFormatter implements LineFormatter { + + /** + * A default instance of this class, for use as default or fallback. + * Note that {@link BasicLineFormatter} is not a singleton, there can + * be many instances of the class itself and of derived classes. + * The instance here provides non-customized, default behavior. + * + * @deprecated (4.3) use {@link #INSTANCE} + */ + @Deprecated + public final static BasicLineFormatter DEFAULT = new BasicLineFormatter(); + + public final static BasicLineFormatter INSTANCE = new BasicLineFormatter(); + + public BasicLineFormatter() { + super(); + } + + /** + * Obtains a buffer for formatting. + * + * @param charBuffer a buffer already available, or null + * + * @return the cleared argument buffer if there is one, or + * a new empty buffer that can be used for formatting + */ + protected CharArrayBuffer initBuffer(final CharArrayBuffer charBuffer) { + CharArrayBuffer buffer = charBuffer; + if (buffer != null) { + buffer.clear(); + } else { + buffer = new CharArrayBuffer(64); + } + return buffer; + } + + + /** + * Formats a protocol version. + * + * @param version the protocol version to format + * @param formatter the formatter to use, or + * null for the + * {@link #INSTANCE default} + * + * @return the formatted protocol version + */ + public static + String formatProtocolVersion(final ProtocolVersion version, + final LineFormatter formatter) { + return (formatter != null ? formatter : BasicLineFormatter.INSTANCE) + .appendProtocolVersion(null, version).toString(); + } + + + // non-javadoc, see interface LineFormatter + public CharArrayBuffer appendProtocolVersion(final CharArrayBuffer buffer, + final ProtocolVersion version) { + Args.notNull(version, "Protocol version"); + // can't use initBuffer, that would clear the argument! + CharArrayBuffer result = buffer; + final int len = estimateProtocolVersionLen(version); + if (result == null) { + result = new CharArrayBuffer(len); + } else { + result.ensureCapacity(len); + } + + result.append(version.getProtocol()); + result.append('/'); + result.append(Integer.toString(version.getMajor())); + result.append('.'); + result.append(Integer.toString(version.getMinor())); + + return result; + } + + + /** + * Guesses the length of a formatted protocol version. + * Needed to guess the length of a formatted request or status line. + * + * @param version the protocol version to format, or null + * + * @return the estimated length of the formatted protocol version, + * in characters + */ + protected int estimateProtocolVersionLen(final ProtocolVersion version) { + return version.getProtocol().length() + 4; // room for "HTTP/1.1" + } + + + /** + * Formats a request line. + * + * @param reqline the request line to format + * @param formatter the formatter to use, or + * null for the + * {@link #INSTANCE default} + * + * @return the formatted request line + */ + public static String formatRequestLine(final RequestLine reqline, + final LineFormatter formatter) { + return (formatter != null ? formatter : BasicLineFormatter.INSTANCE) + .formatRequestLine(null, reqline).toString(); + } + + + // non-javadoc, see interface LineFormatter + public CharArrayBuffer formatRequestLine(final CharArrayBuffer buffer, + final RequestLine reqline) { + Args.notNull(reqline, "Request line"); + final CharArrayBuffer result = initBuffer(buffer); + doFormatRequestLine(result, reqline); + + return result; + } + + + /** + * Actually formats a request line. + * Called from {@link #formatRequestLine}. + * + * @param buffer the empty buffer into which to format, + * never null + * @param reqline the request line to format, never null + */ + protected void doFormatRequestLine(final CharArrayBuffer buffer, + final RequestLine reqline) { + final String method = reqline.getMethod(); + final String uri = reqline.getUri(); + + // room for "GET /index.html HTTP/1.1" + final int len = method.length() + 1 + uri.length() + 1 + + estimateProtocolVersionLen(reqline.getProtocolVersion()); + buffer.ensureCapacity(len); + + buffer.append(method); + buffer.append(' '); + buffer.append(uri); + buffer.append(' '); + appendProtocolVersion(buffer, reqline.getProtocolVersion()); + } + + + + /** + * Formats a status line. + * + * @param statline the status line to format + * @param formatter the formatter to use, or + * null for the + * {@link #INSTANCE default} + * + * @return the formatted status line + */ + public static String formatStatusLine(final StatusLine statline, + final LineFormatter formatter) { + return (formatter != null ? formatter : BasicLineFormatter.INSTANCE) + .formatStatusLine(null, statline).toString(); + } + + + // non-javadoc, see interface LineFormatter + public CharArrayBuffer formatStatusLine(final CharArrayBuffer buffer, + final StatusLine statline) { + Args.notNull(statline, "Status line"); + final CharArrayBuffer result = initBuffer(buffer); + doFormatStatusLine(result, statline); + + return result; + } + + + /** + * Actually formats a status line. + * Called from {@link #formatStatusLine}. + * + * @param buffer the empty buffer into which to format, + * never null + * @param statline the status line to format, never null + */ + protected void doFormatStatusLine(final CharArrayBuffer buffer, + final StatusLine statline) { + + int len = estimateProtocolVersionLen(statline.getProtocolVersion()) + + 1 + 3 + 1; // room for "HTTP/1.1 200 " + final String reason = statline.getReasonPhrase(); + if (reason != null) { + len += reason.length(); + } + buffer.ensureCapacity(len); + + appendProtocolVersion(buffer, statline.getProtocolVersion()); + buffer.append(' '); + buffer.append(Integer.toString(statline.getStatusCode())); + buffer.append(' '); // keep whitespace even if reason phrase is empty + if (reason != null) { + buffer.append(reason); + } + } + + + /** + * Formats a header. + * + * @param header the header to format + * @param formatter the formatter to use, or + * null for the + * {@link #INSTANCE default} + * + * @return the formatted header + */ + public static String formatHeader(final Header header, + final LineFormatter formatter) { + return (formatter != null ? formatter : BasicLineFormatter.INSTANCE) + .formatHeader(null, header).toString(); + } + + + // non-javadoc, see interface LineFormatter + public CharArrayBuffer formatHeader(final CharArrayBuffer buffer, + final Header header) { + Args.notNull(header, "Header"); + final CharArrayBuffer result; + + if (header instanceof FormattedHeader) { + // If the header is backed by a buffer, re-use the buffer + result = ((FormattedHeader)header).getBuffer(); + } else { + result = initBuffer(buffer); + doFormatHeader(result, header); + } + return result; + + } // formatHeader + + + /** + * Actually formats a header. + * Called from {@link #formatHeader}. + * + * @param buffer the empty buffer into which to format, + * never null + * @param header the header to format, never null + */ + protected void doFormatHeader(final CharArrayBuffer buffer, + final Header header) { + final String name = header.getName(); + final String value = header.getValue(); + + int len = name.length() + 2; + if (value != null) { + len += value.length(); + } + buffer.ensureCapacity(len); + + buffer.append(name); + buffer.append(": "); + if (value != null) { + buffer.append(value); + } + } + + +} // class BasicLineFormatter diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineParser.java new file mode 100644 index 000000000..dabde0f62 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineParser.java @@ -0,0 +1,456 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Basic parser for lines in the head section of an HTTP message. + * There are individual methods for parsing a request line, a + * status line, or a header line. + * The lines to parse are passed in memory, the parser does not depend + * on any specific IO mechanism. + * Instances of this class are stateless and thread-safe. + * Derived classes MUST maintain these properties. + * + *

    + * Note: This class was created by refactoring parsing code located in + * various other classes. The author tags from those other classes have + * been replicated here, although the association with the parsing code + * taken from there has not been traced. + *

    + * + * @since 4.0 + */ +@Immutable +public class BasicLineParser implements LineParser { + + /** + * A default instance of this class, for use as default or fallback. + * Note that {@link BasicLineParser} is not a singleton, there can + * be many instances of the class itself and of derived classes. + * The instance here provides non-customized, default behavior. + * + * @deprecated (4.3) use {@link #INSTANCE} + */ + @Deprecated + public final static BasicLineParser DEFAULT = new BasicLineParser(); + + public final static BasicLineParser INSTANCE = new BasicLineParser(); + + /** + * A version of the protocol to parse. + * The version is typically not relevant, but the protocol name. + */ + protected final ProtocolVersion protocol; + + + /** + * Creates a new line parser for the given HTTP-like protocol. + * + * @param proto a version of the protocol to parse, or + * null for HTTP. The actual version + * is not relevant, only the protocol name. + */ + public BasicLineParser(final ProtocolVersion proto) { + this.protocol = proto != null? proto : HttpVersion.HTTP_1_1; + } + + + /** + * Creates a new line parser for HTTP. + */ + public BasicLineParser() { + this(null); + } + + + public static + ProtocolVersion parseProtocolVersion(final String value, + final LineParser parser) throws ParseException { + Args.notNull(value, "Value"); + + final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); + buffer.append(value); + final ParserCursor cursor = new ParserCursor(0, value.length()); + return (parser != null ? parser : BasicLineParser.INSTANCE) + .parseProtocolVersion(buffer, cursor); + } + + + // non-javadoc, see interface LineParser + public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer, + final ParserCursor cursor) throws ParseException { + Args.notNull(buffer, "Char array buffer"); + Args.notNull(cursor, "Parser cursor"); + final String protoname = this.protocol.getProtocol(); + final int protolength = protoname.length(); + + final int indexFrom = cursor.getPos(); + final int indexTo = cursor.getUpperBound(); + + skipWhitespace(buffer, cursor); + + int i = cursor.getPos(); + + // long enough for "HTTP/1.1"? + if (i + protolength + 4 > indexTo) { + throw new ParseException + ("Not a valid protocol version: " + + buffer.substring(indexFrom, indexTo)); + } + + // check the protocol name and slash + boolean ok = true; + for (int j=0; ok && (j buffer.length()) { + return false; + } + + + // just check protocol name and slash, no need to analyse the version + boolean ok = true; + for (int j=0; ok && (j. + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.util.List; +import java.util.NoSuchElementException; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Implementation of a {@link HeaderIterator} based on a {@link List}. + * For use by {@link HeaderGroup}. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicListHeaderIterator implements HeaderIterator { + + /** + * A list of headers to iterate over. + * Not all elements of this array are necessarily part of the iteration. + */ + protected final List
    allHeaders; + + + /** + * The position of the next header in {@link #allHeaders allHeaders}. + * Negative if the iteration is over. + */ + protected int currentIndex; + + + /** + * The position of the last returned header. + * Negative if none has been returned so far. + */ + protected int lastIndex; + + + /** + * The header name to filter by. + * null to iterate over all headers in the array. + */ + protected String headerName; + + + + /** + * Creates a new header iterator. + * + * @param headers a list of headers over which to iterate + * @param name the name of the headers over which to iterate, or + * null for any + */ + public BasicListHeaderIterator(final List
    headers, final String name) { + super(); + this.allHeaders = Args.notNull(headers, "Header list"); + this.headerName = name; + this.currentIndex = findNext(-1); + this.lastIndex = -1; + } + + + /** + * Determines the index of the next header. + * + * @param pos one less than the index to consider first, + * -1 to search for the first header + * + * @return the index of the next header that matches the filter name, + * or negative if there are no more headers + */ + protected int findNext(final int pos) { + int from = pos; + if (from < -1) { + return -1; + } + + final int to = this.allHeaders.size()-1; + boolean found = false; + while (!found && (from < to)) { + from++; + found = filterHeader(from); + } + return found ? from : -1; + } + + + /** + * Checks whether a header is part of the iteration. + * + * @param index the index of the header to check + * + * @return true if the header should be part of the + * iteration, false to skip + */ + protected boolean filterHeader(final int index) { + if (this.headerName == null) { + return true; + } + + // non-header elements, including null, will trigger exceptions + final String name = (this.allHeaders.get(index)).getName(); + + return this.headerName.equalsIgnoreCase(name); + } + + + // non-javadoc, see interface HeaderIterator + public boolean hasNext() { + return (this.currentIndex >= 0); + } + + + /** + * Obtains the next header from this iteration. + * + * @return the next header in this iteration + * + * @throws NoSuchElementException if there are no more headers + */ + public Header nextHeader() + throws NoSuchElementException { + + final int current = this.currentIndex; + if (current < 0) { + throw new NoSuchElementException("Iteration already finished."); + } + + this.lastIndex = current; + this.currentIndex = findNext(current); + + return this.allHeaders.get(current); + } + + + /** + * Returns the next header. + * Same as {@link #nextHeader nextHeader}, but not type-safe. + * + * @return the next header in this iteration + * + * @throws NoSuchElementException if there are no more headers + */ + public final Object next() + throws NoSuchElementException { + return nextHeader(); + } + + + /** + * Removes the header that was returned last. + */ + public void remove() + throws UnsupportedOperationException { + Asserts.check(this.lastIndex >= 0, "No header to remove"); + this.allHeaders.remove(this.lastIndex); + this.lastIndex = -1; + this.currentIndex--; // adjust for the removed element + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicNameValuePair.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicNameValuePair.java new file mode 100644 index 000000000..530e5042e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicNameValuePair.java @@ -0,0 +1,111 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * Basic implementation of {@link NameValuePair}. + * + * @since 4.0 + */ +@Immutable +public class BasicNameValuePair implements NameValuePair, Cloneable, Serializable { + + private static final long serialVersionUID = -6437800749411518984L; + + private final String name; + private final String value; + + /** + * Default Constructor taking a name and a value. The value may be null. + * + * @param name The name. + * @param value The value. + */ + public BasicNameValuePair(final String name, final String value) { + super(); + this.name = Args.notNull(name, "Name"); + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + @Override + public String toString() { + // don't call complex default formatting for a simple toString + + if (this.value == null) { + return name; + } + final int len = this.name.length() + 1 + this.value.length(); + final StringBuilder buffer = new StringBuilder(len); + buffer.append(this.name); + buffer.append("="); + buffer.append(this.value); + return buffer.toString(); + } + + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (object instanceof NameValuePair) { + final BasicNameValuePair that = (BasicNameValuePair) object; + return this.name.equals(that.name) + && LangUtils.equals(this.value, that.value); + } + return false; + } + + @Override + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, this.name); + hash = LangUtils.hashCode(hash, this.value); + return hash; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicRequestLine.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicRequestLine.java new file mode 100644 index 000000000..40c838b12 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicRequestLine.java @@ -0,0 +1,83 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of {@link RequestLine}. + * + * @since 4.0 + */ +@Immutable +public class BasicRequestLine implements RequestLine, Cloneable, Serializable { + + private static final long serialVersionUID = 2810581718468737193L; + + private final ProtocolVersion protoversion; + private final String method; + private final String uri; + + public BasicRequestLine(final String method, + final String uri, + final ProtocolVersion version) { + super(); + this.method = Args.notNull(method, "Method"); + this.uri = Args.notNull(uri, "URI"); + this.protoversion = Args.notNull(version, "Version"); + } + + public String getMethod() { + return this.method; + } + + public ProtocolVersion getProtocolVersion() { + return this.protoversion; + } + + public String getUri() { + return this.uri; + } + + @Override + public String toString() { + // no need for non-default formatting in toString() + return BasicLineFormatter.INSTANCE.formatRequestLine(null, this).toString(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicStatusLine.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicStatusLine.java new file mode 100644 index 000000000..cce1f25d7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicStatusLine.java @@ -0,0 +1,100 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of {@link StatusLine} + * + * @since 4.0 + */ +@Immutable +public class BasicStatusLine implements StatusLine, Cloneable, Serializable { + + private static final long serialVersionUID = -2443303766890459269L; + + // ----------------------------------------------------- Instance Variables + + /** The protocol version. */ + private final ProtocolVersion protoVersion; + + /** The status code. */ + private final int statusCode; + + /** The reason phrase. */ + private final String reasonPhrase; + + // ----------------------------------------------------------- Constructors + /** + * Creates a new status line with the given version, status, and reason. + * + * @param version the protocol version of the response + * @param statusCode the status code of the response + * @param reasonPhrase the reason phrase to the status code, or + * null + */ + public BasicStatusLine(final ProtocolVersion version, final int statusCode, + final String reasonPhrase) { + super(); + this.protoVersion = Args.notNull(version, "Version"); + this.statusCode = Args.notNegative(statusCode, "Status code"); + this.reasonPhrase = reasonPhrase; + } + + // --------------------------------------------------------- Public Methods + + public int getStatusCode() { + return this.statusCode; + } + + public ProtocolVersion getProtocolVersion() { + return this.protoVersion; + } + + public String getReasonPhrase() { + return this.reasonPhrase; + } + + @Override + public String toString() { + // no need for non-default formatting in toString() + return BasicLineFormatter.INSTANCE.formatStatusLine(null, this).toString(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicTokenIterator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicTokenIterator.java new file mode 100644 index 000000000..98bd841b5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicTokenIterator.java @@ -0,0 +1,414 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.util.NoSuchElementException; + +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.TokenIterator; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of a {@link TokenIterator}. + * This implementation parses #token sequences as + * defined by RFC 2616, section 2. + * It extends that definition somewhat beyond US-ASCII. + * + * @since 4.0 + */ +@NotThreadSafe +public class BasicTokenIterator implements TokenIterator { + + /** The HTTP separator characters. Defined in RFC 2616, section 2.2. */ + // the order of the characters here is adjusted to put the + // most likely candidates at the beginning of the collection + public final static String HTTP_SEPARATORS = " ,;=()<>@:\\\"/[]?{}\t"; + + + /** The iterator from which to obtain the next header. */ + protected final HeaderIterator headerIt; + + /** + * The value of the current header. + * This is the header value that includes {@link #currentToken}. + * Undefined if the iteration is over. + */ + protected String currentHeader; + + /** + * The token to be returned by the next call to {@link #nextToken()}. + * null if the iteration is over. + */ + protected String currentToken; + + /** + * The position after {@link #currentToken} in {@link #currentHeader}. + * Undefined if the iteration is over. + */ + protected int searchPos; + + + /** + * Creates a new instance of {@link BasicTokenIterator}. + * + * @param headerIterator the iterator for the headers to tokenize + */ + public BasicTokenIterator(final HeaderIterator headerIterator) { + super(); + this.headerIt = Args.notNull(headerIterator, "Header iterator"); + this.searchPos = findNext(-1); + } + + + // non-javadoc, see interface TokenIterator + public boolean hasNext() { + return (this.currentToken != null); + } + + + /** + * Obtains the next token from this iteration. + * + * @return the next token in this iteration + * + * @throws NoSuchElementException if the iteration is already over + * @throws ParseException if an invalid header value is encountered + */ + public String nextToken() + throws NoSuchElementException, ParseException { + + if (this.currentToken == null) { + throw new NoSuchElementException("Iteration already finished."); + } + + final String result = this.currentToken; + // updates currentToken, may trigger ParseException: + this.searchPos = findNext(this.searchPos); + + return result; + } + + + /** + * Returns the next token. + * Same as {@link #nextToken}, but with generic return type. + * + * @return the next token in this iteration + * + * @throws NoSuchElementException if there are no more tokens + * @throws ParseException if an invalid header value is encountered + */ + public final Object next() + throws NoSuchElementException, ParseException { + return nextToken(); + } + + + /** + * Removing tokens is not supported. + * + * @throws UnsupportedOperationException always + */ + public final void remove() + throws UnsupportedOperationException { + + throw new UnsupportedOperationException + ("Removing tokens is not supported."); + } + + + /** + * Determines the next token. + * If found, the token is stored in {@link #currentToken}. + * The return value indicates the position after the token + * in {@link #currentHeader}. If necessary, the next header + * will be obtained from {@link #headerIt}. + * If not found, {@link #currentToken} is set to null. + * + * @param pos the position in the current header at which to + * start the search, -1 to search in the first header + * + * @return the position after the found token in the current header, or + * negative if there was no next token + * + * @throws ParseException if an invalid header value is encountered + */ + protected int findNext(final int pos) throws ParseException { + int from = pos; + if (from < 0) { + // called from the constructor, initialize the first header + if (!this.headerIt.hasNext()) { + return -1; + } + this.currentHeader = this.headerIt.nextHeader().getValue(); + from = 0; + } else { + // called after a token, make sure there is a separator + from = findTokenSeparator(from); + } + + final int start = findTokenStart(from); + if (start < 0) { + this.currentToken = null; + return -1; // nothing found + } + + final int end = findTokenEnd(start); + this.currentToken = createToken(this.currentHeader, start, end); + return end; + } + + + /** + * Creates a new token to be returned. + * Called from {@link #findNext findNext} after the token is identified. + * The default implementation simply calls + * {@link java.lang.String#substring String.substring}. + *
    + * If header values are significantly longer than tokens, and some + * tokens are permanently referenced by the application, there can + * be problems with garbage collection. A substring will hold a + * reference to the full characters of the original string and + * therefore occupies more memory than might be expected. + * To avoid this, override this method and create a new string + * instead of a substring. + * + * @param value the full header value from which to create a token + * @param start the index of the first token character + * @param end the index after the last token character + * + * @return a string representing the token identified by the arguments + */ + protected String createToken(final String value, final int start, final int end) { + return value.substring(start, end); + } + + + /** + * Determines the starting position of the next token. + * This method will iterate over headers if necessary. + * + * @param pos the position in the current header at which to + * start the search + * + * @return the position of the token start in the current header, + * negative if no token start could be found + */ + protected int findTokenStart(final int pos) { + int from = Args.notNegative(pos, "Search position"); + boolean found = false; + while (!found && (this.currentHeader != null)) { + + final int to = this.currentHeader.length(); + while (!found && (from < to)) { + + final char ch = this.currentHeader.charAt(from); + if (isTokenSeparator(ch) || isWhitespace(ch)) { + // whitspace and token separators are skipped + from++; + } else if (isTokenChar(this.currentHeader.charAt(from))) { + // found the start of a token + found = true; + } else { + throw new ParseException + ("Invalid character before token (pos " + from + + "): " + this.currentHeader); + } + } + if (!found) { + if (this.headerIt.hasNext()) { + this.currentHeader = this.headerIt.nextHeader().getValue(); + from = 0; + } else { + this.currentHeader = null; + } + } + } // while headers + + return found ? from : -1; + } + + + /** + * Determines the position of the next token separator. + * Because of multi-header joining rules, the end of a + * header value is a token separator. This method does + * therefore not need to iterate over headers. + * + * @param pos the position in the current header at which to + * start the search + * + * @return the position of a token separator in the current header, + * or at the end + * + * @throws ParseException + * if a new token is found before a token separator. + * RFC 2616, section 2.1 explicitly requires a comma between + * tokens for #. + */ + protected int findTokenSeparator(final int pos) { + int from = Args.notNegative(pos, "Search position"); + boolean found = false; + final int to = this.currentHeader.length(); + while (!found && (from < to)) { + final char ch = this.currentHeader.charAt(from); + if (isTokenSeparator(ch)) { + found = true; + } else if (isWhitespace(ch)) { + from++; + } else if (isTokenChar(ch)) { + throw new ParseException + ("Tokens without separator (pos " + from + + "): " + this.currentHeader); + } else { + throw new ParseException + ("Invalid character after token (pos " + from + + "): " + this.currentHeader); + } + } + + return from; + } + + + /** + * Determines the ending position of the current token. + * This method will not leave the current header value, + * since the end of the header value is a token boundary. + * + * @param from the position of the first character of the token + * + * @return the position after the last character of the token. + * The behavior is undefined if from does not + * point to a token character in the current header value. + */ + protected int findTokenEnd(final int from) { + Args.notNegative(from, "Search position"); + final int to = this.currentHeader.length(); + int end = from+1; + while ((end < to) && isTokenChar(this.currentHeader.charAt(end))) { + end++; + } + + return end; + } + + + /** + * Checks whether a character is a token separator. + * RFC 2616, section 2.1 defines comma as the separator for + * #token sequences. The end of a header value will + * also separate tokens, but that is not a character check. + * + * @param ch the character to check + * + * @return true if the character is a token separator, + * false otherwise + */ + protected boolean isTokenSeparator(final char ch) { + return (ch == ','); + } + + + /** + * Checks whether a character is a whitespace character. + * RFC 2616, section 2.2 defines space and horizontal tab as whitespace. + * The optional preceeding line break is irrelevant, since header + * continuation is handled transparently when parsing messages. + * + * @param ch the character to check + * + * @return true if the character is whitespace, + * false otherwise + */ + protected boolean isWhitespace(final char ch) { + + // we do not use Character.isWhitspace(ch) here, since that allows + // many control characters which are not whitespace as per RFC 2616 + return ((ch == '\t') || Character.isSpaceChar(ch)); + } + + + /** + * Checks whether a character is a valid token character. + * Whitespace, control characters, and HTTP separators are not + * valid token characters. The HTTP specification (RFC 2616, section 2.2) + * defines tokens only for the US-ASCII character set, this + * method extends the definition to other character sets. + * + * @param ch the character to check + * + * @return true if the character is a valid token start, + * false otherwise + */ + protected boolean isTokenChar(final char ch) { + + // common sense extension of ALPHA + DIGIT + if (Character.isLetterOrDigit(ch)) { + return true; + } + + // common sense extension of CTL + if (Character.isISOControl(ch)) { + return false; + } + + // no common sense extension for this + if (isHttpSeparator(ch)) { + return false; + } + + // RFC 2616, section 2.2 defines a token character as + // "any CHAR except CTLs or separators". The controls + // and separators are included in the checks above. + // This will yield unexpected results for Unicode format characters. + // If that is a problem, overwrite isHttpSeparator(char) to filter + // out the false positives. + return true; + } + + + /** + * Checks whether a character is an HTTP separator. + * The implementation in this class checks only for the HTTP separators + * defined in RFC 2616, section 2.2. If you need to detect other + * separators beyond the US-ASCII character set, override this method. + * + * @param ch the character to check + * + * @return true if the character is an HTTP separator + */ + protected boolean isHttpSeparator(final char ch) { + return (HTTP_SEPARATORS.indexOf(ch) >= 0); + } + + +} // class BasicTokenIterator + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BufferedHeader.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BufferedHeader.java new file mode 100644 index 000000000..11b70bdcb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BufferedHeader.java @@ -0,0 +1,130 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.FormattedHeader; +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * This class represents a raw HTTP header whose content is parsed 'on demand' + * only when the header value needs to be consumed. + * + * @since 4.0 + */ +@NotThreadSafe +public class BufferedHeader implements FormattedHeader, Cloneable, Serializable { + + private static final long serialVersionUID = -2768352615787625448L; + + /** + * Header name. + */ + private final String name; + + /** + * The buffer containing the entire header line. + */ + private final CharArrayBuffer buffer; + + /** + * The beginning of the header value in the buffer + */ + private final int valuePos; + + + /** + * Creates a new header from a buffer. + * The name of the header will be parsed immediately, + * the value only if it is accessed. + * + * @param buffer the buffer containing the header to represent + * + * @throws ParseException in case of a parse error + */ + public BufferedHeader(final CharArrayBuffer buffer) + throws ParseException { + + super(); + Args.notNull(buffer, "Char array buffer"); + final int colon = buffer.indexOf(':'); + if (colon == -1) { + throw new ParseException + ("Invalid header: " + buffer.toString()); + } + final String s = buffer.substringTrimmed(0, colon); + if (s.length() == 0) { + throw new ParseException + ("Invalid header: " + buffer.toString()); + } + this.buffer = buffer; + this.name = s; + this.valuePos = colon + 1; + } + + + public String getName() { + return this.name; + } + + public String getValue() { + return this.buffer.substringTrimmed(this.valuePos, this.buffer.length()); + } + + public HeaderElement[] getElements() throws ParseException { + final ParserCursor cursor = new ParserCursor(0, this.buffer.length()); + cursor.updatePos(this.valuePos); + return BasicHeaderValueParser.INSTANCE.parseElements(this.buffer, cursor); + } + + public int getValuePos() { + return this.valuePos; + } + + public CharArrayBuffer getBuffer() { + return this.buffer; + } + + @Override + public String toString() { + return this.buffer.toString(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + // buffer is considered immutable + // no need to make a copy of it + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderGroup.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderGroup.java new file mode 100644 index 000000000..fe8cdce95 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderGroup.java @@ -0,0 +1,311 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HeaderIterator; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * A class for combining a set of headers. + * This class allows for multiple headers with the same name and + * keeps track of the order in which headers were added. + * + * + * @since 4.0 + */ +@NotThreadSafe +public class HeaderGroup implements Cloneable, Serializable { + + private static final long serialVersionUID = 2608834160639271617L; + + /** The list of headers for this group, in the order in which they were added */ + private final List
    headers; + + /** + * Constructor for HeaderGroup. + */ + public HeaderGroup() { + this.headers = new ArrayList
    (16); + } + + /** + * Removes any contained headers. + */ + public void clear() { + headers.clear(); + } + + /** + * Adds the given header to the group. The order in which this header was + * added is preserved. + * + * @param header the header to add + */ + public void addHeader(final Header header) { + if (header == null) { + return; + } + headers.add(header); + } + + /** + * Removes the given header. + * + * @param header the header to remove + */ + public void removeHeader(final Header header) { + if (header == null) { + return; + } + headers.remove(header); + } + + /** + * Replaces the first occurence of the header with the same name. If no header with + * the same name is found the given header is added to the end of the list. + * + * @param header the new header that should replace the first header with the same + * name if present in the list. + */ + public void updateHeader(final Header header) { + if (header == null) { + return; + } + // HTTPCORE-361 : we don't use the for-each syntax, i.e. + // for (Header header : headers) + // as that creates an Iterator that needs to be garbage-collected + for (int i = 0; i < this.headers.size(); i++) { + final Header current = this.headers.get(i); + if (current.getName().equalsIgnoreCase(header.getName())) { + this.headers.set(i, header); + return; + } + } + this.headers.add(header); + } + + /** + * Sets all of the headers contained within this group overriding any + * existing headers. The headers are added in the order in which they appear + * in the array. + * + * @param headers the headers to set + */ + public void setHeaders(final Header[] headers) { + clear(); + if (headers == null) { + return; + } + Collections.addAll(this.headers, headers); + } + + /** + * Gets a header representing all of the header values with the given name. + * If more that one header with the given name exists the values will be + * combined with a "," as per RFC 2616. + * + *

    Header name comparison is case insensitive. + * + * @param name the name of the header(s) to get + * @return a header with a condensed value or null if no + * headers by the given name are present + */ + public Header getCondensedHeader(final String name) { + final Header[] hdrs = getHeaders(name); + + if (hdrs.length == 0) { + return null; + } else if (hdrs.length == 1) { + return hdrs[0]; + } else { + final CharArrayBuffer valueBuffer = new CharArrayBuffer(128); + valueBuffer.append(hdrs[0].getValue()); + for (int i = 1; i < hdrs.length; i++) { + valueBuffer.append(", "); + valueBuffer.append(hdrs[i].getValue()); + } + + return new BasicHeader(name.toLowerCase(Locale.ENGLISH), valueBuffer.toString()); + } + } + + /** + * Gets all of the headers with the given name. The returned array + * maintains the relative order in which the headers were added. + * + *

    Header name comparison is case insensitive. + * + * @param name the name of the header(s) to get + * + * @return an array of length >= 0 + */ + public Header[] getHeaders(final String name) { + final List

    headersFound = new ArrayList
    (); + // HTTPCORE-361 : we don't use the for-each syntax, i.e. + // for (Header header : headers) + // as that creates an Iterator that needs to be garbage-collected + for (int i = 0; i < this.headers.size(); i++) { + final Header header = this.headers.get(i); + if (header.getName().equalsIgnoreCase(name)) { + headersFound.add(header); + } + } + + return headersFound.toArray(new Header[headersFound.size()]); + } + + /** + * Gets the first header with the given name. + * + *

    Header name comparison is case insensitive. + * + * @param name the name of the header to get + * @return the first header or null + */ + public Header getFirstHeader(final String name) { + // HTTPCORE-361 : we don't use the for-each syntax, i.e. + // for (Header header : headers) + // as that creates an Iterator that needs to be garbage-collected + for (int i = 0; i < this.headers.size(); i++) { + final Header header = this.headers.get(i); + if (header.getName().equalsIgnoreCase(name)) { + return header; + } + } + return null; + } + + /** + * Gets the last header with the given name. + * + *

    Header name comparison is case insensitive. + * + * @param name the name of the header to get + * @return the last header or null + */ + public Header getLastHeader(final String name) { + // start at the end of the list and work backwards + for (int i = headers.size() - 1; i >= 0; i--) { + final Header header = headers.get(i); + if (header.getName().equalsIgnoreCase(name)) { + return header; + } + } + + return null; + } + + /** + * Gets all of the headers contained within this group. + * + * @return an array of length >= 0 + */ + public Header[] getAllHeaders() { + return headers.toArray(new Header[headers.size()]); + } + + /** + * Tests if headers with the given name are contained within this group. + * + *

    Header name comparison is case insensitive. + * + * @param name the header name to test for + * @return true if at least one header with the name is + * contained, false otherwise + */ + public boolean containsHeader(final String name) { + // HTTPCORE-361 : we don't use the for-each syntax, i.e. + // for (Header header : headers) + // as that creates an Iterator that needs to be garbage-collected + for (int i = 0; i < this.headers.size(); i++) { + final Header header = this.headers.get(i); + if (header.getName().equalsIgnoreCase(name)) { + return true; + } + } + + return false; + } + + /** + * Returns an iterator over this group of headers. + * + * @return iterator over this group of headers. + * + * @since 4.0 + */ + public HeaderIterator iterator() { + return new BasicListHeaderIterator(this.headers, null); + } + + /** + * Returns an iterator over the headers with a given name in this group. + * + * @param name the name of the headers over which to iterate, or + * null for all headers + * + * @return iterator over some headers in this group. + * + * @since 4.0 + */ + public HeaderIterator iterator(final String name) { + return new BasicListHeaderIterator(this.headers, name); + } + + /** + * Returns a copy of this object + * + * @return copy of this object + * + * @since 4.0 + */ + public HeaderGroup copy() { + final HeaderGroup clone = new HeaderGroup(); + clone.headers.addAll(this.headers); + return clone; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + @Override + public String toString() { + return this.headers.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueFormatter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueFormatter.java new file mode 100644 index 000000000..8a1acc04e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueFormatter.java @@ -0,0 +1,122 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Interface for formatting elements of a header value. + * This is the complement to {@link HeaderValueParser}. + * Instances of this interface are expected to be stateless and thread-safe. + * + *

    + * All formatting methods accept an optional buffer argument. + * If a buffer is passed in, the formatted element will be appended + * and the modified buffer is returned. If no buffer is passed in, + * a new buffer will be created and filled with the formatted element. + * In both cases, the caller is allowed to modify the returned buffer. + *

    + * + * @since 4.0 + */ +public interface HeaderValueFormatter { + + /** + * Formats an array of header elements. + * + * @param buffer the buffer to append to, or + * null to create a new buffer + * @param elems the header elements to format + * @param quote true to always format with quoted values, + * false to use quotes only when necessary + * + * @return a buffer with the formatted header elements. + * If the buffer argument was not null, + * that buffer will be used and returned. + */ + CharArrayBuffer formatElements(CharArrayBuffer buffer, + HeaderElement[] elems, + boolean quote); + + /** + * Formats one header element. + * + * @param buffer the buffer to append to, or + * null to create a new buffer + * @param elem the header element to format + * @param quote true to always format with quoted values, + * false to use quotes only when necessary + * + * @return a buffer with the formatted header element. + * If the buffer argument was not null, + * that buffer will be used and returned. + */ + CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer, + HeaderElement elem, + boolean quote); + + /** + * Formats the parameters of a header element. + * That's a list of name-value pairs, to be separated by semicolons. + * This method will not generate a leading semicolon. + * + * @param buffer the buffer to append to, or + * null to create a new buffer + * @param nvps the parameters (name-value pairs) to format + * @param quote true to always format with quoted values, + * false to use quotes only when necessary + * + * @return a buffer with the formatted parameters. + * If the buffer argument was not null, + * that buffer will be used and returned. + */ + CharArrayBuffer formatParameters(CharArrayBuffer buffer, + NameValuePair[] nvps, + boolean quote); + + /** + * Formats one name-value pair, where the value is optional. + * + * @param buffer the buffer to append to, or + * null to create a new buffer + * @param nvp the name-value pair to format + * @param quote true to always format with a quoted value, + * false to use quotes only when necessary + * + * @return a buffer with the formatted name-value pair. + * If the buffer argument was not null, + * that buffer will be used and returned. + */ + CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer, + NameValuePair nvp, + boolean quote); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueParser.java new file mode 100644 index 000000000..5286249c7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/HeaderValueParser.java @@ -0,0 +1,134 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Interface for parsing header values into elements. + * Instances of this interface are expected to be stateless and thread-safe. + * + * @since 4.0 + */ +public interface HeaderValueParser { + + /** + * Parses a header value into elements. + * Parse errors are indicated as RuntimeException. + *

    + * Some HTTP headers (such as the set-cookie header) have values that + * can be decomposed into multiple elements. In order to be processed + * by this parser, such headers must be in the following form: + *

    + *
    +     * header  = [ element ] *( "," [ element ] )
    +     * element = name [ "=" [ value ] ] *( ";" [ param ] )
    +     * param   = name [ "=" [ value ] ]
    +     *
    +     * name    = token
    +     * value   = ( token | quoted-string )
    +     *
    +     * token         = 1*<any char except "=", ",", ";", <"> and
    +     *                       white space>
    +     * quoted-string = <"> *( text | quoted-char ) <">
    +     * text          = any char except <">
    +     * quoted-char   = "\" char
    +     * 
    + *

    + * Any amount of white space is allowed between any part of the + * header, element or param and is ignored. A missing value in any + * element or param will be stored as the empty {@link String}; + * if the "=" is also missing null will be stored instead. + *

    + * + * @param buffer buffer holding the header value to parse + * @param cursor the parser cursor containing the current position and + * the bounds within the buffer for the parsing operation + * + * @return an array holding all elements of the header value + * + * @throws ParseException in case of a parse error + */ + HeaderElement[] parseElements( + CharArrayBuffer buffer, + ParserCursor cursor) throws ParseException; + + /** + * Parses a single header element. + * A header element consist of a semicolon-separate list + * of name=value definitions. + * + * @param buffer buffer holding the element to parse + * @param cursor the parser cursor containing the current position and + * the bounds within the buffer for the parsing operation + * + * @return the parsed element + * + * @throws ParseException in case of a parse error + */ + HeaderElement parseHeaderElement( + CharArrayBuffer buffer, + ParserCursor cursor) throws ParseException; + + /** + * Parses a list of name-value pairs. + * These lists are used to specify parameters to a header element. + * Parse errors are indicated as ParseException. + * + * @param buffer buffer holding the name-value list to parse + * @param cursor the parser cursor containing the current position and + * the bounds within the buffer for the parsing operation + * + * @return an array holding all items of the name-value list + * + * @throws ParseException in case of a parse error + */ + NameValuePair[] parseParameters( + CharArrayBuffer buffer, + ParserCursor cursor) throws ParseException; + + + /** + * Parses a name=value specification, where the = and value are optional. + * + * @param buffer the buffer holding the name-value pair to parse + * @param cursor the parser cursor containing the current position and + * the bounds within the buffer for the parsing operation + * + * @return the name-value pair, where the value is null + * if no value is specified + */ + NameValuePair parseNameValuePair( + CharArrayBuffer buffer, + ParserCursor cursor) throws ParseException; + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineFormatter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineFormatter.java new file mode 100644 index 000000000..33e751a75 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineFormatter.java @@ -0,0 +1,131 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Interface for formatting elements of the HEAD section of an HTTP message. + * This is the complement to {@link LineParser}. + * There are individual methods for formatting a request line, a + * status line, or a header line. The formatting does not include the + * trailing line break sequence CR-LF. + * Instances of this interface are expected to be stateless and thread-safe. + * + *

    + * The formatted lines are returned in memory, the formatter does not depend + * on any specific IO mechanism. + * In order to avoid unnecessary creation of temporary objects, + * a buffer can be passed as argument to all formatting methods. + * The implementation may or may not actually use that buffer for formatting. + * If it is used, the buffer will first be cleared by the + * formatXXX methods. + * The argument buffer can always be re-used after the call. The buffer + * returned as the result, if it is different from the argument buffer, + * MUST NOT be modified. + *

    + * + * @since 4.0 + */ +public interface LineFormatter { + + /** + * Formats a protocol version. + * This method does not follow the general contract for + * buffer arguments. + * It does not clear the argument buffer, but appends instead. + * The returned buffer can always be modified by the caller. + * Because of these differing conventions, it is not named + * formatProtocolVersion. + * + * @param buffer a buffer to which to append, or null + * @param version the protocol version to format + * + * @return a buffer with the formatted protocol version appended. + * The caller is allowed to modify the result buffer. + * If the buffer argument is not null, + * the returned buffer is the argument buffer. + */ + CharArrayBuffer appendProtocolVersion(CharArrayBuffer buffer, + ProtocolVersion version); + + /** + * Formats a request line. + * + * @param buffer a buffer available for formatting, or + * null. + * The buffer will be cleared before use. + * @param reqline the request line to format + * + * @return the formatted request line + */ + CharArrayBuffer formatRequestLine(CharArrayBuffer buffer, + RequestLine reqline); + + /** + * Formats a status line. + * + * @param buffer a buffer available for formatting, or + * null. + * The buffer will be cleared before use. + * @param statline the status line to format + * + * @return the formatted status line + * + * @throws ch.boye.httpclientandroidlib.ParseException in case of a parse error + */ + CharArrayBuffer formatStatusLine(CharArrayBuffer buffer, + StatusLine statline); + + /** + * Formats a header. + * Due to header continuation, the result may be multiple lines. + * In order to generate well-formed HTTP, the lines in the result + * must be separated by the HTTP line break sequence CR-LF. + * There is no trailing CR-LF in the result. + *
    + * See the class comment for details about the buffer argument. + * + * @param buffer a buffer available for formatting, or + * null. + * The buffer will be cleared before use. + * @param header the header to format + * + * @return a buffer holding the formatted header, never null. + * The returned buffer may be different from the argument buffer. + * + * @throws ch.boye.httpclientandroidlib.ParseException in case of a parse error + */ + CharArrayBuffer formatHeader(CharArrayBuffer buffer, + Header header); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineParser.java new file mode 100644 index 000000000..d94ca3d75 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/LineParser.java @@ -0,0 +1,137 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Interface for parsing lines in the HEAD section of an HTTP message. + * There are individual methods for parsing a request line, a + * status line, or a header line. + * The lines to parse are passed in memory, the parser does not depend + * on any specific IO mechanism. + * Instances of this interface are expected to be stateless and thread-safe. + * + * @since 4.0 + */ +public interface LineParser { + + /** + * Parses the textual representation of a protocol version. + * This is needed for parsing request lines (last element) + * as well as status lines (first element). + * + * @param buffer a buffer holding the protocol version to parse + * @param cursor the parser cursor containing the current position and + * the bounds within the buffer for the parsing operation + * + * @return the parsed protocol version + * + * @throws ParseException in case of a parse error + */ + ProtocolVersion parseProtocolVersion( + CharArrayBuffer buffer, + ParserCursor cursor) throws ParseException; + + /** + * Checks whether there likely is a protocol version in a line. + * This method implements a heuristic to check for a + * likely protocol version specification. It does not + * guarantee that {@link #parseProtocolVersion} would not + * detect a parse error. + * This can be used to detect garbage lines before a request + * or status line. + * + * @param buffer a buffer holding the line to inspect + * @param cursor the cursor at which to check for a protocol version, or + * negative for "end of line". Whether the check tolerates + * whitespace before or after the protocol version is + * implementation dependent. + * + * @return true if there is a protocol version at the + * argument index (possibly ignoring whitespace), + * false otherwise + */ + boolean hasProtocolVersion( + CharArrayBuffer buffer, + ParserCursor cursor); + + /** + * Parses a request line. + * + * @param buffer a buffer holding the line to parse + * @param cursor the parser cursor containing the current position and + * the bounds within the buffer for the parsing operation + * + * @return the parsed request line + * + * @throws ParseException in case of a parse error + */ + RequestLine parseRequestLine( + CharArrayBuffer buffer, + ParserCursor cursor) throws ParseException; + + /** + * Parses a status line. + * + * @param buffer a buffer holding the line to parse + * @param cursor the parser cursor containing the current position and + * the bounds within the buffer for the parsing operation + * + * @return the parsed status line + * + * @throws ParseException in case of a parse error + */ + StatusLine parseStatusLine( + CharArrayBuffer buffer, + ParserCursor cursor) throws ParseException; + + /** + * Creates a header from a line. + * The full header line is expected here. Header continuation lines + * must be joined by the caller before invoking this method. + * + * @param buffer a buffer holding the full header line. + * This buffer MUST NOT be re-used afterwards, since + * the returned object may reference the contents later. + * + * @return the header in the argument buffer. + * The returned object MAY be a wrapper for the argument buffer. + * The argument buffer MUST NOT be re-used or changed afterwards. + * + * @throws ParseException in case of a parse error + */ + Header parseHeader(CharArrayBuffer buffer) + throws ParseException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/ParserCursor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/ParserCursor.java new file mode 100644 index 000000000..163352bb7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/ParserCursor.java @@ -0,0 +1,100 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.message; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * This class represents a context of a parsing operation: + *
      + *
    • the current position the parsing operation is expected to start at
    • + *
    • the bounds limiting the scope of the parsing operation
    • + *
    + * + * @since 4.0 + */ +@NotThreadSafe +public class ParserCursor { + + private final int lowerBound; + private final int upperBound; + private int pos; + + public ParserCursor(final int lowerBound, final int upperBound) { + super(); + if (lowerBound < 0) { + throw new IndexOutOfBoundsException("Lower bound cannot be negative"); + } + if (lowerBound > upperBound) { + throw new IndexOutOfBoundsException("Lower bound cannot be greater then upper bound"); + } + this.lowerBound = lowerBound; + this.upperBound = upperBound; + this.pos = lowerBound; + } + + public int getLowerBound() { + return this.lowerBound; + } + + public int getUpperBound() { + return this.upperBound; + } + + public int getPos() { + return this.pos; + } + + public void updatePos(final int pos) { + if (pos < this.lowerBound) { + throw new IndexOutOfBoundsException("pos: "+pos+" < lowerBound: "+this.lowerBound); + } + if (pos > this.upperBound) { + throw new IndexOutOfBoundsException("pos: "+pos+" > upperBound: "+this.upperBound); + } + this.pos = pos; + } + + public boolean atEnd() { + return this.pos >= this.upperBound; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append('['); + buffer.append(Integer.toString(this.lowerBound)); + buffer.append('>'); + buffer.append(Integer.toString(this.pos)); + buffer.append('>'); + buffer.append(Integer.toString(this.upperBound)); + buffer.append(']'); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/package-info.java new file mode 100644 index 000000000..bac6c2e32 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Core HTTP message components, message element parser + * and writer APIs and their default implementations. + */ +package ch.boye.httpclientandroidlib.message; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/package-info.java new file mode 100644 index 000000000..3001825af --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/package-info.java @@ -0,0 +1,42 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Core HTTP component APIs and primitives. + *

    + * These deal with the fundamental things required for using the + * HTTP protocol, such as representing a + * {@link ch.boye.httpclientandroidlib.HttpMessage message} including it's + * {@link ch.boye.httpclientandroidlib.Header headers} and optional + * {@link ch.boye.httpclientandroidlib.HttpEntity entity}, and + * {@link ch.boye.httpclientandroidlib.HttpConnection connections} + * over which messages are sent. In order to prepare messages + * before sending or after receiving, there are interceptors for + * {@link ch.boye.httpclientandroidlib.HttpRequestInterceptor requests} and + * {@link ch.boye.httpclientandroidlib.HttpResponseInterceptor responses}. + */ +package ch.boye.httpclientandroidlib; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/AbstractHttpParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/AbstractHttpParams.java new file mode 100644 index 000000000..0d41b8f00 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/AbstractHttpParams.java @@ -0,0 +1,124 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import java.util.Set; + +/** + * Abstract base class for parameter collections. + * Type specific setters and getters are mapped to the abstract, + * generic getters and setters. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public abstract class AbstractHttpParams implements HttpParams, HttpParamsNames { + + /** + * Instantiates parameters. + */ + protected AbstractHttpParams() { + super(); + } + + public long getLongParameter(final String name, final long defaultValue) { + final Object param = getParameter(name); + if (param == null) { + return defaultValue; + } + return ((Long) param).longValue(); + } + + public HttpParams setLongParameter(final String name, final long value) { + setParameter(name, Long.valueOf(value)); + return this; + } + + public int getIntParameter(final String name, final int defaultValue) { + final Object param = getParameter(name); + if (param == null) { + return defaultValue; + } + return ((Integer) param).intValue(); + } + + public HttpParams setIntParameter(final String name, final int value) { + setParameter(name, Integer.valueOf(value)); + return this; + } + + public double getDoubleParameter(final String name, final double defaultValue) { + final Object param = getParameter(name); + if (param == null) { + return defaultValue; + } + return ((Double) param).doubleValue(); + } + + public HttpParams setDoubleParameter(final String name, final double value) { + setParameter(name, Double.valueOf(value)); + return this; + } + + public boolean getBooleanParameter(final String name, final boolean defaultValue) { + final Object param = getParameter(name); + if (param == null) { + return defaultValue; + } + return ((Boolean) param).booleanValue(); + } + + public HttpParams setBooleanParameter(final String name, final boolean value) { + setParameter(name, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + public boolean isParameterTrue(final String name) { + return getBooleanParameter(name, false); + } + + public boolean isParameterFalse(final String name) { + return !getBooleanParameter(name, false); + } + + /** + * {@inheritDoc} + *

    + * Dummy implementation - must be overridden by subclasses. + * + * @since 4.2 + * @throws UnsupportedOperationException - always + */ + public Set getNames(){ + throw new UnsupportedOperationException(); + } + +} // class AbstractHttpParams diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/BasicHttpParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/BasicHttpParams.java new file mode 100644 index 000000000..420ac1a08 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/BasicHttpParams.java @@ -0,0 +1,190 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +/** + * Default implementation of {@link HttpParams} interface. + *

    + * Please note access to the internal structures of this class is not + * synchronized and therefore this class may be thread-unsafe. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +@ThreadSafe +public class BasicHttpParams extends AbstractHttpParams implements Serializable, Cloneable { + + private static final long serialVersionUID = -7086398485908701455L; + + /** Map of HTTP parameters that this collection contains. */ + private final Map parameters = new ConcurrentHashMap(); + + public BasicHttpParams() { + super(); + } + + public Object getParameter(final String name) { + return this.parameters.get(name); + } + + public HttpParams setParameter(final String name, final Object value) { + if (name == null) { + return this; + } + if (value != null) { + this.parameters.put(name, value); + } else { + this.parameters.remove(name); + } + return this; + } + + public boolean removeParameter(final String name) { + //this is to avoid the case in which the key has a null value + if (this.parameters.containsKey(name)) { + this.parameters.remove(name); + return true; + } else { + return false; + } + } + + /** + * Assigns the value to all the parameter with the given names + * + * @param names array of parameter names + * @param value parameter value + */ + public void setParameters(final String[] names, final Object value) { + for (final String name : names) { + setParameter(name, value); + } + } + + /** + * Is the parameter set? + *

    + * Uses {@link #getParameter(String)} (which is overrideable) to + * fetch the parameter value, if any. + *

    + * Also @see {@link #isParameterSetLocally(String)} + * + * @param name parameter name + * @return true if parameter is defined and non-null + */ + public boolean isParameterSet(final String name) { + return getParameter(name) != null; + } + + /** + * Is the parameter set in this object? + *

    + * The parameter value is fetched directly. + *

    + * Also @see {@link #isParameterSet(String)} + * + * @param name parameter name + * @return true if parameter is defined and non-null + */ + public boolean isParameterSetLocally(final String name) { + return this.parameters.get(name) != null; + } + + /** + * Removes all parameters from this collection. + */ + public void clear() { + this.parameters.clear(); + } + + /** + * Creates a copy of these parameters. + * This implementation calls {@link #clone()}. + * + * @return a new set of params holding a copy of the + * local parameters in this object. + * + * @throws UnsupportedOperationException if the clone() fails + */ + public HttpParams copy() { + try { + return (HttpParams) clone(); + } catch (final CloneNotSupportedException ex) { + throw new UnsupportedOperationException("Cloning not supported"); + } + } + + /** + * Clones the instance. + * Uses {@link #copyParams(HttpParams)} to copy the parameters. + */ + @Override + public Object clone() throws CloneNotSupportedException { + final BasicHttpParams clone = (BasicHttpParams) super.clone(); + copyParams(clone); + return clone; + } + + /** + * Copies the locally defined parameters to the argument parameters. + * This method is called from {@link #clone()}. + * + * @param target the parameters to which to copy + * @since 4.2 + */ + public void copyParams(final HttpParams target) { + for (final Map.Entry me : this.parameters.entrySet()) { + target.setParameter(me.getKey(), me.getValue()); + } + } + + /** + * Returns the current set of names. + * + * Changes to the underlying HttpParams are not reflected + * in the set - it is a snapshot. + * + * @return the names, as a Set + * @since 4.2 + */ + @Override + public Set getNames() { + return new HashSet(this.parameters.keySet()); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreConnectionPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreConnectionPNames.java new file mode 100644 index 000000000..dc9c63429 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreConnectionPNames.java @@ -0,0 +1,170 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +/** + * Defines parameter names for connections in HttpCore. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public interface CoreConnectionPNames { + + /** + * Defines the socket timeout (SO_TIMEOUT) in milliseconds, + * which is the timeout for waiting for data or, put differently, + * a maximum period inactivity between two consecutive data packets). + * A timeout value of zero is interpreted as an infinite timeout. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + * @see java.net.SocketOptions#SO_TIMEOUT + */ + public static final String SO_TIMEOUT = "http.socket.timeout"; + + /** + * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm + * tries to conserve bandwidth by minimizing the number of segments that are + * sent. When applications wish to decrease network latency and increase + * performance, they can disable Nagle's algorithm (that is enable + * TCP_NODELAY). Data will be sent earlier, at the cost of an increase + * in bandwidth consumption. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + * @see java.net.SocketOptions#TCP_NODELAY + */ + public static final String TCP_NODELAY = "http.tcp.nodelay"; + + /** + * Determines the size of the internal socket buffer used to buffer data + * while receiving / transmitting HTTP messages. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + */ + public static final String SOCKET_BUFFER_SIZE = "http.socket.buffer-size"; + + /** + * Sets SO_LINGER with the specified linger time in seconds. The maximum + * timeout value is platform specific. Value 0 implies that + * the option is disabled. Value -1 implies that the JRE + * default is used. The setting only affects the socket close operation. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + * @see java.net.SocketOptions#SO_LINGER + */ + public static final String SO_LINGER = "http.socket.linger"; + + /** + * Defines whether the socket can be bound even though a previous connection is + * still in a timeout state. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + * @see java.net.Socket#setReuseAddress(boolean) + * + * @since 4.1 + */ + public static final String SO_REUSEADDR = "http.socket.reuseaddr"; + + /** + * Determines the timeout in milliseconds until a connection is established. + * A timeout value of zero is interpreted as an infinite timeout. + *

    + * Please note this parameter can only be applied to connections that + * are bound to a particular local address. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + */ + public static final String CONNECTION_TIMEOUT = "http.connection.timeout"; + + /** + * Determines whether stale connection check is to be used. The stale + * connection check can cause up to 30 millisecond overhead per request and + * should be used only when appropriate. For performance critical + * operations this check should be disabled. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + */ + public static final String STALE_CONNECTION_CHECK = "http.connection.stalecheck"; + + /** + * Determines the maximum line length limit. If set to a positive value, + * any HTTP line exceeding this limit will cause an IOException. A negative + * or zero value will effectively disable the check. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + */ + public static final String MAX_LINE_LENGTH = "http.connection.max-line-length"; + + /** + * Determines the maximum HTTP header count allowed. If set to a positive + * value, the number of HTTP headers received from the data stream exceeding + * this limit will cause an IOException. A negative or zero value will + * effectively disable the check. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + */ + public static final String MAX_HEADER_COUNT = "http.connection.max-header-count"; + + /** + * Defines the size limit below which data chunks should be buffered in a session I/O buffer + * in order to minimize native method invocations on the underlying network socket. + * The optimal value of this parameter can be platform specific and defines a trade-off + * between performance of memory copy operations and that of native method invocation. + *

    + * This parameter expects a value of type {@link Integer}. + *

    + * + * @since 4.1 + */ + public static final String MIN_CHUNK_LIMIT = "http.connection.min-chunk-limit"; + + + /** + * Defines whether or not TCP is to send automatically a keepalive probe to the peer + * after an interval of inactivity (no data exchanged in either direction) between this + * host and the peer. The purpose of this option is to detect if the peer host crashes. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + * @see java.net.SocketOptions#SO_KEEPALIVE + * @since 4.2 + */ + public static final String SO_KEEPALIVE = "http.socket.keepalive"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreProtocolPNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreProtocolPNames.java new file mode 100644 index 000000000..68cf63c8b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/CoreProtocolPNames.java @@ -0,0 +1,152 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +/** + * Defines parameter names for protocol execution in HttpCore. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public interface CoreProtocolPNames { + + /** + * Defines the {@link ch.boye.httpclientandroidlib.ProtocolVersion} used per default. + *

    + * This parameter expects a value of type {@link ch.boye.httpclientandroidlib.ProtocolVersion}. + *

    + */ + public static final String PROTOCOL_VERSION = "http.protocol.version"; + + /** + * Defines the charset to be used for encoding HTTP protocol elements. + *

    + * This parameter expects a value of type {@link String}. + *

    + */ + public static final String HTTP_ELEMENT_CHARSET = "http.protocol.element-charset"; + + /** + * Defines the charset to be used per default for encoding content body. + *

    + * This parameter expects a value of type {@link String}. + *

    + */ + public static final String HTTP_CONTENT_CHARSET = "http.protocol.content-charset"; + + /** + * Defines the content of the User-Agent header. + *

    + * This parameter expects a value of type {@link String}. + *

    + */ + public static final String USER_AGENT = "http.useragent"; + + /** + * Defines the content of the Server header. + *

    + * This parameter expects a value of type {@link String}. + *

    + */ + public static final String ORIGIN_SERVER = "http.origin-server"; + + /** + * Defines whether responses with an invalid Transfer-Encoding + * header should be rejected. + *

    + * This parameter expects a value of type {@link Boolean}. + *

    + */ + public static final String STRICT_TRANSFER_ENCODING = "http.protocol.strict-transfer-encoding"; + + /** + *

    + * Activates 'Expect: 100-Continue' handshake for the + * entity enclosing methods. The purpose of the 'Expect: 100-Continue' + * handshake is to allow a client that is sending a request message with + * a request body to determine if the origin server is willing to + * accept the request (based on the request headers) before the client + * sends the request body. + *

    + * + *

    + * The use of the 'Expect: 100-continue' handshake can result in + * a noticeable performance improvement for entity enclosing requests + * (such as POST and PUT) that require the target server's + * authentication. + *

    + * + *

    + * 'Expect: 100-continue' handshake should be used with + * caution, as it may cause problems with HTTP servers and + * proxies that do not support HTTP/1.1 protocol. + *

    + * + * This parameter expects a value of type {@link Boolean}. + */ + public static final String USE_EXPECT_CONTINUE = "http.protocol.expect-continue"; + + /** + *

    + * Defines the maximum period of time in milliseconds the client should spend + * waiting for a 100-continue response. + *

    + * + * This parameter expects a value of type {@link Integer}. + */ + public static final String WAIT_FOR_CONTINUE = "http.protocol.wait-for-continue"; + + /** + *

    + * Defines the action to perform upon receiving a malformed input. If the input byte sequence + * is not legal for this charset then the input is said to be malformed + *

    + * + * This parameter expects a value of type {@link java.nio.charset.CodingErrorAction} + * + * @since 4.2 + */ + public static final String HTTP_MALFORMED_INPUT_ACTION = "http.malformed.input.action"; + + /** + *

    + * Defines the action to perform upon receiving an unmappable input. If the input byte sequence + * is legal but cannot be mapped to a valid Unicode character then the input is said to be + * unmappable + *

    + * + * This parameter expects a value of type {@link java.nio.charset.CodingErrorAction} + * + * @since 4.2 + */ + public static final String HTTP_UNMAPPABLE_INPUT_ACTION = "http.unmappable.input.action"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/DefaultedHttpParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/DefaultedHttpParams.java new file mode 100644 index 000000000..88be440c3 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/DefaultedHttpParams.java @@ -0,0 +1,163 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import java.util.HashSet; +import java.util.Set; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * {@link HttpParams} implementation that delegates resolution of a parameter + * to the given default {@link HttpParams} instance if the parameter is not + * present in the local one. The state of the local collection can be mutated, + * whereas the default collection is treated as read-only. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public final class DefaultedHttpParams extends AbstractHttpParams { + + private final HttpParams local; + private final HttpParams defaults; + + /** + * Create the defaulted set of HttpParams. + * + * @param local the mutable set of HttpParams + * @param defaults the default set of HttpParams, not mutated by this class + */ + public DefaultedHttpParams(final HttpParams local, final HttpParams defaults) { + super(); + this.local = Args.notNull(local, "Local HTTP parameters"); + this.defaults = defaults; + } + + /** + * Creates a copy of the local collection with the same default + */ + public HttpParams copy() { + final HttpParams clone = this.local.copy(); + return new DefaultedHttpParams(clone, this.defaults); + } + + /** + * Retrieves the value of the parameter from the local collection and, if the + * parameter is not set locally, delegates its resolution to the default + * collection. + */ + public Object getParameter(final String name) { + Object obj = this.local.getParameter(name); + if (obj == null && this.defaults != null) { + obj = this.defaults.getParameter(name); + } + return obj; + } + + /** + * Attempts to remove the parameter from the local collection. This method + * does not modify the default collection. + */ + public boolean removeParameter(final String name) { + return this.local.removeParameter(name); + } + + /** + * Sets the parameter in the local collection. This method does not + * modify the default collection. + */ + public HttpParams setParameter(final String name, final Object value) { + return this.local.setParameter(name, value); + } + + /** + * + * @return the default HttpParams collection + */ + public HttpParams getDefaults() { + return this.defaults; + } + + /** + * Returns the current set of names + * from both the local and default HttpParams instances. + * + * Changes to the underlying HttpParams intances are not reflected + * in the set - it is a snapshot. + * + * @return the combined set of names, as a Set + * @since 4.2 + * @throws UnsupportedOperationException if either the local or default HttpParams instances do not implement HttpParamsNames + */ + @Override + public Set getNames() { + final Set combined = new HashSet(getNames(defaults)); + combined.addAll(getNames(this.local)); + return combined ; + } + + /** + * Returns the current set of defaults names. + * + * Changes to the underlying HttpParams are not reflected + * in the set - it is a snapshot. + * + * @return the names, as a Set + * @since 4.2 + * @throws UnsupportedOperationException if the default HttpParams instance does not implement HttpParamsNames + */ + public Set getDefaultNames() { + return new HashSet(getNames(this.defaults)); + } + + /** + * Returns the current set of local names. + * + * Changes to the underlying HttpParams are not reflected + * in the set - it is a snapshot. + * + * @return the names, as a Set + * @since 4.2 + * @throws UnsupportedOperationException if the local HttpParams instance does not implement HttpParamsNames + */ + public Set getLocalNames() { + return new HashSet(getNames(this.local)); + } + + // Helper method + private Set getNames(final HttpParams params) { + if (params instanceof HttpParamsNames) { + return ((HttpParamsNames) params).getNames(); + } + throw new UnsupportedOperationException("HttpParams instance does not implement HttpParamsNames"); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpAbstractParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpAbstractParamBean.java new file mode 100644 index 000000000..b3010f1ce --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpAbstractParamBean.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public abstract class HttpAbstractParamBean { + + protected final HttpParams params; + + public HttpAbstractParamBean (final HttpParams params) { + super(); + this.params = Args.notNull(params, "HTTP parameters"); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParamBean.java new file mode 100644 index 000000000..d6e6cc471 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParamBean.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate HTTP connection parameters using Java Beans + * conventions. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public class HttpConnectionParamBean extends HttpAbstractParamBean { + + public HttpConnectionParamBean (final HttpParams params) { + super(params); + } + + public void setSoTimeout (final int soTimeout) { + HttpConnectionParams.setSoTimeout(params, soTimeout); + } + + public void setTcpNoDelay (final boolean tcpNoDelay) { + HttpConnectionParams.setTcpNoDelay(params, tcpNoDelay); + } + + public void setSocketBufferSize (final int socketBufferSize) { + HttpConnectionParams.setSocketBufferSize(params, socketBufferSize); + } + + public void setLinger (final int linger) { + HttpConnectionParams.setLinger(params, linger); + } + + public void setConnectionTimeout (final int connectionTimeout) { + HttpConnectionParams.setConnectionTimeout(params, connectionTimeout); + } + + public void setStaleCheckingEnabled (final boolean staleCheckingEnabled) { + HttpConnectionParams.setStaleCheckingEnabled(params, staleCheckingEnabled); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParams.java new file mode 100644 index 000000000..2efae2c37 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpConnectionParams.java @@ -0,0 +1,243 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Utility class for accessing connection parameters in {@link HttpParams}. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public final class HttpConnectionParams implements CoreConnectionPNames { + + private HttpConnectionParams() { + super(); + } + + /** + * Obtains value of the {@link CoreConnectionPNames#SO_TIMEOUT} parameter. + * If not set, defaults to 0. + * + * @param params HTTP parameters. + * @return SO_TIMEOUT. + */ + public static int getSoTimeout(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0); + } + + /** + * Sets value of the {@link CoreConnectionPNames#SO_TIMEOUT} parameter. + * + * @param params HTTP parameters. + * @param timeout SO_TIMEOUT. + */ + public static void setSoTimeout(final HttpParams params, final int timeout) { + Args.notNull(params, "HTTP parameters"); + params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout); + + } + + /** + * Obtains value of the {@link CoreConnectionPNames#SO_REUSEADDR} parameter. + * If not set, defaults to false. + * + * @param params HTTP parameters. + * @return SO_REUSEADDR. + * + * @since 4.1 + */ + public static boolean getSoReuseaddr(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getBooleanParameter(CoreConnectionPNames.SO_REUSEADDR, false); + } + + /** + * Sets value of the {@link CoreConnectionPNames#SO_REUSEADDR} parameter. + * + * @param params HTTP parameters. + * @param reuseaddr SO_REUSEADDR. + * + * @since 4.1 + */ + public static void setSoReuseaddr(final HttpParams params, final boolean reuseaddr) { + Args.notNull(params, "HTTP parameters"); + params.setBooleanParameter(CoreConnectionPNames.SO_REUSEADDR, reuseaddr); + } + + /** + * Obtains value of the {@link CoreConnectionPNames#TCP_NODELAY} parameter. + * If not set, defaults to true. + * + * @param params HTTP parameters. + * @return Nagle's algorithm flag + */ + public static boolean getTcpNoDelay(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true); + } + + /** + * Sets value of the {@link CoreConnectionPNames#TCP_NODELAY} parameter. + * + * @param params HTTP parameters. + * @param value Nagle's algorithm flag + */ + public static void setTcpNoDelay(final HttpParams params, final boolean value) { + Args.notNull(params, "HTTP parameters"); + params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, value); + } + + /** + * Obtains value of the {@link CoreConnectionPNames#SOCKET_BUFFER_SIZE} + * parameter. If not set, defaults to -1. + * + * @param params HTTP parameters. + * @return socket buffer size + */ + public static int getSocketBufferSize(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1); + } + + /** + * Sets value of the {@link CoreConnectionPNames#SOCKET_BUFFER_SIZE} + * parameter. + * + * @param params HTTP parameters. + * @param size socket buffer size + */ + public static void setSocketBufferSize(final HttpParams params, final int size) { + Args.notNull(params, "HTTP parameters"); + params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, size); + } + + /** + * Obtains value of the {@link CoreConnectionPNames#SO_LINGER} parameter. + * If not set, defaults to -1. + * + * @param params HTTP parameters. + * @return SO_LINGER. + */ + public static int getLinger(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1); + } + + /** + * Sets value of the {@link CoreConnectionPNames#SO_LINGER} parameter. + * + * @param params HTTP parameters. + * @param value SO_LINGER. + */ + public static void setLinger(final HttpParams params, final int value) { + Args.notNull(params, "HTTP parameters"); + params.setIntParameter(CoreConnectionPNames.SO_LINGER, value); + } + + /** + * Obtains value of the {@link CoreConnectionPNames#CONNECTION_TIMEOUT} + * parameter. If not set, defaults to 0. + * + * @param params HTTP parameters. + * @return connect timeout. + */ + public static int getConnectionTimeout(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 0); + } + + /** + * Sets value of the {@link CoreConnectionPNames#CONNECTION_TIMEOUT} + * parameter. + * + * @param params HTTP parameters. + * @param timeout connect timeout. + */ + public static void setConnectionTimeout(final HttpParams params, final int timeout) { + Args.notNull(params, "HTTP parameters"); + params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout); + } + + /** + * Obtains value of the {@link CoreConnectionPNames#STALE_CONNECTION_CHECK} + * parameter. If not set, defaults to true. + * + * @param params HTTP parameters. + * @return stale connection check flag. + */ + public static boolean isStaleCheckingEnabled(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, true); + } + + /** + * Sets value of the {@link CoreConnectionPNames#STALE_CONNECTION_CHECK} + * parameter. + * + * @param params HTTP parameters. + * @param value stale connection check flag. + */ + public static void setStaleCheckingEnabled(final HttpParams params, final boolean value) { + Args.notNull(params, "HTTP parameters"); + params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, value); + } + + /** + * Obtains value of the {@link CoreConnectionPNames#SO_KEEPALIVE} parameter. + * If not set, defaults to false. + * + * @param params HTTP parameters. + * @return SO_KEEPALIVE. + * + * @since 4.2 + */ + public static boolean getSoKeepalive(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getBooleanParameter(CoreConnectionPNames.SO_KEEPALIVE, false); + } + + /** + * Sets value of the {@link CoreConnectionPNames#SO_KEEPALIVE} parameter. + * + * @param params HTTP parameters. + * @param enableKeepalive SO_KEEPALIVE. + * + * @since 4.2 + */ + public static void setSoKeepalive(final HttpParams params, final boolean enableKeepalive) { + Args.notNull(params, "HTTP parameters"); + params.setBooleanParameter(CoreConnectionPNames.SO_KEEPALIVE, enableKeepalive); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamConfig.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamConfig.java new file mode 100644 index 000000000..91a62a657 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamConfig.java @@ -0,0 +1,78 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import java.nio.charset.Charset; +import java.nio.charset.CodingErrorAction; + +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.config.SocketConfig; + +/** + * @deprecated (4.3) provided for compatibility with {@link HttpParams}. Do not use. + * + * @since 4.3 + */ +@Deprecated +public final class HttpParamConfig { + + private HttpParamConfig() { + } + + public static SocketConfig getSocketConfig(final HttpParams params) { + return SocketConfig.custom() + .setSoTimeout(params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0)) + .setSoReuseAddress(params.getBooleanParameter(CoreConnectionPNames.SO_REUSEADDR, false)) + .setSoKeepAlive(params.getBooleanParameter(CoreConnectionPNames.SO_KEEPALIVE, false)) + .setSoLinger(params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1)) + .setTcpNoDelay(params.getBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)) + .build(); + } + + public static MessageConstraints getMessageConstraints(final HttpParams params) { + return MessageConstraints.custom() + .setMaxHeaderCount(params.getIntParameter(CoreConnectionPNames.MAX_HEADER_COUNT, -1)) + .setMaxLineLength(params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1)) + .build(); + } + + public static ConnectionConfig getConnectionConfig(final HttpParams params) { + final MessageConstraints messageConstraints = getMessageConstraints(params); + final String csname = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET); + return ConnectionConfig.custom() + .setCharset(csname != null ? Charset.forName(csname) : null) + .setMalformedInputAction((CodingErrorAction) + params.getParameter(CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION)) + .setMalformedInputAction((CodingErrorAction) + params.getParameter(CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION)) + .setMessageConstraints(messageConstraints) + .build(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParams.java new file mode 100644 index 000000000..7eb780b37 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParams.java @@ -0,0 +1,195 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +/** + * HttpParams interface represents a collection of immutable values that define + * a runtime behavior of a component. HTTP parameters should be simple objects: + * integers, doubles, strings, collections and objects that remain immutable + * at runtime. HttpParams is expected to be used in 'write once - read many' mode. + * Once initialized, HTTP parameters are not expected to mutate in + * the course of HTTP message processing. + *

    + * The purpose of this interface is to define a behavior of other components. + * Usually each complex component has its own HTTP parameter collection. + *

    + * Instances of this interface can be linked together to form a hierarchy. + * In the simplest form one set of parameters can use content of another one + * to obtain default values of parameters not present in the local set. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public interface HttpParams { + + /** + * Obtains the value of the given parameter. + * + * @param name the parent name. + * + * @return an object that represents the value of the parameter, + * null if the parameter is not set or if it + * is explicitly set to null + * + * @see #setParameter(String, Object) + */ + Object getParameter(String name); + + /** + * Assigns the value to the parameter with the given name. + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setParameter(String name, Object value); + + /** + * Creates a copy of these parameters. + * + * @return a new set of parameters holding the same values as this one + */ + HttpParams copy(); + + /** + * Removes the parameter with the specified name. + * + * @param name parameter name + * + * @return true if the parameter existed and has been removed, false else. + */ + boolean removeParameter(String name); + + /** + * Returns a {@link Long} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Long} that represents the value of the parameter. + * + * @see #setLongParameter(String, long) + */ + long getLongParameter(String name, long defaultValue); + + /** + * Assigns a {@link Long} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setLongParameter(String name, long value); + + /** + * Returns an {@link Integer} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Integer} that represents the value of the parameter. + * + * @see #setIntParameter(String, int) + */ + int getIntParameter(String name, int defaultValue); + + /** + * Assigns an {@link Integer} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setIntParameter(String name, int value); + + /** + * Returns a {@link Double} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Double} that represents the value of the parameter. + * + * @see #setDoubleParameter(String, double) + */ + double getDoubleParameter(String name, double defaultValue); + + /** + * Assigns a {@link Double} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setDoubleParameter(String name, double value); + + /** + * Returns a {@link Boolean} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Boolean} that represents the value of the parameter. + * + * @see #setBooleanParameter(String, boolean) + */ + boolean getBooleanParameter(String name, boolean defaultValue); + + /** + * Assigns a {@link Boolean} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setBooleanParameter(String name, boolean value); + + /** + * Checks if a boolean parameter is set to true. + * + * @param name parameter name + * + * @return true if the parameter is set to value true, + * false if it is not set or set to false + */ + boolean isParameterTrue(String name); + + /** + * Checks if a boolean parameter is not set or false. + * + * @param name parameter name + * + * @return true if the parameter is either not set or + * set to value false, + * false if it is set to true + */ + boolean isParameterFalse(String name); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamsNames.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamsNames.java new file mode 100644 index 000000000..0c4a34001 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpParamsNames.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import java.util.Set; + +/** + * Gives access to the full set of parameter names. + * + * @see HttpParams + * + * @since 4.2 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public interface HttpParamsNames { + + /** + * Returns the current set of names; + * in the case of stacked parameters, returns the names + * from all the participating HttpParams instances. + * + * Changes to the underlying HttpParams are not reflected + * in the set - it is a snapshot. + * + * @return the names, as a Set + */ + Set getNames(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParamBean.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParamBean.java new file mode 100644 index 000000000..368bffd5e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParamBean.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import ch.boye.httpclientandroidlib.HttpVersion; + +/** + * This is a Java Bean class that can be used to wrap an instance of + * {@link HttpParams} and manipulate HTTP protocol parameters using Java Beans + * conventions. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public class HttpProtocolParamBean extends HttpAbstractParamBean { + + public HttpProtocolParamBean (final HttpParams params) { + super(params); + } + + public void setHttpElementCharset (final String httpElementCharset) { + HttpProtocolParams.setHttpElementCharset(params, httpElementCharset); + } + + public void setContentCharset (final String contentCharset) { + HttpProtocolParams.setContentCharset(params, contentCharset); + } + + public void setVersion (final HttpVersion version) { + HttpProtocolParams.setVersion(params, version); + } + + public void setUserAgent (final String userAgent) { + HttpProtocolParams.setUserAgent(params, userAgent); + } + + public void setUseExpectContinue (final boolean useExpectContinue) { + HttpProtocolParams.setUseExpectContinue(params, useExpectContinue); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParams.java new file mode 100644 index 000000000..2ef3e53ee --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/HttpProtocolParams.java @@ -0,0 +1,240 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.params; + +import java.nio.charset.CodingErrorAction; + +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.protocol.HTTP; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Utility class for accessing protocol parameters in {@link HttpParams}. + * + * @since 4.0 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@Deprecated +public final class HttpProtocolParams implements CoreProtocolPNames { + + private HttpProtocolParams() { + super(); + } + + /** + * Obtains value of the {@link CoreProtocolPNames#HTTP_ELEMENT_CHARSET} parameter. + * If not set, defaults to US-ASCII. + * + * @param params HTTP parameters. + * @return HTTP element charset. + */ + public static String getHttpElementCharset(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + String charset = (String) params.getParameter + (CoreProtocolPNames.HTTP_ELEMENT_CHARSET); + if (charset == null) { + charset = HTTP.DEF_PROTOCOL_CHARSET.name(); + } + return charset; + } + + /** + * Sets value of the {@link CoreProtocolPNames#HTTP_ELEMENT_CHARSET} parameter. + * + * @param params HTTP parameters. + * @param charset HTTP element charset. + */ + public static void setHttpElementCharset(final HttpParams params, final String charset) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, charset); + } + + /** + * Obtains value of the {@link CoreProtocolPNames#HTTP_CONTENT_CHARSET} parameter. + * If not set, defaults to ISO-8859-1. + * + * @param params HTTP parameters. + * @return HTTP content charset. + */ + public static String getContentCharset(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + String charset = (String) params.getParameter + (CoreProtocolPNames.HTTP_CONTENT_CHARSET); + if (charset == null) { + charset = HTTP.DEF_CONTENT_CHARSET.name(); + } + return charset; + } + + /** + * Sets value of the {@link CoreProtocolPNames#HTTP_CONTENT_CHARSET} parameter. + * + * @param params HTTP parameters. + * @param charset HTTP content charset. + */ + public static void setContentCharset(final HttpParams params, final String charset) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, charset); + } + + /** + * Obtains value of the {@link CoreProtocolPNames#PROTOCOL_VERSION} parameter. + * If not set, defaults to {@link HttpVersion#HTTP_1_1}. + * + * @param params HTTP parameters. + * @return HTTP protocol version. + */ + public static ProtocolVersion getVersion(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + final Object param = params.getParameter + (CoreProtocolPNames.PROTOCOL_VERSION); + if (param == null) { + return HttpVersion.HTTP_1_1; + } + return (ProtocolVersion)param; + } + + /** + * Sets value of the {@link CoreProtocolPNames#PROTOCOL_VERSION} parameter. + * + * @param params HTTP parameters. + * @param version HTTP protocol version. + */ + public static void setVersion(final HttpParams params, final ProtocolVersion version) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, version); + } + + /** + * Obtains value of the {@link CoreProtocolPNames#USER_AGENT} parameter. + * If not set, returns null. + * + * @param params HTTP parameters. + * @return User agent string. + */ + public static String getUserAgent(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return (String) params.getParameter(CoreProtocolPNames.USER_AGENT); + } + + /** + * Sets value of the {@link CoreProtocolPNames#USER_AGENT} parameter. + * + * @param params HTTP parameters. + * @param useragent User agent string. + */ + public static void setUserAgent(final HttpParams params, final String useragent) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(CoreProtocolPNames.USER_AGENT, useragent); + } + + /** + * Obtains value of the {@link CoreProtocolPNames#USE_EXPECT_CONTINUE} parameter. + * If not set, returns false. + * + * @param params HTTP parameters. + * @return User agent string. + */ + public static boolean useExpectContinue(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + return params.getBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false); + } + + /** + * Sets value of the {@link CoreProtocolPNames#USE_EXPECT_CONTINUE} parameter. + * + * @param params HTTP parameters. + * @param b expect-continue flag. + */ + public static void setUseExpectContinue(final HttpParams params, final boolean b) { + Args.notNull(params, "HTTP parameters"); + params.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, b); + } + + /** + * Obtains value of the {@link CoreProtocolPNames#HTTP_MALFORMED_INPUT_ACTION} parameter. + * @param params HTTP parameters. + * @return Action to perform upon receiving a malformed input + * + * @since 4.2 + */ + public static CodingErrorAction getMalformedInputAction(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + final Object param = params.getParameter(CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION); + if (param == null) { + // the default CodingErrorAction + return CodingErrorAction.REPORT; + } + return (CodingErrorAction) param; + } + + /** + * Sets value of the {@link CoreProtocolPNames#HTTP_MALFORMED_INPUT_ACTION} parameter. + * @param params HTTP parameters + * @param action action to perform on malformed inputs + * + * @since 4.2 + */ + public static void setMalformedInputAction(final HttpParams params, final CodingErrorAction action) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION, action); + } + + /** + * Obtains the value of the {@link CoreProtocolPNames#HTTP_UNMAPPABLE_INPUT_ACTION} parameter. + * @param params HTTP parameters + * @return Action to perform upon receiving a unmapped input + * + * @since 4.2 + */ + public static CodingErrorAction getUnmappableInputAction(final HttpParams params) { + Args.notNull(params, "HTTP parameters"); + final Object param = params.getParameter(CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION); + if (param == null) { + // the default CodingErrorAction + return CodingErrorAction.REPORT; + } + return (CodingErrorAction) param; + } + + /** + * Sets the value of the {@link CoreProtocolPNames#HTTP_UNMAPPABLE_INPUT_ACTION} parameter. + * @param params HTTP parameters + * @param action action to perform on un mappable inputs + * + * @since 4.2 + */ + public static void setUnmappableInputAction(final HttpParams params, final CodingErrorAction action) { + Args.notNull(params, "HTTP parameters"); + params.setParameter(CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION, action); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/SyncBasicHttpParams.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/SyncBasicHttpParams.java new file mode 100644 index 000000000..30393a074 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/SyncBasicHttpParams.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.params; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +/** + * Thread-safe extension of the {@link BasicHttpParams}. + * + * @since 4.1 + * + * @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config' + * and 'ch.boye.httpclientandroidlib.client.config' + */ +@ThreadSafe +@Deprecated +public class SyncBasicHttpParams extends BasicHttpParams { + + private static final long serialVersionUID = 5387834869062660642L; + + public SyncBasicHttpParams() { + super(); + } + + @Override + public synchronized boolean removeParameter(final String name) { + return super.removeParameter(name); + } + + @Override + public synchronized HttpParams setParameter(final String name, final Object value) { + return super.setParameter(name, value); + } + + @Override + public synchronized Object getParameter(final String name) { + return super.getParameter(name); + } + + @Override + public synchronized boolean isParameterSet(final String name) { + return super.isParameterSet(name); + } + + @Override + public synchronized boolean isParameterSetLocally(final String name) { + return super.isParameterSetLocally(name); + } + + @Override + public synchronized void setParameters(final String[] names, final Object value) { + super.setParameters(names, value); + } + + @Override + public synchronized void clear() { + super.clear(); + } + + @Override + public synchronized Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/package-info.java new file mode 100644 index 000000000..0589b0ff9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/params/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Deprecated. + * @deprecated (4.3). + */ +package ch.boye.httpclientandroidlib.params; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/AbstractConnPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/AbstractConnPool.java new file mode 100644 index 000000000..35aeb656f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/AbstractConnPool.java @@ -0,0 +1,533 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.concurrent.FutureCallback; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Abstract synchronous (blocking) pool of connections. + *

    + * Please note that this class does not maintain its own pool of execution {@link Thread}s. + * Therefore, one must call {@link Future#get()} or {@link Future#get(long, TimeUnit)} + * method on the {@link Future} object returned by the + * {@link #lease(Object, Object, FutureCallback)} method in order for the lease operation + * to complete. + * + * @param the route type that represents the opposite endpoint of a pooled + * connection. + * @param the connection type. + * @param the type of the pool entry containing a pooled connection. + * @since 4.2 + */ +@ThreadSafe +public abstract class AbstractConnPool> + implements ConnPool, ConnPoolControl { + + private final Lock lock; + private final ConnFactory connFactory; + private final Map> routeToPool; + private final Set leased; + private final LinkedList available; + private final LinkedList> pending; + private final Map maxPerRoute; + + private volatile boolean isShutDown; + private volatile int defaultMaxPerRoute; + private volatile int maxTotal; + + public AbstractConnPool( + final ConnFactory connFactory, + final int defaultMaxPerRoute, + final int maxTotal) { + super(); + this.connFactory = Args.notNull(connFactory, "Connection factory"); + this.defaultMaxPerRoute = Args.notNegative(defaultMaxPerRoute, "Max per route value"); + this.maxTotal = Args.notNegative(maxTotal, "Max total value"); + this.lock = new ReentrantLock(); + this.routeToPool = new HashMap>(); + this.leased = new HashSet(); + this.available = new LinkedList(); + this.pending = new LinkedList>(); + this.maxPerRoute = new HashMap(); + } + + /** + * Creates a new entry for the given connection with the given route. + */ + protected abstract E createEntry(T route, C conn); + + /** + * @since 4.3 + */ + protected void onLease(final E entry) { + } + + /** + * @since 4.3 + */ + protected void onRelease(final E entry) { + } + + public boolean isShutdown() { + return this.isShutDown; + } + + /** + * Shuts down the pool. + */ + public void shutdown() throws IOException { + if (this.isShutDown) { + return ; + } + this.isShutDown = true; + this.lock.lock(); + try { + for (final E entry: this.available) { + entry.close(); + } + for (final E entry: this.leased) { + entry.close(); + } + for (final RouteSpecificPool pool: this.routeToPool.values()) { + pool.shutdown(); + } + this.routeToPool.clear(); + this.leased.clear(); + this.available.clear(); + } finally { + this.lock.unlock(); + } + } + + private RouteSpecificPool getPool(final T route) { + RouteSpecificPool pool = this.routeToPool.get(route); + if (pool == null) { + pool = new RouteSpecificPool(route) { + + @Override + protected E createEntry(final C conn) { + return AbstractConnPool.this.createEntry(route, conn); + } + + }; + this.routeToPool.put(route, pool); + } + return pool; + } + + /** + * {@inheritDoc} + *

    + * Please note that this class does not maintain its own pool of execution + * {@link Thread}s. Therefore, one must call {@link Future#get()} + * or {@link Future#get(long, TimeUnit)} method on the {@link Future} + * returned by this method in order for the lease operation to complete. + */ + public Future lease(final T route, final Object state, final FutureCallback callback) { + Args.notNull(route, "Route"); + Asserts.check(!this.isShutDown, "Connection pool shut down"); + return new PoolEntryFuture(this.lock, callback) { + + @Override + public E getPoolEntry( + final long timeout, + final TimeUnit tunit) + throws InterruptedException, TimeoutException, IOException { + final E entry = getPoolEntryBlocking(route, state, timeout, tunit, this); + onLease(entry); + return entry; + } + + }; + } + + /** + * Attempts to lease a connection for the given route and with the given + * state from the pool. + *

    + * Please note that this class does not maintain its own pool of execution + * {@link Thread}s. Therefore, one must call {@link Future#get()} + * or {@link Future#get(long, TimeUnit)} method on the {@link Future} + * returned by this method in order for the lease operation to complete. + * + * @param route route of the connection. + * @param state arbitrary object that represents a particular state + * (usually a security principal or a unique token identifying + * the user whose credentials have been used while establishing the connection). + * May be null. + * @return future for a leased pool entry. + */ + public Future lease(final T route, final Object state) { + return lease(route, state, null); + } + + private E getPoolEntryBlocking( + final T route, final Object state, + final long timeout, final TimeUnit tunit, + final PoolEntryFuture future) + throws IOException, InterruptedException, TimeoutException { + + Date deadline = null; + if (timeout > 0) { + deadline = new Date + (System.currentTimeMillis() + tunit.toMillis(timeout)); + } + + this.lock.lock(); + try { + final RouteSpecificPool pool = getPool(route); + E entry = null; + while (entry == null) { + Asserts.check(!this.isShutDown, "Connection pool shut down"); + for (;;) { + entry = pool.getFree(state); + if (entry == null) { + break; + } + if (entry.isClosed() || entry.isExpired(System.currentTimeMillis())) { + entry.close(); + this.available.remove(entry); + pool.free(entry, false); + } else { + break; + } + } + if (entry != null) { + this.available.remove(entry); + this.leased.add(entry); + return entry; + } + + // New connection is needed + final int maxPerRoute = getMax(route); + // Shrink the pool prior to allocating a new connection + final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute); + if (excess > 0) { + for (int i = 0; i < excess; i++) { + final E lastUsed = pool.getLastUsed(); + if (lastUsed == null) { + break; + } + lastUsed.close(); + this.available.remove(lastUsed); + pool.remove(lastUsed); + } + } + + if (pool.getAllocatedCount() < maxPerRoute) { + final int totalUsed = this.leased.size(); + final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0); + if (freeCapacity > 0) { + final int totalAvailable = this.available.size(); + if (totalAvailable > freeCapacity - 1) { + if (!this.available.isEmpty()) { + final E lastUsed = this.available.removeLast(); + lastUsed.close(); + final RouteSpecificPool otherpool = getPool(lastUsed.getRoute()); + otherpool.remove(lastUsed); + } + } + final C conn = this.connFactory.create(route); + entry = pool.add(conn); + this.leased.add(entry); + return entry; + } + } + + boolean success = false; + try { + pool.queue(future); + this.pending.add(future); + success = future.await(deadline); + } finally { + // In case of 'success', we were woken up by the + // connection pool and should now have a connection + // waiting for us, or else we're shutting down. + // Just continue in the loop, both cases are checked. + pool.unqueue(future); + this.pending.remove(future); + } + // check for spurious wakeup vs. timeout + if (!success && (deadline != null) && + (deadline.getTime() <= System.currentTimeMillis())) { + break; + } + } + throw new TimeoutException("Timeout waiting for connection"); + } finally { + this.lock.unlock(); + } + } + + public void release(final E entry, final boolean reusable) { + this.lock.lock(); + try { + if (this.leased.remove(entry)) { + final RouteSpecificPool pool = getPool(entry.getRoute()); + pool.free(entry, reusable); + if (reusable && !this.isShutDown) { + this.available.addFirst(entry); + onRelease(entry); + } else { + entry.close(); + } + PoolEntryFuture future = pool.nextPending(); + if (future != null) { + this.pending.remove(future); + } else { + future = this.pending.poll(); + } + if (future != null) { + future.wakeup(); + } + } + } finally { + this.lock.unlock(); + } + } + + private int getMax(final T route) { + final Integer v = this.maxPerRoute.get(route); + if (v != null) { + return v.intValue(); + } else { + return this.defaultMaxPerRoute; + } + } + + public void setMaxTotal(final int max) { + Args.notNegative(max, "Max value"); + this.lock.lock(); + try { + this.maxTotal = max; + } finally { + this.lock.unlock(); + } + } + + public int getMaxTotal() { + this.lock.lock(); + try { + return this.maxTotal; + } finally { + this.lock.unlock(); + } + } + + public void setDefaultMaxPerRoute(final int max) { + Args.notNegative(max, "Max per route value"); + this.lock.lock(); + try { + this.defaultMaxPerRoute = max; + } finally { + this.lock.unlock(); + } + } + + public int getDefaultMaxPerRoute() { + this.lock.lock(); + try { + return this.defaultMaxPerRoute; + } finally { + this.lock.unlock(); + } + } + + public void setMaxPerRoute(final T route, final int max) { + Args.notNull(route, "Route"); + Args.notNegative(max, "Max per route value"); + this.lock.lock(); + try { + this.maxPerRoute.put(route, Integer.valueOf(max)); + } finally { + this.lock.unlock(); + } + } + + public int getMaxPerRoute(final T route) { + Args.notNull(route, "Route"); + this.lock.lock(); + try { + return getMax(route); + } finally { + this.lock.unlock(); + } + } + + public PoolStats getTotalStats() { + this.lock.lock(); + try { + return new PoolStats( + this.leased.size(), + this.pending.size(), + this.available.size(), + this.maxTotal); + } finally { + this.lock.unlock(); + } + } + + public PoolStats getStats(final T route) { + Args.notNull(route, "Route"); + this.lock.lock(); + try { + final RouteSpecificPool pool = getPool(route); + return new PoolStats( + pool.getLeasedCount(), + pool.getPendingCount(), + pool.getAvailableCount(), + getMax(route)); + } finally { + this.lock.unlock(); + } + } + + /** + * Enumerates all available connections. + * + * @since 4.3 + */ + protected void enumAvailable(final PoolEntryCallback callback) { + this.lock.lock(); + try { + final Iterator it = this.available.iterator(); + while (it.hasNext()) { + final E entry = it.next(); + callback.process(entry); + if (entry.isClosed()) { + final RouteSpecificPool pool = getPool(entry.getRoute()); + pool.remove(entry); + it.remove(); + } + } + purgePoolMap(); + } finally { + this.lock.unlock(); + } + } + + /** + * Enumerates all leased connections. + * + * @since 4.3 + */ + protected void enumLeased(final PoolEntryCallback callback) { + this.lock.lock(); + try { + final Iterator it = this.leased.iterator(); + while (it.hasNext()) { + final E entry = it.next(); + callback.process(entry); + } + } finally { + this.lock.unlock(); + } + } + + private void purgePoolMap() { + final Iterator>> it = this.routeToPool.entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry> entry = it.next(); + final RouteSpecificPool pool = entry.getValue(); + if (pool.getPendingCount() + pool.getAllocatedCount() == 0) { + it.remove(); + } + } + } + + /** + * Closes connections that have been idle longer than the given period + * of time and evicts them from the pool. + * + * @param idletime maximum idle time. + * @param tunit time unit. + */ + public void closeIdle(final long idletime, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + long time = tunit.toMillis(idletime); + if (time < 0) { + time = 0; + } + final long deadline = System.currentTimeMillis() - time; + enumAvailable(new PoolEntryCallback() { + + public void process(final PoolEntry entry) { + if (entry.getUpdated() <= deadline) { + entry.close(); + } + } + + }); + } + + /** + * Closes expired connections and evicts them from the pool. + */ + public void closeExpired() { + final long now = System.currentTimeMillis(); + enumAvailable(new PoolEntryCallback() { + + public void process(final PoolEntry entry) { + if (entry.isExpired(now)) { + entry.close(); + } + } + + }); + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append("[leased: "); + buffer.append(this.leased); + buffer.append("][available: "); + buffer.append(this.available); + buffer.append("][pending: "); + buffer.append(this.pending); + buffer.append("]"); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnFactory.java new file mode 100644 index 000000000..e2319639c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnFactory.java @@ -0,0 +1,44 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.pool; + +import java.io.IOException; + +/** + * Factory for poolable blocking connections. + * + * @param the route type that represents the opposite endpoint of a pooled + * connection. + * @param the connection type. + * @since 4.2 + */ +public interface ConnFactory { + + C create(T route) throws IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPool.java new file mode 100644 index 000000000..803ecda88 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPool.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +import java.util.concurrent.Future; + +import ch.boye.httpclientandroidlib.concurrent.FutureCallback; + +/** + * ConnPool represents a shared pool connections can be leased from + * and released back to. + * + * @param the route type that represents the opposite endpoint of a pooled + * connection. + * @param the type of the pool entry containing a pooled connection. + * @since 4.2 + */ +public interface ConnPool { + + /** + * Attempts to lease a connection for the given route and with the given + * state from the pool. + * + * @param route route of the connection. + * @param state arbitrary object that represents a particular state + * (usually a security principal or a unique token identifying + * the user whose credentials have been used while establishing the connection). + * May be null. + * @param callback operation completion callback. + * + * @return future for a leased pool entry. + */ + Future lease(final T route, final Object state, final FutureCallback callback); + + /** + * Releases the pool entry back to the pool. + * + * @param entry pool entry leased from the pool + * @param reusable flag indicating whether or not the released connection + * is in a consistent state and is safe for further use. + */ + void release(E entry, boolean reusable); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPoolControl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPoolControl.java new file mode 100644 index 000000000..841ba7ccc --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/ConnPoolControl.java @@ -0,0 +1,56 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +/** + * Interface to control runtime properties of a {@link ConnPool} such as + * maximum total number of connections or maximum connections per route + * allowed. + * + * @param the route type that represents the opposite endpoint of a pooled + * connection. + * @since 4.2 + */ +public interface ConnPoolControl { + + void setMaxTotal(int max); + + int getMaxTotal(); + + void setDefaultMaxPerRoute(int max); + + int getDefaultMaxPerRoute(); + + void setMaxPerRoute(final T route, int max); + + int getMaxPerRoute(final T route); + + PoolStats getTotalStats(); + + PoolStats getStats(final T route); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntry.java new file mode 100644 index 000000000..db52afb6f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntry.java @@ -0,0 +1,183 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Pool entry containing a pool connection object along with its route. + *

    + * The connection contained by the pool entry may have an expiration time which + * can be either set upon construction time or updated with + * the {@link #updateExpiry(long, TimeUnit)}. + *

    + * Pool entry may also have an object associated with it that represents + * a connection state (usually a security principal or a unique token identifying + * the user whose credentials have been used while establishing the connection). + * + * @param the route type that represents the opposite endpoint of a pooled + * connection. + * @param the connection type. + * @since 4.2 + */ +@ThreadSafe +public abstract class PoolEntry { + + private final String id; + private final T route; + private final C conn; + private final long created; + private final long validUnit; + + @GuardedBy("this") + private long updated; + + @GuardedBy("this") + private long expiry; + + private volatile Object state; + + /** + * Creates new PoolEntry instance. + * + * @param id unique identifier of the pool entry. May be null. + * @param route route to the opposite endpoint. + * @param conn the connection. + * @param timeToLive maximum time to live. May be zero if the connection + * does not have an expiry deadline. + * @param tunit time unit. + */ + public PoolEntry(final String id, final T route, final C conn, + final long timeToLive, final TimeUnit tunit) { + super(); + Args.notNull(route, "Route"); + Args.notNull(conn, "Connection"); + Args.notNull(tunit, "Time unit"); + this.id = id; + this.route = route; + this.conn = conn; + this.created = System.currentTimeMillis(); + if (timeToLive > 0) { + this.validUnit = this.created + tunit.toMillis(timeToLive); + } else { + this.validUnit = Long.MAX_VALUE; + } + this.expiry = this.validUnit; + } + + /** + * Creates new PoolEntry instance without an expiry deadline. + * + * @param id unique identifier of the pool entry. May be null. + * @param route route to the opposite endpoint. + * @param conn the connection. + */ + public PoolEntry(final String id, final T route, final C conn) { + this(id, route, conn, 0, TimeUnit.MILLISECONDS); + } + + public String getId() { + return this.id; + } + + public T getRoute() { + return this.route; + } + + public C getConnection() { + return this.conn; + } + + public long getCreated() { + return this.created; + } + + public long getValidUnit() { + return this.validUnit; + } + + public Object getState() { + return this.state; + } + + public void setState(final Object state) { + this.state = state; + } + + public synchronized long getUpdated() { + return this.updated; + } + + public synchronized long getExpiry() { + return this.expiry; + } + + public synchronized void updateExpiry(final long time, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + this.updated = System.currentTimeMillis(); + final long newExpiry; + if (time > 0) { + newExpiry = this.updated + tunit.toMillis(time); + } else { + newExpiry = Long.MAX_VALUE; + } + this.expiry = Math.min(newExpiry, this.validUnit); + } + + public synchronized boolean isExpired(final long now) { + return now >= this.expiry; + } + + /** + * Invalidates the pool entry and closes the pooled connection associated + * with it. + */ + public abstract void close(); + + /** + * Returns true if the pool entry has been invalidated. + */ + public abstract boolean isClosed(); + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append("[id:"); + buffer.append(this.id); + buffer.append("][route:"); + buffer.append(this.route); + buffer.append("][state:"); + buffer.append(this.state); + buffer.append("]"); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryCallback.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryCallback.java new file mode 100644 index 000000000..5c70d63da --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryCallback.java @@ -0,0 +1,41 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +/** + * Pool entry callabck. + * + * @param the route type that represents the opposite endpoint of a pooled + * connection. + * @param the connection type. + * @since 4.3 + */ +public interface PoolEntryCallback { + + void process(PoolEntry entry); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryFuture.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryFuture.java new file mode 100644 index 000000000..743abe8c0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolEntryFuture.java @@ -0,0 +1,155 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.concurrent.FutureCallback; +import ch.boye.httpclientandroidlib.util.Args; + +@ThreadSafe +abstract class PoolEntryFuture implements Future { + + private final Lock lock; + private final FutureCallback callback; + private final Condition condition; + private volatile boolean cancelled; + private volatile boolean completed; + private T result; + + PoolEntryFuture(final Lock lock, final FutureCallback callback) { + super(); + this.lock = lock; + this.condition = lock.newCondition(); + this.callback = callback; + } + + public boolean cancel(final boolean mayInterruptIfRunning) { + this.lock.lock(); + try { + if (this.completed) { + return false; + } + this.completed = true; + this.cancelled = true; + if (this.callback != null) { + this.callback.cancelled(); + } + this.condition.signalAll(); + return true; + } finally { + this.lock.unlock(); + } + } + + public boolean isCancelled() { + return this.cancelled; + } + + public boolean isDone() { + return this.completed; + } + + public T get() throws InterruptedException, ExecutionException { + try { + return get(0, TimeUnit.MILLISECONDS); + } catch (final TimeoutException ex) { + throw new ExecutionException(ex); + } + } + + public T get( + final long timeout, + final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + Args.notNull(unit, "Time unit"); + this.lock.lock(); + try { + if (this.completed) { + return this.result; + } + this.result = getPoolEntry(timeout, unit); + this.completed = true; + if (this.callback != null) { + this.callback.completed(this.result); + } + return result; + } catch (final IOException ex) { + this.completed = true; + this.result = null; + if (this.callback != null) { + this.callback.failed(ex); + } + throw new ExecutionException(ex); + } finally { + this.lock.unlock(); + } + } + + protected abstract T getPoolEntry( + long timeout, TimeUnit unit) throws IOException, InterruptedException, TimeoutException; + + public boolean await(final Date deadline) throws InterruptedException { + this.lock.lock(); + try { + if (this.cancelled) { + throw new InterruptedException("Operation interrupted"); + } + final boolean success; + if (deadline != null) { + success = this.condition.awaitUntil(deadline); + } else { + this.condition.await(); + success = true; + } + if (this.cancelled) { + throw new InterruptedException("Operation interrupted"); + } + return success; + } finally { + this.lock.unlock(); + } + + } + + public void wakeup() { + this.lock.lock(); + try { + this.condition.signalAll(); + } finally { + this.lock.unlock(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolStats.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolStats.java new file mode 100644 index 000000000..17e948ee5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/PoolStats.java @@ -0,0 +1,114 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Pool statistics. + *

    + * The total number of connections in the pool is equal to {@code available} plus {@code leased}. + *

    + * + * @since 4.2 + */ +@Immutable +public class PoolStats { + + private final int leased; + private final int pending; + private final int available; + private final int max; + + public PoolStats(final int leased, final int pending, final int free, final int max) { + super(); + this.leased = leased; + this.pending = pending; + this.available = free; + this.max = max; + } + + /** + * Gets the number of persistent connections tracked by the connection manager currently being used to execute + * requests. + *

    + * The total number of connections in the pool is equal to {@code available} plus {@code leased}. + *

    + * + * @return the number of persistent connections. + */ + public int getLeased() { + return this.leased; + } + + /** + * Gets the number of connection requests being blocked awaiting a free connection. This can happen only if there + * are more worker threads contending for fewer connections. + * + * @return the number of connection requests being blocked awaiting a free connection. + */ + public int getPending() { + return this.pending; + } + + /** + * Gets the number idle persistent connections. + *

    + * The total number of connections in the pool is equal to {@code available} plus {@code leased}. + *

    + * + * @return number idle persistent connections. + */ + public int getAvailable() { + return this.available; + } + + /** + * Gets the maximum number of allowed persistent connections. + * + * @return the maximum number of allowed persistent connections. + */ + public int getMax() { + return this.max; + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append("[leased: "); + buffer.append(this.leased); + buffer.append("; pending: "); + buffer.append(this.pending); + buffer.append("; available: "); + buffer.append(this.available); + buffer.append("; max: "); + buffer.append(this.max); + buffer.append("]"); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/RouteSpecificPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/RouteSpecificPool.java new file mode 100644 index 000000000..7023f79aa --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/RouteSpecificPool.java @@ -0,0 +1,184 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.pool; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +@NotThreadSafe +abstract class RouteSpecificPool> { + + private final T route; + private final Set leased; + private final LinkedList available; + private final LinkedList> pending; + + RouteSpecificPool(final T route) { + super(); + this.route = route; + this.leased = new HashSet(); + this.available = new LinkedList(); + this.pending = new LinkedList>(); + } + + protected abstract E createEntry(C conn); + + public final T getRoute() { + return route; + } + + public int getLeasedCount() { + return this.leased.size(); + } + + public int getPendingCount() { + return this.pending.size(); + } + + public int getAvailableCount() { + return this.available.size(); + } + + public int getAllocatedCount() { + return this.available.size() + this.leased.size(); + } + + public E getFree(final Object state) { + if (!this.available.isEmpty()) { + if (state != null) { + final Iterator it = this.available.iterator(); + while (it.hasNext()) { + final E entry = it.next(); + if (state.equals(entry.getState())) { + it.remove(); + this.leased.add(entry); + return entry; + } + } + } + final Iterator it = this.available.iterator(); + while (it.hasNext()) { + final E entry = it.next(); + if (entry.getState() == null) { + it.remove(); + this.leased.add(entry); + return entry; + } + } + } + return null; + } + + public E getLastUsed() { + if (!this.available.isEmpty()) { + return this.available.getLast(); + } else { + return null; + } + } + + public boolean remove(final E entry) { + Args.notNull(entry, "Pool entry"); + if (!this.available.remove(entry)) { + if (!this.leased.remove(entry)) { + return false; + } + } + return true; + } + + public void free(final E entry, final boolean reusable) { + Args.notNull(entry, "Pool entry"); + final boolean found = this.leased.remove(entry); + Asserts.check(found, "Entry %s has not been leased from this pool", entry); + if (reusable) { + this.available.addFirst(entry); + } + } + + public E add(final C conn) { + final E entry = createEntry(conn); + this.leased.add(entry); + return entry; + } + + public void queue(final PoolEntryFuture future) { + if (future == null) { + return; + } + this.pending.add(future); + } + + public PoolEntryFuture nextPending() { + return this.pending.poll(); + } + + public void unqueue(final PoolEntryFuture future) { + if (future == null) { + return; + } + + this.pending.remove(future); + } + + public void shutdown() { + for (final PoolEntryFuture future: this.pending) { + future.cancel(true); + } + this.pending.clear(); + for (final E entry: this.available) { + entry.close(); + } + this.available.clear(); + for (final E entry: this.leased) { + entry.close(); + } + this.leased.clear(); + } + + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + buffer.append("[route: "); + buffer.append(this.route); + buffer.append("][leased: "); + buffer.append(this.leased.size()); + buffer.append("][available: "); + buffer.append(this.available.size()); + buffer.append("][pending: "); + buffer.append(this.pending.size()); + buffer.append("]"); + return buffer.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/package-info.java new file mode 100644 index 000000000..fdca4e695 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/pool/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Client side connection pools APIs for synchronous, blocking + * communication. + */ +package ch.boye.httpclientandroidlib.pool; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpContext.java new file mode 100644 index 000000000..72c247a5e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpContext.java @@ -0,0 +1,95 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of {@link HttpContext}. + *

    + * Please note instances of this class can be thread unsafe if the + * parent context is not thread safe. + * + * @since 4.0 + */ +@ThreadSafe +public class BasicHttpContext implements HttpContext { + + private final HttpContext parentContext; + private final Map map; + + public BasicHttpContext() { + this(null); + } + + public BasicHttpContext(final HttpContext parentContext) { + super(); + this.map = new ConcurrentHashMap(); + this.parentContext = parentContext; + } + + public Object getAttribute(final String id) { + Args.notNull(id, "Id"); + Object obj = this.map.get(id); + if (obj == null && this.parentContext != null) { + obj = this.parentContext.getAttribute(id); + } + return obj; + } + + public void setAttribute(final String id, final Object obj) { + Args.notNull(id, "Id"); + if (obj != null) { + this.map.put(id, obj); + } else { + this.map.remove(id); + } + } + + public Object removeAttribute(final String id) { + Args.notNull(id, "Id"); + return this.map.remove(id); + } + + /** + * @since 4.2 + */ + public void clear() { + this.map.clear(); + } + + @Override + public String toString() { + return this.map.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpProcessor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpProcessor.java new file mode 100644 index 000000000..1fa203cef --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/BasicHttpProcessor.java @@ -0,0 +1,246 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of {@link HttpProcessor}. + *

    + * Please note access to the internal structures of this class is not + * synchronized and therefore this class may be thread-unsafe. + * + * @since 4.0 + * + * @deprecated (4.3) + */ +@NotThreadSafe +@Deprecated +public final class BasicHttpProcessor implements + HttpProcessor, HttpRequestInterceptorList, HttpResponseInterceptorList, Cloneable { + + // Don't allow direct access, as nulls are not allowed + protected final List requestInterceptors = new ArrayList(); + protected final List responseInterceptors = new ArrayList(); + + public void addRequestInterceptor(final HttpRequestInterceptor itcp) { + if (itcp == null) { + return; + } + this.requestInterceptors.add(itcp); + } + + public void addRequestInterceptor( + final HttpRequestInterceptor itcp, final int index) { + if (itcp == null) { + return; + } + this.requestInterceptors.add(index, itcp); + } + + public void addResponseInterceptor( + final HttpResponseInterceptor itcp, final int index) { + if (itcp == null) { + return; + } + this.responseInterceptors.add(index, itcp); + } + + public void removeRequestInterceptorByClass(final Class clazz) { + for (final Iterator it = this.requestInterceptors.iterator(); + it.hasNext(); ) { + final Object request = it.next(); + if (request.getClass().equals(clazz)) { + it.remove(); + } + } + } + + public void removeResponseInterceptorByClass(final Class clazz) { + for (final Iterator it = this.responseInterceptors.iterator(); + it.hasNext(); ) { + final Object request = it.next(); + if (request.getClass().equals(clazz)) { + it.remove(); + } + } + } + + public final void addInterceptor(final HttpRequestInterceptor interceptor) { + addRequestInterceptor(interceptor); + } + + public final void addInterceptor(final HttpRequestInterceptor interceptor, final int index) { + addRequestInterceptor(interceptor, index); + } + + public int getRequestInterceptorCount() { + return this.requestInterceptors.size(); + } + + public HttpRequestInterceptor getRequestInterceptor(final int index) { + if ((index < 0) || (index >= this.requestInterceptors.size())) { + return null; + } + return this.requestInterceptors.get(index); + } + + public void clearRequestInterceptors() { + this.requestInterceptors.clear(); + } + + public void addResponseInterceptor(final HttpResponseInterceptor itcp) { + if (itcp == null) { + return; + } + this.responseInterceptors.add(itcp); + } + + public final void addInterceptor(final HttpResponseInterceptor interceptor) { + addResponseInterceptor(interceptor); + } + + public final void addInterceptor(final HttpResponseInterceptor interceptor, final int index) { + addResponseInterceptor(interceptor, index); + } + + public int getResponseInterceptorCount() { + return this.responseInterceptors.size(); + } + + public HttpResponseInterceptor getResponseInterceptor(final int index) { + if ((index < 0) || (index >= this.responseInterceptors.size())) { + return null; + } + return this.responseInterceptors.get(index); + } + + public void clearResponseInterceptors() { + this.responseInterceptors.clear(); + } + + /** + * Sets the interceptor lists. + * First, both interceptor lists maintained by this processor + * will be cleared. + * Subsequently, + * elements of the argument list that are request interceptors will be + * added to the request interceptor list. + * Elements that are response interceptors will be + * added to the response interceptor list. + * Elements that are both request and response interceptor will be + * added to both lists. + * Elements that are neither request nor response interceptor + * will be ignored. + * + * @param list the list of request and response interceptors + * from which to initialize + */ + public void setInterceptors(final List list) { + Args.notNull(list, "Inteceptor list"); + this.requestInterceptors.clear(); + this.responseInterceptors.clear(); + for (final Object obj : list) { + if (obj instanceof HttpRequestInterceptor) { + addInterceptor((HttpRequestInterceptor) obj); + } + if (obj instanceof HttpResponseInterceptor) { + addInterceptor((HttpResponseInterceptor) obj); + } + } + } + + /** + * Clears both interceptor lists maintained by this processor. + */ + public void clearInterceptors() { + clearRequestInterceptors(); + clearResponseInterceptors(); + } + + public void process( + final HttpRequest request, + final HttpContext context) + throws IOException, HttpException { + for (final HttpRequestInterceptor interceptor : this.requestInterceptors) { + interceptor.process(request, context); + } + } + + public void process( + final HttpResponse response, + final HttpContext context) + throws IOException, HttpException { + for (final HttpResponseInterceptor interceptor : this.responseInterceptors) { + interceptor.process(response, context); + } + } + + /** + * Sets up the target to have the same list of interceptors + * as the current instance. + * + * @param target object to be initialised + */ + protected void copyInterceptors(final BasicHttpProcessor target) { + target.requestInterceptors.clear(); + target.requestInterceptors.addAll(this.requestInterceptors); + target.responseInterceptors.clear(); + target.responseInterceptors.addAll(this.responseInterceptors); + } + + /** + * Creates a copy of this instance + * + * @return new instance of the BasicHttpProcessor + */ + public BasicHttpProcessor copy() { + final BasicHttpProcessor clone = new BasicHttpProcessor(); + copyInterceptors(clone); + return clone; + } + + @Override + public Object clone() throws CloneNotSupportedException { + final BasicHttpProcessor clone = (BasicHttpProcessor) super.clone(); + copyInterceptors(clone); + return clone; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ChainBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ChainBuilder.java new file mode 100644 index 000000000..19971ffa1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ChainBuilder.java @@ -0,0 +1,126 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * Builder class to build a linked list (chain) of unique class instances. Each class can have + * only one instance in the list. Useful for building lists of protocol interceptors. + * + * @see ImmutableHttpProcessor + * + * @since 4.3 + */ +@NotThreadSafe +final class ChainBuilder { + + private final LinkedList list; + private final Map, E> uniqueClasses; + + public ChainBuilder() { + this.list = new LinkedList(); + this.uniqueClasses = new HashMap, E>(); + } + + private void ensureUnique(final E e) { + final E previous = this.uniqueClasses.remove(e.getClass()); + if (previous != null) { + this.list.remove(previous); + } + this.uniqueClasses.put(e.getClass(), e); + } + + public ChainBuilder addFirst(final E e) { + if (e == null) { + return this; + } + ensureUnique(e); + this.list.addFirst(e); + return this; + } + + public ChainBuilder addLast(final E e) { + if (e == null) { + return this; + } + ensureUnique(e); + this.list.addLast(e); + return this; + } + + public ChainBuilder addAllFirst(final Collection c) { + if (c == null) { + return this; + } + for (final E e: c) { + addFirst(e); + } + return this; + } + + public ChainBuilder addAllFirst(final E... c) { + if (c == null) { + return this; + } + for (final E e: c) { + addFirst(e); + } + return this; + } + + public ChainBuilder addAllLast(final Collection c) { + if (c == null) { + return this; + } + for (final E e: c) { + addLast(e); + } + return this; + } + + public ChainBuilder addAllLast(final E... c) { + if (c == null) { + return this; + } + for (final E e: c) { + addLast(e); + } + return this; + } + + public LinkedList build() { + return new LinkedList(this.list); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/DefaultedHttpContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/DefaultedHttpContext.java new file mode 100644 index 000000000..63ef87b98 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/DefaultedHttpContext.java @@ -0,0 +1,84 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * {@link HttpContext} implementation that delegates resolution of an attribute + * to the given default {@link HttpContext} instance if the attribute is not + * present in the local one. The state of the local context can be mutated, + * whereas the default context is treated as read-only. + * + * @since 4.0 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +public final class DefaultedHttpContext implements HttpContext { + + private final HttpContext local; + private final HttpContext defaults; + + public DefaultedHttpContext(final HttpContext local, final HttpContext defaults) { + super(); + this.local = Args.notNull(local, "HTTP context"); + this.defaults = defaults; + } + + public Object getAttribute(final String id) { + final Object obj = this.local.getAttribute(id); + if (obj == null) { + return this.defaults.getAttribute(id); + } else { + return obj; + } + } + + public Object removeAttribute(final String id) { + return this.local.removeAttribute(id); + } + + public void setAttribute(final String id, final Object obj) { + this.local.setAttribute(id, obj); + } + + public HttpContext getDefaults() { + return this.defaults; + } + + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("[local: ").append(this.local); + buf.append("defaults: ").append(this.defaults); + buf.append("]"); + return buf.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ExecutionContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ExecutionContext.java new file mode 100644 index 000000000..b27a481df --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ExecutionContext.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +/** + * {@link HttpContext} attribute names for protocol execution. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link HttpCoreContext}. + */ +@Deprecated +public interface ExecutionContext { + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpConnection} object that + * represents the actual HTTP connection. + */ + public static final String HTTP_CONNECTION = "http.connection"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpRequest} object that + * represents the actual HTTP request. + */ + public static final String HTTP_REQUEST = "http.request"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpResponse} object that + * represents the actual HTTP response. + */ + public static final String HTTP_RESPONSE = "http.response"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpHost} object that + * represents the connection target. + */ + public static final String HTTP_TARGET_HOST = "http.target_host"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpHost} object that + * represents the connection proxy. + * + * @deprecated (4.3) do not use. + */ + @Deprecated + public static final String HTTP_PROXY_HOST = "http.proxy_host"; + + /** + * Attribute name of a {@link Boolean} object that represents the + * the flag indicating whether the actual request has been fully transmitted + * to the target host. + */ + public static final String HTTP_REQ_SENT = "http.request_sent"; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HTTP.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HTTP.java new file mode 100644 index 000000000..593b3bfdf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HTTP.java @@ -0,0 +1,135 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.nio.charset.Charset; + +import ch.boye.httpclientandroidlib.Consts; + +/** + * Constants and static helpers related to the HTTP protocol. + * + * @since 4.0 + */ +public final class HTTP { + + public static final int CR = 13; // + public static final int LF = 10; // + public static final int SP = 32; // + public static final int HT = 9; // + + /** HTTP header definitions */ + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + public static final String CONTENT_LEN = "Content-Length"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_ENCODING = "Content-Encoding"; + public static final String EXPECT_DIRECTIVE = "Expect"; + public static final String CONN_DIRECTIVE = "Connection"; + public static final String TARGET_HOST = "Host"; + public static final String USER_AGENT = "User-Agent"; + public static final String DATE_HEADER = "Date"; + public static final String SERVER_HEADER = "Server"; + + /** HTTP expectations */ + public static final String EXPECT_CONTINUE = "100-continue"; + + /** HTTP connection control */ + public static final String CONN_CLOSE = "Close"; + public static final String CONN_KEEP_ALIVE = "Keep-Alive"; + + /** Transfer encoding definitions */ + public static final String CHUNK_CODING = "chunked"; + public static final String IDENTITY_CODING = "identity"; + + public static final Charset DEF_CONTENT_CHARSET = Consts.ISO_8859_1; + public static final Charset DEF_PROTOCOL_CHARSET = Consts.ASCII; + + /** + * @deprecated (4.2) + */ + @Deprecated + public static final String UTF_8 = "UTF-8"; + /** + * @deprecated (4.2) + */ + @Deprecated + public static final String UTF_16 = "UTF-16"; + /** + * @deprecated (4.2) + */ + @Deprecated + public static final String US_ASCII = "US-ASCII"; + /** + * @deprecated (4.2) + */ + @Deprecated + public static final String ASCII = "ASCII"; + /** + * @deprecated (4.2) + */ + @Deprecated + public static final String ISO_8859_1 = "ISO-8859-1"; + /** + * @deprecated (4.2) + */ + @Deprecated + public static final String DEFAULT_CONTENT_CHARSET = ISO_8859_1; + /** + * @deprecated (4.2) + */ + @Deprecated + public static final String DEFAULT_PROTOCOL_CHARSET = US_ASCII; + /** + * @deprecated (4.2) + */ + @Deprecated + public final static String OCTET_STREAM_TYPE = "application/octet-stream"; + /** + * @deprecated (4.2) + */ + @Deprecated + public final static String PLAIN_TEXT_TYPE = "text/plain"; + /** + * @deprecated (4.2) + */ + @Deprecated + public final static String CHARSET_PARAM = "; charset="; + /** + * @deprecated (4.2) + */ + @Deprecated + public final static String DEFAULT_CONTENT_TYPE = OCTET_STREAM_TYPE; + + public static boolean isWhitespace(final char ch) { + return ch == SP || ch == HT || ch == CR || ch == LF; + } + + private HTTP() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpContext.java new file mode 100644 index 000000000..c2b70ccea --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpContext.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +/** + * HttpContext represents execution state of an HTTP process. It is a structure + * that can be used to map an attribute name to an attribute value. + *

    + * The primary purpose of the HTTP context is to facilitate information sharing + * among various logically related components. HTTP context can be used + * to store a processing state for one message or several consecutive messages. + * Multiple logically related messages can participate in a logical session + * if the same context is reused between consecutive messages. + *

    / + * IMPORTANT: Please note HTTP context implementation, even when thread safe, + * may not be used concurrently by multiple threads, as the context may contain + * thread unsafe attributes. + * + * @since 4.0 + */ +public interface HttpContext { + + /** The prefix reserved for use by HTTP components. "http." */ + public static final String RESERVED_PREFIX = "http."; + + /** + * Obtains attribute with the given name. + * + * @param id the attribute name. + * @return attribute value, or null if not set. + */ + Object getAttribute(String id); + + /** + * Sets value of the attribute with the given name. + * + * @param id the attribute name. + * @param obj the attribute value. + */ + void setAttribute(String id, Object obj); + + /** + * Removes attribute with the given name from the context. + * + * @param id the attribute name. + * @return attribute value, or null if not set. + */ + Object removeAttribute(String id); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpCoreContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpCoreContext.java new file mode 100644 index 000000000..3d4262751 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpCoreContext.java @@ -0,0 +1,152 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import ch.boye.httpclientandroidlib.HttpConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Implementation of {@link HttpContext} that provides convenience + * setters for user assignable attributes and getter for readable attributes. + * + * @since 4.3 + */ +@NotThreadSafe +public class HttpCoreContext implements HttpContext { + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpConnection} object that + * represents the actual HTTP connection. + */ + public static final String HTTP_CONNECTION = "http.connection"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpRequest} object that + * represents the actual HTTP request. + */ + public static final String HTTP_REQUEST = "http.request"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpResponse} object that + * represents the actual HTTP response. + */ + public static final String HTTP_RESPONSE = "http.response"; + + /** + * Attribute name of a {@link ch.boye.httpclientandroidlib.HttpHost} object that + * represents the connection target. + */ + public static final String HTTP_TARGET_HOST = "http.target_host"; + + /** + * Attribute name of a {@link Boolean} object that represents the + * the flag indicating whether the actual request has been fully transmitted + * to the target host. + */ + public static final String HTTP_REQ_SENT = "http.request_sent"; + + public static HttpCoreContext create() { + return new HttpCoreContext(new BasicHttpContext()); + } + + public static HttpCoreContext adapt(final HttpContext context) { + Args.notNull(context, "HTTP context"); + if (context instanceof HttpCoreContext) { + return (HttpCoreContext) context; + } else { + return new HttpCoreContext(context); + } + } + + private final HttpContext context; + + public HttpCoreContext(final HttpContext context) { + super(); + this.context = context; + } + + public HttpCoreContext() { + super(); + this.context = new BasicHttpContext(); + } + + public Object getAttribute(final String id) { + return context.getAttribute(id); + } + + public void setAttribute(final String id, final Object obj) { + context.setAttribute(id, obj); + } + + public Object removeAttribute(final String id) { + return context.removeAttribute(id); + } + + public T getAttribute(final String attribname, final Class clazz) { + Args.notNull(clazz, "Attribute class"); + final Object obj = getAttribute(attribname); + if (obj == null) { + return null; + } + return clazz.cast(obj); + } + + public T getConnection(final Class clazz) { + return getAttribute(HTTP_CONNECTION, clazz); + } + + public HttpConnection getConnection() { + return getAttribute(HTTP_CONNECTION, HttpConnection.class); + } + + public HttpRequest getRequest() { + return getAttribute(HTTP_REQUEST, HttpRequest.class); + } + + public boolean isRequestSent() { + final Boolean b = getAttribute(HTTP_REQ_SENT, Boolean.class); + return b != null && b.booleanValue(); + } + + public HttpResponse getResponse() { + return getAttribute(HTTP_RESPONSE, HttpResponse.class); + } + + public void setTargetHost(final HttpHost host) { + setAttribute(HTTP_TARGET_HOST, host); + } + + public HttpHost getTargetHost() { + return getAttribute(HTTP_TARGET_HOST, HttpHost.class); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpDateGenerator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpDateGenerator.java new file mode 100644 index 000000000..8e99c1ea2 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpDateGenerator.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +/** + * Generates a date in the format required by the HTTP protocol. + * + * @since 4.0 + */ +@ThreadSafe +public class HttpDateGenerator { + + /** Date format pattern used to generate the header in RFC 1123 format. */ + public static final + String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; + + /** The time zone to use in the date header. */ + public static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + + @GuardedBy("this") + private final DateFormat dateformat; + @GuardedBy("this") + private long dateAsLong = 0L; + @GuardedBy("this") + private String dateAsText = null; + + public HttpDateGenerator() { + super(); + this.dateformat = new SimpleDateFormat(PATTERN_RFC1123, Locale.US); + this.dateformat.setTimeZone(GMT); + } + + public synchronized String getCurrentDate() { + final long now = System.currentTimeMillis(); + if (now - this.dateAsLong > 1000) { + // Generate new date string + this.dateAsText = this.dateformat.format(new Date(now)); + this.dateAsLong = now; + } + return this.dateAsText; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpExpectationVerifier.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpExpectationVerifier.java new file mode 100644 index 000000000..105704616 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpExpectationVerifier.java @@ -0,0 +1,81 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; + +/** + * Defines an interface to verify whether an incoming HTTP request meets + * the target server's expectations. + *

    + * The Expect request-header field is used to indicate that particular + * server behaviors are required by the client. + *

    + *
    + *    Expect       =  "Expect" ":" 1#expectation
    + *
    + *    expectation  =  "100-continue" | expectation-extension
    + *    expectation-extension =  token [ "=" ( token | quoted-string )
    + *                             *expect-params ]
    + *    expect-params =  ";" token [ "=" ( token | quoted-string ) ]
    + *
    + *

    + * A server that does not understand or is unable to comply with any of + * the expectation values in the Expect field of a request MUST respond + * with appropriate error status. The server MUST respond with a 417 + * (Expectation Failed) status if any of the expectations cannot be met + * or, if there are other problems with the request, some other 4xx + * status. + *

    + * + * @since 4.0 + */ +public interface HttpExpectationVerifier { + + /** + * Verifies whether the given request meets the server's expectations. + *

    + * If the request fails to meet particular criteria, this method can + * trigger a terminal response back to the client by setting the status + * code of the response object to a value greater or equal to + * 200. In this case the client will not have to transmit + * the request body. If the request meets expectations this method can + * terminate without modifying the response object. Per default the status + * code of the response object will be set to 100. + * + * @param request the HTTP request. + * @param response the HTTP response. + * @param context the HTTP context. + * @throws HttpException in case of an HTTP protocol violation. + */ + void verify(HttpRequest request, HttpResponse response, HttpContext context) + throws HttpException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessor.java new file mode 100644 index 000000000..e4dac6795 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessor.java @@ -0,0 +1,55 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; + +/** + * HTTP protocol processor is a collection of protocol interceptors that + * implements the 'Chain of Responsibility' pattern, where each individual + * protocol interceptor is expected to work on a particular aspect of the HTTP + * protocol the interceptor is responsible for. + *

    + * Usually the order in which interceptors are executed should not matter as + * long as they do not depend on a particular state of the execution context. + * If protocol interceptors have interdependencies and therefore must be + * executed in a particular order, they should be added to the protocol + * processor in the same sequence as their expected execution order. + *

    + * Protocol interceptors must be implemented as thread-safe. Similarly to + * servlets, protocol interceptors should not use instance variables unless + * access to those variables is synchronized. + * + * @since 4.0 + */ +public interface HttpProcessor + extends HttpRequestInterceptor, HttpResponseInterceptor { + + // no additional methods +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessorBuilder.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessorBuilder.java new file mode 100644 index 000000000..29dfccd60 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpProcessorBuilder.java @@ -0,0 +1,151 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; + +/** + * Builder for {@link HttpProcessor} instances. + * + * @since 4.3 + */ +public class HttpProcessorBuilder { + + private ChainBuilder requestChainBuilder; + private ChainBuilder responseChainBuilder; + + public static HttpProcessorBuilder create() { + return new HttpProcessorBuilder(); + } + + HttpProcessorBuilder() { + super(); + } + + private ChainBuilder getRequestChainBuilder() { + if (requestChainBuilder == null) { + requestChainBuilder = new ChainBuilder(); + } + return requestChainBuilder; + } + + private ChainBuilder getResponseChainBuilder() { + if (responseChainBuilder == null) { + responseChainBuilder = new ChainBuilder(); + } + return responseChainBuilder; + } + + public HttpProcessorBuilder addFirst(final HttpRequestInterceptor e) { + if (e == null) { + return this; + } + getRequestChainBuilder().addFirst(e); + return this; + } + + public HttpProcessorBuilder addLast(final HttpRequestInterceptor e) { + if (e == null) { + return this; + } + getRequestChainBuilder().addLast(e); + return this; + } + + public HttpProcessorBuilder add(final HttpRequestInterceptor e) { + return addLast(e); + } + + public HttpProcessorBuilder addAllFirst(final HttpRequestInterceptor... e) { + if (e == null) { + return this; + } + getRequestChainBuilder().addAllFirst(e); + return this; + } + + public HttpProcessorBuilder addAllLast(final HttpRequestInterceptor... e) { + if (e == null) { + return this; + } + getRequestChainBuilder().addAllLast(e); + return this; + } + + public HttpProcessorBuilder addAll(final HttpRequestInterceptor... e) { + return addAllLast(e); + } + + public HttpProcessorBuilder addFirst(final HttpResponseInterceptor e) { + if (e == null) { + return this; + } + getResponseChainBuilder().addFirst(e); + return this; + } + + public HttpProcessorBuilder addLast(final HttpResponseInterceptor e) { + if (e == null) { + return this; + } + getResponseChainBuilder().addLast(e); + return this; + } + + public HttpProcessorBuilder add(final HttpResponseInterceptor e) { + return addLast(e); + } + + public HttpProcessorBuilder addAllFirst(final HttpResponseInterceptor... e) { + if (e == null) { + return this; + } + getResponseChainBuilder().addAllFirst(e); + return this; + } + + public HttpProcessorBuilder addAllLast(final HttpResponseInterceptor... e) { + if (e == null) { + return this; + } + getResponseChainBuilder().addAllLast(e); + return this; + } + + public HttpProcessorBuilder addAll(final HttpResponseInterceptor... e) { + return addAllLast(e); + } + + public HttpProcessor build() { + return new ImmutableHttpProcessor( + requestChainBuilder != null ? requestChainBuilder.build() : null, + responseChainBuilder != null ? responseChainBuilder.build() : null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestExecutor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestExecutor.java new file mode 100644 index 000000000..d895f0420 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestExecutor.java @@ -0,0 +1,311 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * HttpRequestExecutor is a client side HTTP protocol handler based + * on the blocking (classic) I/O model. + *

    + * HttpRequestExecutor relies on {@link HttpProcessor} to generate + * mandatory protocol headers for all outgoing messages and apply common, + * cross-cutting message transformations to all incoming and outgoing messages. + * Application specific processing can be implemented outside + * HttpRequestExecutor once the request has been executed and + * a response has been received. + * + * @since 4.0 + */ +@Immutable +public class HttpRequestExecutor { + + public static final int DEFAULT_WAIT_FOR_CONTINUE = 3000; + + private final int waitForContinue; + + /** + * Creates new instance of HttpRequestExecutor. + * + * @since 4.3 + */ + public HttpRequestExecutor(final int waitForContinue) { + super(); + this.waitForContinue = Args.positive(waitForContinue, "Wait for continue time"); + } + + public HttpRequestExecutor() { + this(DEFAULT_WAIT_FOR_CONTINUE); + } + + /** + * Decide whether a response comes with an entity. + * The implementation in this class is based on RFC 2616. + *
    + * Derived executors can override this method to handle + * methods and response codes not specified in RFC 2616. + * + * @param request the request, to obtain the executed method + * @param response the response, to obtain the status code + */ + protected boolean canResponseHaveBody(final HttpRequest request, + final HttpResponse response) { + + if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) { + return false; + } + final int status = response.getStatusLine().getStatusCode(); + return status >= HttpStatus.SC_OK + && status != HttpStatus.SC_NO_CONTENT + && status != HttpStatus.SC_NOT_MODIFIED + && status != HttpStatus.SC_RESET_CONTENT; + } + + /** + * Sends the request and obtain a response. + * + * @param request the request to execute. + * @param conn the connection over which to execute the request. + * + * @return the response to the request. + * + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + public HttpResponse execute( + final HttpRequest request, + final HttpClientConnection conn, + final HttpContext context) throws IOException, HttpException { + Args.notNull(request, "HTTP request"); + Args.notNull(conn, "Client connection"); + Args.notNull(context, "HTTP context"); + try { + HttpResponse response = doSendRequest(request, conn, context); + if (response == null) { + response = doReceiveResponse(request, conn, context); + } + return response; + } catch (final IOException ex) { + closeConnection(conn); + throw ex; + } catch (final HttpException ex) { + closeConnection(conn); + throw ex; + } catch (final RuntimeException ex) { + closeConnection(conn); + throw ex; + } + } + + private static void closeConnection(final HttpClientConnection conn) { + try { + conn.close(); + } catch (final IOException ignore) { + } + } + + /** + * Pre-process the given request using the given protocol processor and + * initiates the process of request execution. + * + * @param request the request to prepare + * @param processor the processor to use + * @param context the context for sending the request + * + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + public void preProcess( + final HttpRequest request, + final HttpProcessor processor, + final HttpContext context) throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + Args.notNull(processor, "HTTP processor"); + Args.notNull(context, "HTTP context"); + context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); + processor.process(request, context); + } + + /** + * Send the given request over the given connection. + *

    + * This method also handles the expect-continue handshake if necessary. + * If it does not have to handle an expect-continue handshake, it will + * not use the connection for reading or anything else that depends on + * data coming in over the connection. + * + * @param request the request to send, already + * {@link #preProcess preprocessed} + * @param conn the connection over which to send the request, + * already established + * @param context the context for sending the request + * + * @return a terminal response received as part of an expect-continue + * handshake, or + * null if the expect-continue handshake is not used + * + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + protected HttpResponse doSendRequest( + final HttpRequest request, + final HttpClientConnection conn, + final HttpContext context) throws IOException, HttpException { + Args.notNull(request, "HTTP request"); + Args.notNull(conn, "Client connection"); + Args.notNull(context, "HTTP context"); + + HttpResponse response = null; + + context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); + context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.FALSE); + + conn.sendRequestHeader(request); + if (request instanceof HttpEntityEnclosingRequest) { + // Check for expect-continue handshake. We have to flush the + // headers and wait for an 100-continue response to handle it. + // If we get a different response, we must not send the entity. + boolean sendentity = true; + final ProtocolVersion ver = + request.getRequestLine().getProtocolVersion(); + if (((HttpEntityEnclosingRequest) request).expectContinue() && + !ver.lessEquals(HttpVersion.HTTP_1_0)) { + + conn.flush(); + // As suggested by RFC 2616 section 8.2.3, we don't wait for a + // 100-continue response forever. On timeout, send the entity. + if (conn.isResponseAvailable(this.waitForContinue)) { + response = conn.receiveResponseHeader(); + if (canResponseHaveBody(request, response)) { + conn.receiveResponseEntity(response); + } + final int status = response.getStatusLine().getStatusCode(); + if (status < 200) { + if (status != HttpStatus.SC_CONTINUE) { + throw new ProtocolException( + "Unexpected response: " + response.getStatusLine()); + } + // discard 100-continue + response = null; + } else { + sendentity = false; + } + } + } + if (sendentity) { + conn.sendRequestEntity((HttpEntityEnclosingRequest) request); + } + } + conn.flush(); + context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.TRUE); + return response; + } + + /** + * Waits for and receives a response. + * This method will automatically ignore intermediate responses + * with status code 1xx. + * + * @param request the request for which to obtain the response + * @param conn the connection over which the request was sent + * @param context the context for receiving the response + * + * @return the terminal response, not yet post-processed + * + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + protected HttpResponse doReceiveResponse( + final HttpRequest request, + final HttpClientConnection conn, + final HttpContext context) throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + Args.notNull(conn, "Client connection"); + Args.notNull(context, "HTTP context"); + HttpResponse response = null; + int statusCode = 0; + + while (response == null || statusCode < HttpStatus.SC_OK) { + + response = conn.receiveResponseHeader(); + if (canResponseHaveBody(request, response)) { + conn.receiveResponseEntity(response); + } + statusCode = response.getStatusLine().getStatusCode(); + + } // while intermediate response + + return response; + } + + /** + * Post-processes the given response using the given protocol processor and + * completes the process of request execution. + *

    + * This method does not read the response entity, if any. + * The connection over which content of the response entity is being + * streamed from cannot be reused until + * {@link ch.boye.httpclientandroidlib.util.EntityUtils#consume(ch.boye.httpclientandroidlib.HttpEntity)} + * has been invoked. + * + * @param response the response object to post-process + * @param processor the processor to use + * @param context the context for post-processing the response + * + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + public void postProcess( + final HttpResponse response, + final HttpProcessor processor, + final HttpContext context) throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + Args.notNull(processor, "HTTP processor"); + Args.notNull(context, "HTTP context"); + context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); + processor.process(response, context); + } + +} // class HttpRequestExecutor diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandler.java new file mode 100644 index 000000000..b948ae65d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandler.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; + +/** + * HttpRequestHandler represents a routine for processing of a specific group + * of HTTP requests. Protocol handlers are designed to take care of protocol + * specific aspects, whereas individual request handlers are expected to take + * care of application specific HTTP processing. The main purpose of a request + * handler is to generate a response object with a content entity to be sent + * back to the client in response to the given request + * + * @since 4.0 + */ +public interface HttpRequestHandler { + + /** + * Handles the request and produces a response to be sent back to + * the client. + * + * @param request the HTTP request. + * @param response the HTTP response. + * @param context the HTTP execution context. + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + void handle(HttpRequest request, HttpResponse response, HttpContext context) + throws HttpException, IOException; + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerMapper.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerMapper.java new file mode 100644 index 000000000..e97da4e88 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerMapper.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import ch.boye.httpclientandroidlib.HttpRequest; + +/** + * HttpRequestHandlerMapper can be used to resolve an instance of + * {@link HttpRequestHandler} matching a particular {@link HttpRequest}. Usually the + * mapped request handler will be used to process the request. + * + * @since 4.3 + */ +public interface HttpRequestHandlerMapper { + + /** + * Looks up a handler matching the given request. + * + * @param request the request to map to a handler + * @return HTTP request handler or null if no match + * is found. + */ + HttpRequestHandler lookup(HttpRequest request); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerRegistry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerRegistry.java new file mode 100644 index 000000000..3592d00ac --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerRegistry.java @@ -0,0 +1,107 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.util.Map; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Maintains a map of HTTP request handlers keyed by a request URI pattern. + *
    + * Patterns may have three formats: + *

      + *
    • *
    • + *
    • *<uri>
    • + *
    • <uri>*
    • + *
    + *
    + * This class can be used to resolve an instance of + * {@link HttpRequestHandler} matching a particular request URI. Usually the + * resolved request handler will be used to process the request with the + * specified request URI. + * + * @since 4.0 + * @deprecated (4.3) use {@link UriHttpRequestHandlerMapper} + */ +@ThreadSafe // provided injected dependencies are thread-safe +@Deprecated +public class HttpRequestHandlerRegistry implements HttpRequestHandlerResolver { + + private final UriPatternMatcher matcher; + + public HttpRequestHandlerRegistry() { + matcher = new UriPatternMatcher(); + } + + /** + * Registers the given {@link HttpRequestHandler} as a handler for URIs + * matching the given pattern. + * + * @param pattern the pattern to register the handler for. + * @param handler the handler. + */ + public void register(final String pattern, final HttpRequestHandler handler) { + Args.notNull(pattern, "URI request pattern"); + Args.notNull(handler, "Request handler"); + matcher.register(pattern, handler); + } + + /** + * Removes registered handler, if exists, for the given pattern. + * + * @param pattern the pattern to unregister the handler for. + */ + public void unregister(final String pattern) { + matcher.unregister(pattern); + } + + /** + * Sets handlers from the given map. + * @param map the map containing handlers keyed by their URI patterns. + */ + public void setHandlers(final Map map) { + matcher.setObjects(map); + } + + /** + * Get the handler map. + * @return The map of handlers and their associated URI patterns. + * + * @since 4.2 + */ + public Map getHandlers() { + return matcher.getObjects(); + } + + public HttpRequestHandler lookup(final String requestURI) { + return matcher.lookup(requestURI); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerResolver.java new file mode 100644 index 000000000..a898b8d18 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestHandlerResolver.java @@ -0,0 +1,51 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +/** + * HttpRequestHandlerResolver can be used to resolve an instance of + * {@link HttpRequestHandler} matching a particular request URI. Usually the + * mapped request handler will be used to process the request with the + * specified request URI. + * + * @since 4.0 + * @deprecated see {@link HttpRequestHandlerMapper} + */ +@Deprecated +public interface HttpRequestHandlerResolver { + + /** + * Looks up a handler matching the given request URI. + * + * @param requestURI the request URI + * @return HTTP request handler or null if no match + * is found. + */ + HttpRequestHandler lookup(String requestURI); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestInterceptorList.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestInterceptorList.java new file mode 100644 index 000000000..da4fd6f86 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpRequestInterceptorList.java @@ -0,0 +1,103 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; + +/** + * Provides access to an ordered list of request interceptors. + * Lists are expected to be built upfront and used read-only afterwards + * for {@link HttpProcessor processing}. + * + * @since 4.0 + * + * @deprecated (4.3) + */ +@Deprecated +public interface HttpRequestInterceptorList { + + /** + * Appends a request interceptor to this list. + * + * @param interceptor the request interceptor to add + */ + void addRequestInterceptor(HttpRequestInterceptor interceptor); + + /** + * Inserts a request interceptor at the specified index. + * + * @param interceptor the request interceptor to add + * @param index the index to insert the interceptor at + */ + void addRequestInterceptor(HttpRequestInterceptor interceptor, int index); + + /** + * Obtains the current size of this list. + * + * @return the number of request interceptors in this list + */ + int getRequestInterceptorCount(); + + /** + * Obtains a request interceptor from this list. + * + * @param index the index of the interceptor to obtain, + * 0 for first + * + * @return the interceptor at the given index, or + * null if the index is out of range + */ + HttpRequestInterceptor getRequestInterceptor(int index); + + /** + * Removes all request interceptors from this list. + */ + void clearRequestInterceptors(); + + /** + * Removes all request interceptor of the specified class + * + * @param clazz the class of the instances to be removed. + */ + void removeRequestInterceptorByClass(Class clazz); + + /** + * Sets the request interceptors in this list. + * This list will be cleared and re-initialized to contain + * all request interceptors from the argument list. + * If the argument list includes elements that are not request + * interceptors, the behavior is implementation dependent. + * + * @param list the list of request interceptors + */ + void setInterceptors(List list); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpResponseInterceptorList.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpResponseInterceptorList.java new file mode 100644 index 000000000..9265b6738 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpResponseInterceptorList.java @@ -0,0 +1,103 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; + +/** + * Provides access to an ordered list of response interceptors. + * Lists are expected to be built upfront and used read-only afterwards + * for {@link HttpProcessor processing}. + * + * @since 4.0 + * + * @deprecated (4.3) + */ +@Deprecated +public interface HttpResponseInterceptorList { + + /** + * Appends a response interceptor to this list. + * + * @param interceptor the response interceptor to add + */ + void addResponseInterceptor(HttpResponseInterceptor interceptor); + + /** + * Inserts a response interceptor at the specified index. + * + * @param interceptor the response interceptor to add + * @param index the index to insert the interceptor at + */ + void addResponseInterceptor(HttpResponseInterceptor interceptor, int index); + + /** + * Obtains the current size of this list. + * + * @return the number of response interceptors in this list + */ + int getResponseInterceptorCount(); + + /** + * Obtains a response interceptor from this list. + * + * @param index the index of the interceptor to obtain, + * 0 for first + * + * @return the interceptor at the given index, or + * null if the index is out of range + */ + HttpResponseInterceptor getResponseInterceptor(int index); + + /** + * Removes all response interceptors from this list. + */ + void clearResponseInterceptors(); + + /** + * Removes all response interceptor of the specified class + * + * @param clazz the class of the instances to be removed. + */ + void removeResponseInterceptorByClass(Class clazz); + + /** + * Sets the response interceptors in this list. + * This list will be cleared and re-initialized to contain + * all response interceptors from the argument list. + * If the argument list includes elements that are not response + * interceptors, the behavior is implementation dependent. + * + * @param list the list of response interceptors + */ + void setInterceptors(List list); + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpService.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpService.java new file mode 100644 index 000000000..13defa2d9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/HttpService.java @@ -0,0 +1,447 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.ConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.HttpServerConnection; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.MethodNotSupportedException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.UnsupportedHttpVersionException; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.entity.ByteArrayEntity; +import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy; +import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.EncodingUtils; +import ch.boye.httpclientandroidlib.util.EntityUtils; + +/** + * HttpService is a server side HTTP protocol handler based on + * the classic (blocking) I/O model. + *

    + * HttpService relies on {@link HttpProcessor} to generate mandatory + * protocol headers for all outgoing messages and apply common, cross-cutting + * message transformations to all incoming and outgoing messages, whereas + * individual {@link HttpRequestHandler}s are expected to implement + * application specific content generation and processing. + *

    + * HttpService uses {@link HttpRequestHandlerMapper} to map + * matching request handler for a particular request URI of an incoming HTTP + * request. + *

    + * HttpService can use optional {@link HttpExpectationVerifier} + * to ensure that incoming requests meet server's expectations. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@Immutable // provided injected dependencies are immutable and deprecated methods are not used +public class HttpService { + + /** + * TODO: make all variables final in the next major version + */ + private volatile HttpParams params = null; + private volatile HttpProcessor processor = null; + private volatile HttpRequestHandlerMapper handlerMapper = null; + private volatile ConnectionReuseStrategy connStrategy = null; + private volatile HttpResponseFactory responseFactory = null; + private volatile HttpExpectationVerifier expectationVerifier = null; + + /** + * Create a new HTTP service. + * + * @param processor the processor to use on requests and responses + * @param connStrategy the connection reuse strategy + * @param responseFactory the response factory + * @param handlerResolver the handler resolver. May be null. + * @param expectationVerifier the expectation verifier. May be null. + * @param params the HTTP parameters + * + * @since 4.1 + * @deprecated (4.3) use {@link HttpService#HttpService(HttpProcessor, ConnectionReuseStrategy, + * HttpResponseFactory, HttpRequestHandlerMapper, HttpExpectationVerifier)} + */ + @Deprecated + public HttpService( + final HttpProcessor processor, + final ConnectionReuseStrategy connStrategy, + final HttpResponseFactory responseFactory, + final HttpRequestHandlerResolver handlerResolver, + final HttpExpectationVerifier expectationVerifier, + final HttpParams params) { + this(processor, + connStrategy, + responseFactory, + new HttpRequestHandlerResolverAdapter(handlerResolver), + expectationVerifier); + this.params = params; + } + + /** + * Create a new HTTP service. + * + * @param processor the processor to use on requests and responses + * @param connStrategy the connection reuse strategy + * @param responseFactory the response factory + * @param handlerResolver the handler resolver. May be null. + * @param params the HTTP parameters + * + * @since 4.1 + * @deprecated (4.3) use {@link HttpService#HttpService(HttpProcessor, ConnectionReuseStrategy, + * HttpResponseFactory, HttpRequestHandlerMapper)} + */ + @Deprecated + public HttpService( + final HttpProcessor processor, + final ConnectionReuseStrategy connStrategy, + final HttpResponseFactory responseFactory, + final HttpRequestHandlerResolver handlerResolver, + final HttpParams params) { + this(processor, + connStrategy, + responseFactory, + new HttpRequestHandlerResolverAdapter(handlerResolver), + null); + this.params = params; + } + + /** + * Create a new HTTP service. + * + * @param proc the processor to use on requests and responses + * @param connStrategy the connection reuse strategy + * @param responseFactory the response factory + * + * @deprecated (4.1) use {@link HttpService#HttpService(HttpProcessor, + * ConnectionReuseStrategy, HttpResponseFactory, HttpRequestHandlerResolver, HttpParams)} + */ + @Deprecated + public HttpService( + final HttpProcessor proc, + final ConnectionReuseStrategy connStrategy, + final HttpResponseFactory responseFactory) { + super(); + setHttpProcessor(proc); + setConnReuseStrategy(connStrategy); + setResponseFactory(responseFactory); + } + + /** + * Create a new HTTP service. + * + * @param processor the processor to use on requests and responses + * @param connStrategy the connection reuse strategy. If null + * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used. + * @param responseFactory the response factory. If null + * {@link DefaultHttpResponseFactory#INSTANCE} will be used. + * @param handlerMapper the handler mapper. May be null. + * @param expectationVerifier the expectation verifier. May be null. + * + * @since 4.3 + */ + public HttpService( + final HttpProcessor processor, + final ConnectionReuseStrategy connStrategy, + final HttpResponseFactory responseFactory, + final HttpRequestHandlerMapper handlerMapper, + final HttpExpectationVerifier expectationVerifier) { + super(); + this.processor = Args.notNull(processor, "HTTP processor"); + this.connStrategy = connStrategy != null ? connStrategy : + DefaultConnectionReuseStrategy.INSTANCE; + this.responseFactory = responseFactory != null ? responseFactory : + DefaultHttpResponseFactory.INSTANCE; + this.handlerMapper = handlerMapper; + this.expectationVerifier = expectationVerifier; + } + + /** + * Create a new HTTP service. + * + * @param processor the processor to use on requests and responses + * @param connStrategy the connection reuse strategy. If null + * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used. + * @param responseFactory the response factory. If null + * {@link DefaultHttpResponseFactory#INSTANCE} will be used. + * @param handlerMapper the handler mapper. May be null. + * + * @since 4.3 + */ + public HttpService( + final HttpProcessor processor, + final ConnectionReuseStrategy connStrategy, + final HttpResponseFactory responseFactory, + final HttpRequestHandlerMapper handlerMapper) { + this(processor, connStrategy, responseFactory, handlerMapper, null); + } + + /** + * Create a new HTTP service. + * + * @param processor the processor to use on requests and responses + * @param handlerMapper the handler mapper. May be null. + * + * @since 4.3 + */ + public HttpService( + final HttpProcessor processor, final HttpRequestHandlerMapper handlerMapper) { + this(processor, null, null, handlerMapper, null); + } + + /** + * @deprecated (4.1) set {@link HttpProcessor} using constructor + */ + @Deprecated + public void setHttpProcessor(final HttpProcessor processor) { + Args.notNull(processor, "HTTP processor"); + this.processor = processor; + } + + /** + * @deprecated (4.1) set {@link ConnectionReuseStrategy} using constructor + */ + @Deprecated + public void setConnReuseStrategy(final ConnectionReuseStrategy connStrategy) { + Args.notNull(connStrategy, "Connection reuse strategy"); + this.connStrategy = connStrategy; + } + + /** + * @deprecated (4.1) set {@link HttpResponseFactory} using constructor + */ + @Deprecated + public void setResponseFactory(final HttpResponseFactory responseFactory) { + Args.notNull(responseFactory, "Response factory"); + this.responseFactory = responseFactory; + } + + /** + * @deprecated (4.1) set {@link HttpResponseFactory} using constructor + */ + @Deprecated + public void setParams(final HttpParams params) { + this.params = params; + } + + /** + * @deprecated (4.1) set {@link HttpRequestHandlerResolver} using constructor + */ + @Deprecated + public void setHandlerResolver(final HttpRequestHandlerResolver handlerResolver) { + this.handlerMapper = new HttpRequestHandlerResolverAdapter(handlerResolver); + } + + /** + * @deprecated (4.1) set {@link HttpExpectationVerifier} using constructor + */ + @Deprecated + public void setExpectationVerifier(final HttpExpectationVerifier expectationVerifier) { + this.expectationVerifier = expectationVerifier; + } + + /** + * @deprecated (4.3) no longer used. + */ + @Deprecated + public HttpParams getParams() { + return this.params; + } + + /** + * Handles receives one HTTP request over the given connection within the + * given execution context and sends a response back to the client. + * + * @param conn the active connection to the client + * @param context the actual execution context. + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + public void handleRequest( + final HttpServerConnection conn, + final HttpContext context) throws IOException, HttpException { + + context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn); + + HttpResponse response = null; + + try { + + final HttpRequest request = conn.receiveRequestHeader(); + if (request instanceof HttpEntityEnclosingRequest) { + + if (((HttpEntityEnclosingRequest) request).expectContinue()) { + response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1, + HttpStatus.SC_CONTINUE, context); + if (this.expectationVerifier != null) { + try { + this.expectationVerifier.verify(request, response, context); + } catch (final HttpException ex) { + response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0, + HttpStatus.SC_INTERNAL_SERVER_ERROR, context); + handleException(ex, response); + } + } + if (response.getStatusLine().getStatusCode() < 200) { + // Send 1xx response indicating the server expections + // have been met + conn.sendResponseHeader(response); + conn.flush(); + response = null; + conn.receiveRequestEntity((HttpEntityEnclosingRequest) request); + } + } else { + conn.receiveRequestEntity((HttpEntityEnclosingRequest) request); + } + } + + context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); + + if (response == null) { + response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1, + HttpStatus.SC_OK, context); + this.processor.process(request, context); + doService(request, response, context); + } + + // Make sure the request content is fully consumed + if (request instanceof HttpEntityEnclosingRequest) { + final HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity(); + EntityUtils.consume(entity); + } + + } catch (final HttpException ex) { + response = this.responseFactory.newHttpResponse + (HttpVersion.HTTP_1_0, HttpStatus.SC_INTERNAL_SERVER_ERROR, + context); + handleException(ex, response); + } + + context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); + + this.processor.process(response, context); + conn.sendResponseHeader(response); + conn.sendResponseEntity(response); + conn.flush(); + + if (!this.connStrategy.keepAlive(response, context)) { + conn.close(); + } + } + + /** + * Handles the given exception and generates an HTTP response to be sent + * back to the client to inform about the exceptional condition encountered + * in the course of the request processing. + * + * @param ex the exception. + * @param response the HTTP response. + */ + protected void handleException(final HttpException ex, final HttpResponse response) { + if (ex instanceof MethodNotSupportedException) { + response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); + } else if (ex instanceof UnsupportedHttpVersionException) { + response.setStatusCode(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED); + } else if (ex instanceof ProtocolException) { + response.setStatusCode(HttpStatus.SC_BAD_REQUEST); + } else { + response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); + } + String message = ex.getMessage(); + if (message == null) { + message = ex.toString(); + } + final byte[] msg = EncodingUtils.getAsciiBytes(message); + final ByteArrayEntity entity = new ByteArrayEntity(msg); + entity.setContentType("text/plain; charset=US-ASCII"); + response.setEntity(entity); + } + + /** + * The default implementation of this method attempts to resolve an + * {@link HttpRequestHandler} for the request URI of the given request + * and, if found, executes its + * {@link HttpRequestHandler#handle(HttpRequest, HttpResponse, HttpContext)} + * method. + *

    + * Super-classes can override this method in order to provide a custom + * implementation of the request processing logic. + * + * @param request the HTTP request. + * @param response the HTTP response. + * @param context the execution context. + * @throws IOException in case of an I/O error. + * @throws HttpException in case of HTTP protocol violation or a processing + * problem. + */ + protected void doService( + final HttpRequest request, + final HttpResponse response, + final HttpContext context) throws HttpException, IOException { + HttpRequestHandler handler = null; + if (this.handlerMapper != null) { + handler = this.handlerMapper.lookup(request); + } + if (handler != null) { + handler.handle(request, response, context); + } else { + response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); + } + } + + /** + * Adaptor class to transition from HttpRequestHandlerResolver to HttpRequestHandlerMapper. + */ + @Deprecated + private static class HttpRequestHandlerResolverAdapter implements HttpRequestHandlerMapper { + + private final HttpRequestHandlerResolver resolver; + + public HttpRequestHandlerResolverAdapter(final HttpRequestHandlerResolver resolver) { + this.resolver = resolver; + } + + public HttpRequestHandler lookup(final HttpRequest request) { + return resolver.lookup(request.getRequestLine().getUri()); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ImmutableHttpProcessor.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ImmutableHttpProcessor.java new file mode 100644 index 000000000..8cb80c91b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ImmutableHttpProcessor.java @@ -0,0 +1,143 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; + +/** + * Immutable {@link HttpProcessor}. + * + * @since 4.1 + */ +@ThreadSafe // provided injected dependencies are immutable +public final class ImmutableHttpProcessor implements HttpProcessor { + + private final HttpRequestInterceptor[] requestInterceptors; + private final HttpResponseInterceptor[] responseInterceptors; + + public ImmutableHttpProcessor( + final HttpRequestInterceptor[] requestInterceptors, + final HttpResponseInterceptor[] responseInterceptors) { + super(); + if (requestInterceptors != null) { + final int l = requestInterceptors.length; + this.requestInterceptors = new HttpRequestInterceptor[l]; + System.arraycopy(requestInterceptors, 0, this.requestInterceptors, 0, l); + } else { + this.requestInterceptors = new HttpRequestInterceptor[0]; + } + if (responseInterceptors != null) { + final int l = responseInterceptors.length; + this.responseInterceptors = new HttpResponseInterceptor[l]; + System.arraycopy(responseInterceptors, 0, this.responseInterceptors, 0, l); + } else { + this.responseInterceptors = new HttpResponseInterceptor[0]; + } + } + + /** + * @since 4.3 + */ + public ImmutableHttpProcessor( + final List requestInterceptors, + final List responseInterceptors) { + super(); + if (requestInterceptors != null) { + final int l = requestInterceptors.size(); + this.requestInterceptors = requestInterceptors.toArray(new HttpRequestInterceptor[l]); + } else { + this.requestInterceptors = new HttpRequestInterceptor[0]; + } + if (responseInterceptors != null) { + final int l = responseInterceptors.size(); + this.responseInterceptors = responseInterceptors.toArray(new HttpResponseInterceptor[l]); + } else { + this.responseInterceptors = new HttpResponseInterceptor[0]; + } + } + + /** + * @deprecated (4.3) do not use. + */ + @Deprecated + public ImmutableHttpProcessor( + final HttpRequestInterceptorList requestInterceptors, + final HttpResponseInterceptorList responseInterceptors) { + super(); + if (requestInterceptors != null) { + final int count = requestInterceptors.getRequestInterceptorCount(); + this.requestInterceptors = new HttpRequestInterceptor[count]; + for (int i = 0; i < count; i++) { + this.requestInterceptors[i] = requestInterceptors.getRequestInterceptor(i); + } + } else { + this.requestInterceptors = new HttpRequestInterceptor[0]; + } + if (responseInterceptors != null) { + final int count = responseInterceptors.getResponseInterceptorCount(); + this.responseInterceptors = new HttpResponseInterceptor[count]; + for (int i = 0; i < count; i++) { + this.responseInterceptors[i] = responseInterceptors.getResponseInterceptor(i); + } + } else { + this.responseInterceptors = new HttpResponseInterceptor[0]; + } + } + + public ImmutableHttpProcessor(final HttpRequestInterceptor... requestInterceptors) { + this(requestInterceptors, null); + } + + public ImmutableHttpProcessor(final HttpResponseInterceptor... responseInterceptors) { + this(null, responseInterceptors); + } + + public void process( + final HttpRequest request, + final HttpContext context) throws IOException, HttpException { + for (final HttpRequestInterceptor requestInterceptor : this.requestInterceptors) { + requestInterceptor.process(request, context); + } + } + + public void process( + final HttpResponse response, + final HttpContext context) throws IOException, HttpException { + for (final HttpResponseInterceptor responseInterceptor : this.responseInterceptors) { + responseInterceptor.process(response, context); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestConnControl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestConnControl.java new file mode 100644 index 000000000..1db509599 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestConnControl.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * RequestConnControl is responsible for adding Connection header + * to the outgoing requests, which is essential for managing persistence of + * HTTP/1.0 connections. This interceptor is recommended for + * client side protocol processors. + * + * @since 4.0 + */ +@Immutable +public class RequestConnControl implements HttpRequestInterceptor { + + public RequestConnControl() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase("CONNECT")) { + return; + } + + if (!request.containsHeader(HTTP.CONN_DIRECTIVE)) { + // Default policy is to keep connection alive + // whenever possible + request.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestContent.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestContent.java new file mode 100644 index 000000000..2f90ad6d8 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestContent.java @@ -0,0 +1,127 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * RequestContent is the most important interceptor for outgoing requests. + * It is responsible for delimiting content length by adding + * Content-Length or Transfer-Content headers based + * on the properties of the enclosed entity and the protocol version. + * This interceptor is required for correct functioning of client side protocol + * processors. + * + * @since 4.0 + */ +@Immutable +public class RequestContent implements HttpRequestInterceptor { + + private final boolean overwrite; + + /** + * Default constructor. The Content-Length or Transfer-Encoding + * will cause the interceptor to throw {@link ProtocolException} if already present in the + * response message. + */ + public RequestContent() { + this(false); + } + + /** + * Constructor that can be used to fine-tune behavior of this interceptor. + * + * @param overwrite If set to true the Content-Length and + * Transfer-Encoding headers will be created or updated if already present. + * If set to false the Content-Length and + * Transfer-Encoding headers will cause the interceptor to throw + * {@link ProtocolException} if already present in the response message. + * + * @since 4.2 + */ + public RequestContent(final boolean overwrite) { + super(); + this.overwrite = overwrite; + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + if (request instanceof HttpEntityEnclosingRequest) { + if (this.overwrite) { + request.removeHeaders(HTTP.TRANSFER_ENCODING); + request.removeHeaders(HTTP.CONTENT_LEN); + } else { + if (request.containsHeader(HTTP.TRANSFER_ENCODING)) { + throw new ProtocolException("Transfer-encoding header already present"); + } + if (request.containsHeader(HTTP.CONTENT_LEN)) { + throw new ProtocolException("Content-Length header already present"); + } + } + final ProtocolVersion ver = request.getRequestLine().getProtocolVersion(); + final HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity(); + if (entity == null) { + request.addHeader(HTTP.CONTENT_LEN, "0"); + return; + } + // Must specify a transfer encoding or a content length + if (entity.isChunked() || entity.getContentLength() < 0) { + if (ver.lessEquals(HttpVersion.HTTP_1_0)) { + throw new ProtocolException( + "Chunked transfer encoding not allowed for " + ver); + } + request.addHeader(HTTP.TRANSFER_ENCODING, HTTP.CHUNK_CODING); + } else { + request.addHeader(HTTP.CONTENT_LEN, Long.toString(entity.getContentLength())); + } + // Specify a content type if known + if (entity.getContentType() != null && !request.containsHeader( + HTTP.CONTENT_TYPE )) { + request.addHeader(entity.getContentType()); + } + // Specify a content encoding if known + if (entity.getContentEncoding() != null && !request.containsHeader( + HTTP.CONTENT_ENCODING)) { + request.addHeader(entity.getContentEncoding()); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestDate.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestDate.java new file mode 100644 index 000000000..a2a0fa8b6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestDate.java @@ -0,0 +1,65 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * RequestDate interceptor is responsible for adding Date header + * to the outgoing requests This interceptor is optional for client side + * protocol processors. + * + * @since 4.0 + */ +@ThreadSafe +public class RequestDate implements HttpRequestInterceptor { + + private static final HttpDateGenerator DATE_GENERATOR = new HttpDateGenerator(); + + public RequestDate() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + if ((request instanceof HttpEntityEnclosingRequest) && + !request.containsHeader(HTTP.DATE_HEADER)) { + final String httpdate = DATE_GENERATOR.getCurrentDate(); + request.setHeader(HTTP.DATE_HEADER, httpdate); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestExpectContinue.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestExpectContinue.java new file mode 100644 index 000000000..16cb8cd1c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestExpectContinue.java @@ -0,0 +1,93 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.params.CoreProtocolPNames; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * RequestExpectContinue is responsible for enabling the 'expect-continue' + * handshake by adding Expect header. This interceptor is + * recommended for client side protocol processors. + * + * @since 4.0 + */ +@Immutable +@SuppressWarnings("deprecation") +public class RequestExpectContinue implements HttpRequestInterceptor { + + private final boolean activeByDefault; + + /** + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.protocol.RequestExpectContinue#RequestExpectContinue(boolean)} + */ + @Deprecated + public RequestExpectContinue() { + this(false); + } + + /** + * @since 4.3 + */ + public RequestExpectContinue(final boolean activeByDefault) { + super(); + this.activeByDefault = activeByDefault; + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + + if (!request.containsHeader(HTTP.EXPECT_DIRECTIVE)) { + if (request instanceof HttpEntityEnclosingRequest) { + final ProtocolVersion ver = request.getRequestLine().getProtocolVersion(); + final HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity(); + // Do not send the expect header if request body is known to be empty + if (entity != null + && entity.getContentLength() != 0 && !ver.lessEquals(HttpVersion.HTTP_1_0)) { + final boolean active = request.getParams().getBooleanParameter( + CoreProtocolPNames.USE_EXPECT_CONTINUE, this.activeByDefault); + if (active) { + request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE); + } + } + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestTargetHost.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestTargetHost.java new file mode 100644 index 000000000..531a8a235 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestTargetHost.java @@ -0,0 +1,95 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpConnection; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpInetConnection; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * RequestTargetHost is responsible for adding Host header. This + * interceptor is required for client side protocol processors. + * + * @since 4.0 + */ +@Immutable +public class RequestTargetHost implements HttpRequestInterceptor { + + public RequestTargetHost() { + super(); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + + final HttpCoreContext corecontext = HttpCoreContext.adapt(context); + + final ProtocolVersion ver = request.getRequestLine().getProtocolVersion(); + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase("CONNECT") && ver.lessEquals(HttpVersion.HTTP_1_0)) { + return; + } + + if (!request.containsHeader(HTTP.TARGET_HOST)) { + HttpHost targethost = corecontext.getTargetHost(); + if (targethost == null) { + final HttpConnection conn = corecontext.getConnection(); + if (conn instanceof HttpInetConnection) { + // Populate the context with a default HTTP host based on the + // inet address of the target host + final InetAddress address = ((HttpInetConnection) conn).getRemoteAddress(); + final int port = ((HttpInetConnection) conn).getRemotePort(); + if (address != null) { + targethost = new HttpHost(address.getHostName(), port); + } + } + if (targethost == null) { + if (ver.lessEquals(HttpVersion.HTTP_1_0)) { + return; + } else { + throw new ProtocolException("Target host missing"); + } + } + } + request.addHeader(HTTP.TARGET_HOST, targethost.toHostString()); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestUserAgent.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestUserAgent.java new file mode 100644 index 000000000..23f4a45a7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/RequestUserAgent.java @@ -0,0 +1,79 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpRequestInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.params.CoreProtocolPNames; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * RequestUserAgent is responsible for adding User-Agent header. + * This interceptor is recommended for client side protocol processors. + * + * @since 4.0 + */ +@SuppressWarnings("deprecation") +@Immutable +public class RequestUserAgent implements HttpRequestInterceptor { + + private final String userAgent; + + public RequestUserAgent(final String userAgent) { + super(); + this.userAgent = userAgent; + } + + public RequestUserAgent() { + this(null); + } + + public void process(final HttpRequest request, final HttpContext context) + throws HttpException, IOException { + Args.notNull(request, "HTTP request"); + if (!request.containsHeader(HTTP.USER_AGENT)) { + String s = null; + final HttpParams params = request.getParams(); + if (params != null) { + s = (String) params.getParameter(CoreProtocolPNames.USER_AGENT); + } + if (s == null) { + s = this.userAgent; + } + if (s != null) { + request.addHeader(HTTP.USER_AGENT, s); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseConnControl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseConnControl.java new file mode 100644 index 000000000..29014e32f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseConnControl.java @@ -0,0 +1,105 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * ResponseConnControl is responsible for adding Connection header + * to the outgoing responses, which is essential for managing persistence of + * HTTP/1.0 connections. This interceptor is recommended for + * server side protocol processors. + * + * @since 4.0 + */ +@Immutable +public class ResponseConnControl implements HttpResponseInterceptor { + + public ResponseConnControl() { + super(); + } + + public void process(final HttpResponse response, final HttpContext context) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + + final HttpCoreContext corecontext = HttpCoreContext.adapt(context); + + // Always drop connection after certain type of responses + final int status = response.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_BAD_REQUEST || + status == HttpStatus.SC_REQUEST_TIMEOUT || + status == HttpStatus.SC_LENGTH_REQUIRED || + status == HttpStatus.SC_REQUEST_TOO_LONG || + status == HttpStatus.SC_REQUEST_URI_TOO_LONG || + status == HttpStatus.SC_SERVICE_UNAVAILABLE || + status == HttpStatus.SC_NOT_IMPLEMENTED) { + response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); + return; + } + final Header explicit = response.getFirstHeader(HTTP.CONN_DIRECTIVE); + if (explicit != null && HTTP.CONN_CLOSE.equalsIgnoreCase(explicit.getValue())) { + // Connection persistence explicitly disabled + return; + } + // Always drop connection for HTTP/1.0 responses and below + // if the content body cannot be correctly delimited + final HttpEntity entity = response.getEntity(); + if (entity != null) { + final ProtocolVersion ver = response.getStatusLine().getProtocolVersion(); + if (entity.getContentLength() < 0 && + (!entity.isChunked() || ver.lessEquals(HttpVersion.HTTP_1_0))) { + response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); + return; + } + } + // Drop connection if requested by the client or request was <= 1.0 + final HttpRequest request = corecontext.getRequest(); + if (request != null) { + final Header header = request.getFirstHeader(HTTP.CONN_DIRECTIVE); + if (header != null) { + response.setHeader(HTTP.CONN_DIRECTIVE, header.getValue()); + } else if (request.getProtocolVersion().lessEquals(HttpVersion.HTTP_1_0)) { + response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseContent.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseContent.java new file mode 100644 index 000000000..e2dcf2eef --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseContent.java @@ -0,0 +1,133 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.HttpVersion; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.ProtocolVersion; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * ResponseContent is the most important interceptor for outgoing responses. + * It is responsible for delimiting content length by adding + * Content-Length or Transfer-Content headers based + * on the properties of the enclosed entity and the protocol version. + * This interceptor is required for correct functioning of server side protocol + * processors. + * + * @since 4.0 + */ +@Immutable +public class ResponseContent implements HttpResponseInterceptor { + + private final boolean overwrite; + + /** + * Default constructor. The Content-Length or Transfer-Encoding + * will cause the interceptor to throw {@link ProtocolException} if already present in the + * response message. + */ + public ResponseContent() { + this(false); + } + + /** + * Constructor that can be used to fine-tune behavior of this interceptor. + * + * @param overwrite If set to true the Content-Length and + * Transfer-Encoding headers will be created or updated if already present. + * If set to false the Content-Length and + * Transfer-Encoding headers will cause the interceptor to throw + * {@link ProtocolException} if already present in the response message. + * + * @since 4.2 + */ + public ResponseContent(final boolean overwrite) { + super(); + this.overwrite = overwrite; + } + + /** + * Processes the response (possibly updating or inserting) Content-Length and Transfer-Encoding headers. + * @param response The HttpResponse to modify. + * @param context Unused. + * @throws ProtocolException If either the Content-Length or Transfer-Encoding headers are found. + * @throws IllegalArgumentException If the response is null. + */ + public void process(final HttpResponse response, final HttpContext context) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + if (this.overwrite) { + response.removeHeaders(HTTP.TRANSFER_ENCODING); + response.removeHeaders(HTTP.CONTENT_LEN); + } else { + if (response.containsHeader(HTTP.TRANSFER_ENCODING)) { + throw new ProtocolException("Transfer-encoding header already present"); + } + if (response.containsHeader(HTTP.CONTENT_LEN)) { + throw new ProtocolException("Content-Length header already present"); + } + } + final ProtocolVersion ver = response.getStatusLine().getProtocolVersion(); + final HttpEntity entity = response.getEntity(); + if (entity != null) { + final long len = entity.getContentLength(); + if (entity.isChunked() && !ver.lessEquals(HttpVersion.HTTP_1_0)) { + response.addHeader(HTTP.TRANSFER_ENCODING, HTTP.CHUNK_CODING); + } else if (len >= 0) { + response.addHeader(HTTP.CONTENT_LEN, Long.toString(entity.getContentLength())); + } + // Specify a content type if known + if (entity.getContentType() != null && !response.containsHeader( + HTTP.CONTENT_TYPE )) { + response.addHeader(entity.getContentType()); + } + // Specify a content encoding if known + if (entity.getContentEncoding() != null && !response.containsHeader( + HTTP.CONTENT_ENCODING)) { + response.addHeader(entity.getContentEncoding()); + } + } else { + final int status = response.getStatusLine().getStatusCode(); + if (status != HttpStatus.SC_NO_CONTENT + && status != HttpStatus.SC_NOT_MODIFIED + && status != HttpStatus.SC_RESET_CONTENT) { + response.addHeader(HTTP.CONTENT_LEN, "0"); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseDate.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseDate.java new file mode 100644 index 000000000..300ff761e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseDate.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * ResponseDate is responsible for adding Date header to the + * outgoing responses. This interceptor is recommended for server side protocol + * processors. + * + * @since 4.0 + */ +@ThreadSafe +public class ResponseDate implements HttpResponseInterceptor { + + private static final HttpDateGenerator DATE_GENERATOR = new HttpDateGenerator(); + + public ResponseDate() { + super(); + } + + public void process(final HttpResponse response, final HttpContext context) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + final int status = response.getStatusLine().getStatusCode(); + if ((status >= HttpStatus.SC_OK) && + !response.containsHeader(HTTP.DATE_HEADER)) { + final String httpdate = DATE_GENERATOR.getCurrentDate(); + response.setHeader(HTTP.DATE_HEADER, httpdate); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseServer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseServer.java new file mode 100644 index 000000000..f0672a006 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/ResponseServer.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseInterceptor; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * ResponseServer is responsible for adding Server header. This + * interceptor is recommended for server side protocol processors. + * + * @since 4.0 + */ +@Immutable +public class ResponseServer implements HttpResponseInterceptor { + + private final String originServer; + + /** + * @since 4.3 + */ + public ResponseServer(final String originServer) { + super(); + this.originServer = originServer; + } + + public ResponseServer() { + this(null); + } + + public void process(final HttpResponse response, final HttpContext context) + throws HttpException, IOException { + Args.notNull(response, "HTTP response"); + if (!response.containsHeader(HTTP.SERVER_HEADER)) { + if (this.originServer != null) { + response.addHeader(HTTP.SERVER_HEADER, this.originServer); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/SyncBasicHttpContext.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/SyncBasicHttpContext.java new file mode 100644 index 000000000..60138764b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/SyncBasicHttpContext.java @@ -0,0 +1,74 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +/** + * Thread-safe extension of the {@link BasicHttpContext}. + * + * @since 4.0 + * + * @deprecated (4.2) HttpContext instances may not be shared by multiple threads + */ +@Deprecated +public class SyncBasicHttpContext extends BasicHttpContext { + + public SyncBasicHttpContext(final HttpContext parentContext) { + super(parentContext); + } + + /** + * @since 4.2 + */ + public SyncBasicHttpContext() { + super(); + } + + @Override + public synchronized Object getAttribute(final String id) { + return super.getAttribute(id); + } + + @Override + public synchronized void setAttribute(final String id, final Object obj) { + super.setAttribute(id, obj); + } + + @Override + public synchronized Object removeAttribute(final String id) { + return super.removeAttribute(id); + } + + /** + * @since 4.2 + */ + @Override + public synchronized void clear() { + super.clear(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriHttpRequestHandlerMapper.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriHttpRequestHandlerMapper.java new file mode 100644 index 000000000..f09dea611 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriHttpRequestHandlerMapper.java @@ -0,0 +1,115 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Maintains a map of HTTP request handlers keyed by a request URI pattern. + *
    + * Patterns may have three formats: + *

      + *
    • *
    • + *
    • *<uri>
    • + *
    • <uri>*
    • + *
    + *
    + * This class can be used to map an instance of + * {@link HttpRequestHandler} matching a particular request URI. Usually the + * mapped request handler will be used to process the request with the + * specified request URI. + * + * @since 4.3 + */ +@ThreadSafe // provided injected dependencies are thread-safe +public class UriHttpRequestHandlerMapper implements HttpRequestHandlerMapper { + + private final UriPatternMatcher matcher; + + protected UriHttpRequestHandlerMapper(final UriPatternMatcher matcher) { + super(); + this.matcher = Args.notNull(matcher, "Pattern matcher"); + } + + public UriHttpRequestHandlerMapper() { + this(new UriPatternMatcher()); + } + + /** + * Registers the given {@link HttpRequestHandler} as a handler for URIs + * matching the given pattern. + * + * @param pattern the pattern to register the handler for. + * @param handler the handler. + */ + public void register(final String pattern, final HttpRequestHandler handler) { + Args.notNull(pattern, "Pattern"); + Args.notNull(handler, "Handler"); + matcher.register(pattern, handler); + } + + /** + * Removes registered handler, if exists, for the given pattern. + * + * @param pattern the pattern to unregister the handler for. + */ + public void unregister(final String pattern) { + matcher.unregister(pattern); + } + + /** + * Extracts request path from the given {@link HttpRequest} + */ + protected String getRequestPath(final HttpRequest request) { + String uriPath = request.getRequestLine().getUri(); + int index = uriPath.indexOf("?"); + if (index != -1) { + uriPath = uriPath.substring(0, index); + } else { + index = uriPath.indexOf("#"); + if (index != -1) { + uriPath = uriPath.substring(0, index); + } + } + return uriPath; + } + + /** + * Looks up a handler matching the given request URI. + * + * @param request the request + * @return handler or null if no match is found. + */ + public HttpRequestHandler lookup(final HttpRequest request) { + Args.notNull(request, "HTTP request"); + return matcher.lookup(getRequestPath(request)); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriPatternMatcher.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriPatternMatcher.java new file mode 100644 index 000000000..77c46a70c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/UriPatternMatcher.java @@ -0,0 +1,165 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.protocol; + +import java.util.HashMap; +import java.util.Map; + +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Maintains a map of objects keyed by a request URI pattern. + *
    + * Patterns may have three formats: + *
      + *
    • *
    • + *
    • *<uri>
    • + *
    • <uri>*
    • + *
    + *
    + * This class can be used to resolve an object matching a particular request + * URI. + * + * @since 4.0 + */ +@ThreadSafe +public class UriPatternMatcher { + + @GuardedBy("this") + private final Map map; + + public UriPatternMatcher() { + super(); + this.map = new HashMap(); + } + + /** + * Registers the given object for URIs matching the given pattern. + * + * @param pattern the pattern to register the handler for. + * @param obj the object. + */ + public synchronized void register(final String pattern, final T obj) { + Args.notNull(pattern, "URI request pattern"); + this.map.put(pattern, obj); + } + + /** + * Removes registered object, if exists, for the given pattern. + * + * @param pattern the pattern to unregister. + */ + public synchronized void unregister(final String pattern) { + if (pattern == null) { + return; + } + this.map.remove(pattern); + } + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + public synchronized void setHandlers(final Map map) { + Args.notNull(map, "Map of handlers"); + this.map.clear(); + this.map.putAll(map); + } + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + public synchronized void setObjects(final Map map) { + Args.notNull(map, "Map of handlers"); + this.map.clear(); + this.map.putAll(map); + } + + /** + * @deprecated (4.1) do not use + */ + @Deprecated + public synchronized Map getObjects() { + return this.map; + } + + /** + * Looks up an object matching the given request path. + * + * @param path the request path + * @return object or null if no match is found. + */ + public synchronized T lookup(final String path) { + Args.notNull(path, "Request path"); + // direct match? + T obj = this.map.get(path); + if (obj == null) { + // pattern match? + String bestMatch = null; + for (final String pattern : this.map.keySet()) { + if (matchUriRequestPattern(pattern, path)) { + // we have a match. is it any better? + if (bestMatch == null + || (bestMatch.length() < pattern.length()) + || (bestMatch.length() == pattern.length() && pattern.endsWith("*"))) { + obj = this.map.get(pattern); + bestMatch = pattern; + } + } + } + } + return obj; + } + + /** + * Tests if the given request path matches the given pattern. + * + * @param pattern the pattern + * @param path the request path + * @return true if the request URI matches the pattern, + * false otherwise. + */ + protected boolean matchUriRequestPattern(final String pattern, final String path) { + if (pattern.equals("*")) { + return true; + } else { + return + (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1))) || + (pattern.startsWith("*") && path.endsWith(pattern.substring(1, pattern.length()))); + } + } + + @Override + public String toString() { + return this.map.toString(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/package-info.java new file mode 100644 index 000000000..97fcbfdc1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/protocol/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Core HTTP protocol execution framework and HTTP protocol handlers + * for synchronous, blocking communication. + */ +package ch.boye.httpclientandroidlib.protocol; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Args.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Args.java new file mode 100644 index 000000000..16e6ec4bf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Args.java @@ -0,0 +1,111 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +import java.util.Collection; + +public class Args { + + public static void check(final boolean expression, final String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + } + + public static void check(final boolean expression, final String message, final Object... args) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, args)); + } + } + + public static T notNull(final T argument, final String name) { + if (argument == null) { + throw new IllegalArgumentException(name + " may not be null"); + } + return argument; + } + + public static T notEmpty(final T argument, final String name) { + if (argument == null) { + throw new IllegalArgumentException(name + " may not be null"); + } + if (TextUtils.isEmpty(argument)) { + throw new IllegalArgumentException(name + " may not be empty"); + } + return argument; + } + + public static T notBlank(final T argument, final String name) { + if (argument == null) { + throw new IllegalArgumentException(name + " may not be null"); + } + if (TextUtils.isBlank(argument)) { + throw new IllegalArgumentException(name + " may not be blank"); + } + return argument; + } + + public static > T notEmpty(final T argument, final String name) { + if (argument == null) { + throw new IllegalArgumentException(name + " may not be null"); + } + if (argument.isEmpty()) { + throw new IllegalArgumentException(name + " may not be empty"); + } + return argument; + } + + public static int positive(final int n, final String name) { + if (n <= 0) { + throw new IllegalArgumentException(name + " may not be negative or zero"); + } + return n; + } + + public static long positive(final long n, final String name) { + if (n <= 0) { + throw new IllegalArgumentException(name + " may not be negative or zero"); + } + return n; + } + + public static int notNegative(final int n, final String name) { + if (n < 0) { + throw new IllegalArgumentException(name + " may not be negative"); + } + return n; + } + + public static long notNegative(final long n, final String name) { + if (n < 0) { + throw new IllegalArgumentException(name + " may not be negative"); + } + return n; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Asserts.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Asserts.java new file mode 100644 index 000000000..4515e1577 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/Asserts.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +public class Asserts { + + public static void check(final boolean expression, final String message) { + if (!expression) { + throw new IllegalStateException(message); + } + } + + public static void check(final boolean expression, final String message, final Object... args) { + if (!expression) { + throw new IllegalStateException(String.format(message, args)); + } + } + + public static void notNull(final Object object, final String name) { + if (object == null) { + throw new IllegalStateException(name + " is null"); + } + } + + public static void notEmpty(final CharSequence s, final String name) { + if (TextUtils.isEmpty(s)) { + throw new IllegalStateException(name + " is empty"); + } + } + + public static void notBlank(final CharSequence s, final String name) { + if (TextUtils.isBlank(s)) { + throw new IllegalStateException(name + " is blank"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ByteArrayBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ByteArrayBuffer.java new file mode 100644 index 000000000..cbf7c943d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ByteArrayBuffer.java @@ -0,0 +1,347 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +/** + * A resizable byte array. + * + * @since 4.0 + */ +@NotThreadSafe +public final class ByteArrayBuffer implements Serializable { + + private static final long serialVersionUID = 4359112959524048036L; + + private byte[] buffer; + private int len; + + /** + * Creates an instance of {@link ByteArrayBuffer} with the given initial + * capacity. + * + * @param capacity the capacity + */ + public ByteArrayBuffer(final int capacity) { + super(); + Args.notNegative(capacity, "Buffer capacity"); + this.buffer = new byte[capacity]; + } + + private void expand(final int newlen) { + final byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)]; + System.arraycopy(this.buffer, 0, newbuffer, 0, this.len); + this.buffer = newbuffer; + } + + /** + * Appends len bytes to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len bytes. + * + * @param b the bytes to be appended. + * @param off the index of the first byte to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off if out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final byte[] b, final int off, final int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + final int newlen = this.len + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + System.arraycopy(b, off, this.buffer, this.len, len); + this.len = newlen; + } + + /** + * Appends b byte to this buffer. The capacity of the buffer + * is increased, if necessary, to accommodate the additional byte. + * + * @param b the byte to be appended. + */ + public void append(final int b) { + final int newlen = this.len + 1; + if (newlen > this.buffer.length) { + expand(newlen); + } + this.buffer[this.len] = (byte)b; + this.len = newlen; + } + + /** + * Appends len chars to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased if necessary to accommodate all len chars. + *

    + * The chars are converted to bytes using simple cast. + * + * @param b the chars to be appended. + * @param off the index of the first char to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off if out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final char[] b, final int off, final int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + final int oldlen = this.len; + final int newlen = oldlen + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { + this.buffer[i2] = (byte) b[i1]; + } + this.len = newlen; + } + + /** + * Appends len chars to this buffer from the given source + * char array buffer starting at index off. The capacity + * of the buffer is increased if necessary to accommodate all + * len chars. + *

    + * The chars are converted to bytes using simple cast. + * + * @param b the chars to be appended. + * @param off the index of the first char to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off if out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final CharArrayBuffer b, final int off, final int len) { + if (b == null) { + return; + } + append(b.buffer(), off, len); + } + + /** + * Clears content of the buffer. The underlying byte array is not resized. + */ + public void clear() { + this.len = 0; + } + + /** + * Converts the content of this buffer to an array of bytes. + * + * @return byte array + */ + public byte[] toByteArray() { + final byte[] b = new byte[this.len]; + if (this.len > 0) { + System.arraycopy(this.buffer, 0, b, 0, this.len); + } + return b; + } + + /** + * Returns the byte value in this buffer at the specified + * index. The index argument must be greater than or equal to + * 0, and less than the length of this buffer. + * + * @param i the index of the desired byte value. + * @return the byte value at the specified index. + * @throws IndexOutOfBoundsException if index is + * negative or greater than or equal to {@link #length()}. + */ + public int byteAt(final int i) { + return this.buffer[i]; + } + + /** + * Returns the current capacity. The capacity is the amount of storage + * available for newly appended bytes, beyond which an allocation + * will occur. + * + * @return the current capacity + */ + public int capacity() { + return this.buffer.length; + } + + /** + * Returns the length of the buffer (byte count). + * + * @return the length of the buffer + */ + public int length() { + return this.len; + } + + /** + * Ensures that the capacity is at least equal to the specified minimum. + * If the current capacity is less than the argument, then a new internal + * array is allocated with greater capacity. If the required + * argument is non-positive, this method takes no action. + * + * @param required the minimum required capacity. + * + * @since 4.1 + */ + public void ensureCapacity(final int required) { + if (required <= 0) { + return; + } + final int available = this.buffer.length - this.len; + if (required > available) { + expand(this.len + required); + } + } + + /** + * Returns reference to the underlying byte array. + * + * @return the byte array. + */ + public byte[] buffer() { + return this.buffer; + } + + /** + * Sets the length of the buffer. The new length value is expected to be + * less than the current capacity and greater than or equal to + * 0. + * + * @param len the new length + * @throws IndexOutOfBoundsException if the + * len argument is greater than the current + * capacity of the buffer or less than 0. + */ + public void setLength(final int len) { + if (len < 0 || len > this.buffer.length) { + throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length); + } + this.len = len; + } + + /** + * Returns true if this buffer is empty, that is, its + * {@link #length()} is equal to 0. + * @return true if this buffer is empty, false + * otherwise. + */ + public boolean isEmpty() { + return this.len == 0; + } + + /** + * Returns true if this buffer is full, that is, its + * {@link #length()} is equal to its {@link #capacity()}. + * @return true if this buffer is full, false + * otherwise. + */ + public boolean isFull() { + return this.len == this.buffer.length; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified byte, starting the search at the specified + * beginIndex and finishing at endIndex. + * If no such byte occurs in this buffer within the specified bounds, + * -1 is returned. + *

    + * There is no restriction on the value of beginIndex and + * endIndex. If beginIndex is negative, + * it has the same effect as if it were zero. If endIndex is + * greater than {@link #length()}, it has the same effect as if it were + * {@link #length()}. If the beginIndex is greater than + * the endIndex, -1 is returned. + * + * @param b the byte to search for. + * @param from the index to start the search from. + * @param to the index to finish the search at. + * @return the index of the first occurrence of the byte in the buffer + * within the given bounds, or -1 if the byte does + * not occur. + * + * @since 4.1 + */ + public int indexOf(final byte b, final int from, final int to) { + int beginIndex = from; + if (beginIndex < 0) { + beginIndex = 0; + } + int endIndex = to; + if (endIndex > this.len) { + endIndex = this.len; + } + if (beginIndex > endIndex) { + return -1; + } + for (int i = beginIndex; i < endIndex; i++) { + if (this.buffer[i] == b) { + return i; + } + } + return -1; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified byte, starting the search at 0 and finishing + * at {@link #length()}. If no such byte occurs in this buffer within + * those bounds, -1 is returned. + * + * @param b the byte to search for. + * @return the index of the first occurrence of the byte in the + * buffer, or -1 if the byte does not occur. + * + * @since 4.1 + */ + public int indexOf(final byte b) { + return indexOf(b, 0, this.len); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharArrayBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharArrayBuffer.java new file mode 100644 index 000000000..88dc36c7a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharArrayBuffer.java @@ -0,0 +1,464 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +import java.io.Serializable; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * A resizable char array. + * + * @since 4.0 + */ +@NotThreadSafe +public final class CharArrayBuffer implements Serializable { + + private static final long serialVersionUID = -6208952725094867135L; + + private char[] buffer; + private int len; + + /** + * Creates an instance of {@link CharArrayBuffer} with the given initial + * capacity. + * + * @param capacity the capacity + */ + public CharArrayBuffer(final int capacity) { + super(); + Args.notNegative(capacity, "Buffer capacity"); + this.buffer = new char[capacity]; + } + + private void expand(final int newlen) { + final char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)]; + System.arraycopy(this.buffer, 0, newbuffer, 0, this.len); + this.buffer = newbuffer; + } + + /** + * Appends len chars to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len chars. + * + * @param b the chars to be appended. + * @param off the index of the first char to append. + * @param len the number of chars to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final char[] b, final int off, final int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + final int newlen = this.len + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + System.arraycopy(b, off, this.buffer, this.len, len); + this.len = newlen; + } + + /** + * Appends chars of the given string to this buffer. The capacity of the + * buffer is increased, if necessary, to accommodate all chars. + * + * @param str the string. + */ + public void append(final String str) { + final String s = str != null ? str : "null"; + final int strlen = s.length(); + final int newlen = this.len + strlen; + if (newlen > this.buffer.length) { + expand(newlen); + } + s.getChars(0, strlen, this.buffer, this.len); + this.len = newlen; + } + + /** + * Appends len chars to this buffer from the given source + * buffer starting at index off. The capacity of the + * destination buffer is increased, if necessary, to accommodate all + * len chars. + * + * @param b the source buffer to be appended. + * @param off the index of the first char to append. + * @param len the number of chars to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final CharArrayBuffer b, final int off, final int len) { + if (b == null) { + return; + } + append(b.buffer, off, len); + } + + /** + * Appends all chars to this buffer from the given source buffer starting + * at index 0. The capacity of the destination buffer is + * increased, if necessary, to accommodate all {@link #length()} chars. + * + * @param b the source buffer to be appended. + */ + public void append(final CharArrayBuffer b) { + if (b == null) { + return; + } + append(b.buffer,0, b.len); + } + + /** + * Appends ch char to this buffer. The capacity of the buffer + * is increased, if necessary, to accommodate the additional char. + * + * @param ch the char to be appended. + */ + public void append(final char ch) { + final int newlen = this.len + 1; + if (newlen > this.buffer.length) { + expand(newlen); + } + this.buffer[this.len] = ch; + this.len = newlen; + } + + /** + * Appends len bytes to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len bytes. + *

    + * The bytes are converted to chars using simple cast. + * + * @param b the bytes to be appended. + * @param off the index of the first byte to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final byte[] b, final int off, final int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + final int oldlen = this.len; + final int newlen = oldlen + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { + this.buffer[i2] = (char) (b[i1] & 0xff); + } + this.len = newlen; + } + + /** + * Appends len bytes to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len bytes. + *

    + * The bytes are converted to chars using simple cast. + * + * @param b the bytes to be appended. + * @param off the index of the first byte to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final ByteArrayBuffer b, final int off, final int len) { + if (b == null) { + return; + } + append(b.buffer(), off, len); + } + + /** + * Appends chars of the textual representation of the given object to this + * buffer. The capacity of the buffer is increased, if necessary, to + * accommodate all chars. + * + * @param obj the object. + */ + public void append(final Object obj) { + append(String.valueOf(obj)); + } + + /** + * Clears content of the buffer. The underlying char array is not resized. + */ + public void clear() { + this.len = 0; + } + + /** + * Converts the content of this buffer to an array of chars. + * + * @return char array + */ + public char[] toCharArray() { + final char[] b = new char[this.len]; + if (this.len > 0) { + System.arraycopy(this.buffer, 0, b, 0, this.len); + } + return b; + } + + /** + * Returns the char value in this buffer at the specified + * index. The index argument must be greater than or equal to + * 0, and less than the length of this buffer. + * + * @param i the index of the desired char value. + * @return the char value at the specified index. + * @throws IndexOutOfBoundsException if index is + * negative or greater than or equal to {@link #length()}. + */ + public char charAt(final int i) { + return this.buffer[i]; + } + + /** + * Returns reference to the underlying char array. + * + * @return the char array. + */ + public char[] buffer() { + return this.buffer; + } + + /** + * Returns the current capacity. The capacity is the amount of storage + * available for newly appended chars, beyond which an allocation will + * occur. + * + * @return the current capacity + */ + public int capacity() { + return this.buffer.length; + } + + /** + * Returns the length of the buffer (char count). + * + * @return the length of the buffer + */ + public int length() { + return this.len; + } + + /** + * Ensures that the capacity is at least equal to the specified minimum. + * If the current capacity is less than the argument, then a new internal + * array is allocated with greater capacity. If the required + * argument is non-positive, this method takes no action. + * + * @param required the minimum required capacity. + */ + public void ensureCapacity(final int required) { + if (required <= 0) { + return; + } + final int available = this.buffer.length - this.len; + if (required > available) { + expand(this.len + required); + } + } + + /** + * Sets the length of the buffer. The new length value is expected to be + * less than the current capacity and greater than or equal to + * 0. + * + * @param len the new length + * @throws IndexOutOfBoundsException if the + * len argument is greater than the current + * capacity of the buffer or less than 0. + */ + public void setLength(final int len) { + if (len < 0 || len > this.buffer.length) { + throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length); + } + this.len = len; + } + + /** + * Returns true if this buffer is empty, that is, its + * {@link #length()} is equal to 0. + * @return true if this buffer is empty, false + * otherwise. + */ + public boolean isEmpty() { + return this.len == 0; + } + + /** + * Returns true if this buffer is full, that is, its + * {@link #length()} is equal to its {@link #capacity()}. + * @return true if this buffer is full, false + * otherwise. + */ + public boolean isFull() { + return this.len == this.buffer.length; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified character, starting the search at the specified + * beginIndex and finishing at endIndex. + * If no such character occurs in this buffer within the specified bounds, + * -1 is returned. + *

    + * There is no restriction on the value of beginIndex and + * endIndex. If beginIndex is negative, + * it has the same effect as if it were zero. If endIndex is + * greater than {@link #length()}, it has the same effect as if it were + * {@link #length()}. If the beginIndex is greater than + * the endIndex, -1 is returned. + * + * @param ch the char to search for. + * @param from the index to start the search from. + * @param to the index to finish the search at. + * @return the index of the first occurrence of the character in the buffer + * within the given bounds, or -1 if the character does + * not occur. + */ + public int indexOf(final int ch, final int from, final int to) { + int beginIndex = from; + if (beginIndex < 0) { + beginIndex = 0; + } + int endIndex = to; + if (endIndex > this.len) { + endIndex = this.len; + } + if (beginIndex > endIndex) { + return -1; + } + for (int i = beginIndex; i < endIndex; i++) { + if (this.buffer[i] == ch) { + return i; + } + } + return -1; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified character, starting the search at 0 and finishing + * at {@link #length()}. If no such character occurs in this buffer within + * those bounds, -1 is returned. + * + * @param ch the char to search for. + * @return the index of the first occurrence of the character in the + * buffer, or -1 if the character does not occur. + */ + public int indexOf(final int ch) { + return indexOf(ch, 0, this.len); + } + + /** + * Returns a substring of this buffer. The substring begins at the specified + * beginIndex and extends to the character at index + * endIndex - 1. + * + * @param beginIndex the beginning index, inclusive. + * @param endIndex the ending index, exclusive. + * @return the specified substring. + * @exception StringIndexOutOfBoundsException if the + * beginIndex is negative, or + * endIndex is larger than the length of this + * buffer, or beginIndex is larger than + * endIndex. + */ + public String substring(final int beginIndex, final int endIndex) { + return new String(this.buffer, beginIndex, endIndex - beginIndex); + } + + /** + * Returns a substring of this buffer with leading and trailing whitespace + * omitted. The substring begins with the first non-whitespace character + * from beginIndex and extends to the last + * non-whitespace character with the index lesser than + * endIndex. + * + * @param from the beginning index, inclusive. + * @param to the ending index, exclusive. + * @return the specified substring. + * @exception IndexOutOfBoundsException if the + * beginIndex is negative, or + * endIndex is larger than the length of this + * buffer, or beginIndex is larger than + * endIndex. + */ + public String substringTrimmed(final int from, final int to) { + int beginIndex = from; + int endIndex = to; + if (beginIndex < 0) { + throw new IndexOutOfBoundsException("Negative beginIndex: "+beginIndex); + } + if (endIndex > this.len) { + throw new IndexOutOfBoundsException("endIndex: "+endIndex+" > length: "+this.len); + } + if (beginIndex > endIndex) { + throw new IndexOutOfBoundsException("beginIndex: "+beginIndex+" > endIndex: "+endIndex); + } + while (beginIndex < endIndex && HTTP.isWhitespace(this.buffer[beginIndex])) { + beginIndex++; + } + while (endIndex > beginIndex && HTTP.isWhitespace(this.buffer[endIndex - 1])) { + endIndex--; + } + return new String(this.buffer, beginIndex, endIndex - beginIndex); + } + + @Override + public String toString() { + return new String(this.buffer, 0, this.len); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharsetUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharsetUtils.java new file mode 100644 index 000000000..e6e75e0ad --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/CharsetUtils.java @@ -0,0 +1,58 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +public class CharsetUtils { + + public static Charset lookup(final String name) { + if (name == null) { + return null; + } + try { + return Charset.forName(name); + } catch (final UnsupportedCharsetException ex) { + return null; + } + } + + public static Charset get(final String name) throws UnsupportedEncodingException { + if (name == null) { + return null; + } + try { + return Charset.forName(name); + } catch (final UnsupportedCharsetException ex) { + throw new UnsupportedEncodingException(name); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EncodingUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EncodingUtils.java new file mode 100644 index 000000000..964553909 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EncodingUtils.java @@ -0,0 +1,152 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.util; + +import java.io.UnsupportedEncodingException; + +import ch.boye.httpclientandroidlib.Consts; + +/** + * The home for utility methods that handle various encoding tasks. + * + * + * @since 4.0 + */ +public final class EncodingUtils { + + /** + * Converts the byte array of HTTP content characters to a string. If + * the specified charset is not supported, default system encoding + * is used. + * + * @param data the byte array to be encoded + * @param offset the index of the first byte to encode + * @param length the number of bytes to encode + * @param charset the desired character encoding + * @return The result of the conversion. + */ + public static String getString( + final byte[] data, + final int offset, + final int length, + final String charset) { + Args.notNull(data, "Input"); + Args.notEmpty(charset, "Charset"); + try { + return new String(data, offset, length, charset); + } catch (final UnsupportedEncodingException e) { + return new String(data, offset, length); + } + } + + + /** + * Converts the byte array of HTTP content characters to a string. If + * the specified charset is not supported, default system encoding + * is used. + * + * @param data the byte array to be encoded + * @param charset the desired character encoding + * @return The result of the conversion. + */ + public static String getString(final byte[] data, final String charset) { + Args.notNull(data, "Input"); + return getString(data, 0, data.length, charset); + } + + /** + * Converts the specified string to a byte array. If the charset is not supported the + * default system charset is used. + * + * @param data the string to be encoded + * @param charset the desired character encoding + * @return The resulting byte array. + */ + public static byte[] getBytes(final String data, final String charset) { + Args.notNull(data, "Input"); + Args.notEmpty(charset, "Charset"); + try { + return data.getBytes(charset); + } catch (final UnsupportedEncodingException e) { + return data.getBytes(); + } + } + + /** + * Converts the specified string to byte array of ASCII characters. + * + * @param data the string to be encoded + * @return The string as a byte array. + */ + public static byte[] getAsciiBytes(final String data) { + Args.notNull(data, "Input"); + try { + return data.getBytes(Consts.ASCII.name()); + } catch (final UnsupportedEncodingException e) { + throw new Error("ASCII not supported"); + } + } + + /** + * Converts the byte array of ASCII characters to a string. This method is + * to be used when decoding content of HTTP elements (such as response + * headers) + * + * @param data the byte array to be encoded + * @param offset the index of the first byte to encode + * @param length the number of bytes to encode + * @return The string representation of the byte array + */ + public static String getAsciiString(final byte[] data, final int offset, final int length) { + Args.notNull(data, "Input"); + try { + return new String(data, offset, length, Consts.ASCII.name()); + } catch (final UnsupportedEncodingException e) { + throw new Error("ASCII not supported"); + } + } + + /** + * Converts the byte array of ASCII characters to a string. This method is + * to be used when decoding content of HTTP elements (such as response + * headers) + * + * @param data the byte array to be encoded + * @return The string representation of the byte array + */ + public static String getAsciiString(final byte[] data) { + Args.notNull(data, "Input"); + return getAsciiString(data, 0, data.length); + } + + /** + * This class should not be instantiated. + */ + private EncodingUtils() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EntityUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EntityUtils.java new file mode 100644 index 000000000..6bc19ef1e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/EntityUtils.java @@ -0,0 +1,291 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +import ch.boye.httpclientandroidlib.HeaderElement; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.ParseException; +import ch.boye.httpclientandroidlib.entity.ContentType; +import ch.boye.httpclientandroidlib.protocol.HTTP; + +/** + * Static helpers for dealing with {@link HttpEntity}s. + * + * @since 4.0 + */ +public final class EntityUtils { + + private EntityUtils() { + } + + /** + * Ensures that the entity content is fully consumed and the content stream, if exists, + * is closed. The process is done, quietly , without throwing any IOException. + * + * @param entity the entity to consume. + * + * + * @since 4.2 + */ + public static void consumeQuietly(final HttpEntity entity) { + try { + consume(entity); + } catch (final IOException ignore) { + } + } + + /** + * Ensures that the entity content is fully consumed and the content stream, if exists, + * is closed. + * + * @param entity the entity to consume. + * @throws IOException if an error occurs reading the input stream + * + * @since 4.1 + */ + public static void consume(final HttpEntity entity) throws IOException { + if (entity == null) { + return; + } + if (entity.isStreaming()) { + final InputStream instream = entity.getContent(); + if (instream != null) { + instream.close(); + } + } + } + + /** + * Updates an entity in a response by first consuming an existing entity, then setting the new one. + * + * @param response the response with an entity to update; must not be null. + * @param entity the entity to set in the response. + * @throws IOException if an error occurs while reading the input stream on the existing + * entity. + * @throws IllegalArgumentException if response is null. + * + * @since 4.3 + */ + public static void updateEntity( + final HttpResponse response, final HttpEntity entity) throws IOException { + Args.notNull(response, "Response"); + consume(response.getEntity()); + response.setEntity(entity); + } + + /** + * Read the contents of an entity and return it as a byte array. + * + * @param entity the entity to read from= + * @return byte array containing the entity content. May be null if + * {@link HttpEntity#getContent()} is null. + * @throws IOException if an error occurs reading the input stream + * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE + */ + public static byte[] toByteArray(final HttpEntity entity) throws IOException { + Args.notNull(entity, "Entity"); + final InputStream instream = entity.getContent(); + if (instream == null) { + return null; + } + try { + Args.check(entity.getContentLength() <= Integer.MAX_VALUE, + "HTTP entity too large to be buffered in memory"); + int i = (int)entity.getContentLength(); + if (i < 0) { + i = 4096; + } + final ByteArrayBuffer buffer = new ByteArrayBuffer(i); + final byte[] tmp = new byte[4096]; + int l; + while((l = instream.read(tmp)) != -1) { + buffer.append(tmp, 0, l); + } + return buffer.toByteArray(); + } finally { + instream.close(); + } + } + + /** + * Obtains character set of the entity, if known. + * + * @param entity must not be null + * @return the character set, or null if not found + * @throws ParseException if header elements cannot be parsed + * @throws IllegalArgumentException if entity is null + * + * @deprecated (4.1.3) use {@link ContentType#getOrDefault(HttpEntity)} + */ + @Deprecated + public static String getContentCharSet(final HttpEntity entity) throws ParseException { + Args.notNull(entity, "Entity"); + String charset = null; + if (entity.getContentType() != null) { + final HeaderElement values[] = entity.getContentType().getElements(); + if (values.length > 0) { + final NameValuePair param = values[0].getParameterByName("charset"); + if (param != null) { + charset = param.getValue(); + } + } + } + return charset; + } + + /** + * Obtains MIME type of the entity, if known. + * + * @param entity must not be null + * @return the character set, or null if not found + * @throws ParseException if header elements cannot be parsed + * @throws IllegalArgumentException if entity is null + * + * @since 4.1 + * + * @deprecated (4.1.3) use {@link ContentType#getOrDefault(HttpEntity)} + */ + @Deprecated + public static String getContentMimeType(final HttpEntity entity) throws ParseException { + Args.notNull(entity, "Entity"); + String mimeType = null; + if (entity.getContentType() != null) { + final HeaderElement values[] = entity.getContentType().getElements(); + if (values.length > 0) { + mimeType = values[0].getName(); + } + } + return mimeType; + } + + /** + * Get the entity content as a String, using the provided default character set + * if none is found in the entity. + * If defaultCharset is null, the default "ISO-8859-1" is used. + * + * @param entity must not be null + * @param defaultCharset character set to be applied if none found in the entity + * @return the entity content as a String. May be null if + * {@link HttpEntity#getContent()} is null. + * @throws ParseException if header elements cannot be parsed + * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE + * @throws IOException if an error occurs reading the input stream + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public static String toString( + final HttpEntity entity, final Charset defaultCharset) throws IOException, ParseException { + Args.notNull(entity, "Entity"); + final InputStream instream = entity.getContent(); + if (instream == null) { + return null; + } + try { + Args.check(entity.getContentLength() <= Integer.MAX_VALUE, + "HTTP entity too large to be buffered in memory"); + int i = (int)entity.getContentLength(); + if (i < 0) { + i = 4096; + } + Charset charset = null; + try { + final ContentType contentType = ContentType.get(entity); + if (contentType != null) { + charset = contentType.getCharset(); + } + } catch (final UnsupportedCharsetException ex) { + throw new UnsupportedEncodingException(ex.getMessage()); + } + if (charset == null) { + charset = defaultCharset; + } + if (charset == null) { + charset = HTTP.DEF_CONTENT_CHARSET; + } + final Reader reader = new InputStreamReader(instream, charset); + final CharArrayBuffer buffer = new CharArrayBuffer(i); + final char[] tmp = new char[1024]; + int l; + while((l = reader.read(tmp)) != -1) { + buffer.append(tmp, 0, l); + } + return buffer.toString(); + } finally { + instream.close(); + } + } + + /** + * Get the entity content as a String, using the provided default character set + * if none is found in the entity. + * If defaultCharset is null, the default "ISO-8859-1" is used. + * + * @param entity must not be null + * @param defaultCharset character set to be applied if none found in the entity + * @return the entity content as a String. May be null if + * {@link HttpEntity#getContent()} is null. + * @throws ParseException if header elements cannot be parsed + * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE + * @throws IOException if an error occurs reading the input stream + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public static String toString( + final HttpEntity entity, final String defaultCharset) throws IOException, ParseException { + return toString(entity, defaultCharset != null ? Charset.forName(defaultCharset) : null); + } + + /** + * Read the contents of an entity and return it as a String. + * The content is converted using the character set from the entity (if any), + * failing that, "ISO-8859-1" is used. + * + * @param entity the entity to convert to a string; must not be null + * @return String containing the content. + * @throws ParseException if header elements cannot be parsed + * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE + * @throws IOException if an error occurs reading the input stream + * @throws UnsupportedCharsetException Thrown when the named charset is not available in + * this instance of the Java virtual machine + */ + public static String toString(final HttpEntity entity) + throws IOException, ParseException { + return toString(entity, (Charset)null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ExceptionUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ExceptionUtils.java new file mode 100644 index 000000000..a0c3001ac --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/ExceptionUtils.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package ch.boye.httpclientandroidlib.util; + +import java.lang.reflect.Method; + +/** + * The home for utility methods that handle various exception-related tasks. + * + * + * @since 4.0 + * + * @deprecated (4.2) no longer used + */ +@Deprecated +public final class ExceptionUtils { + + /** A reference to Throwable's initCause method, or null if it's not there in this JVM */ + static private final Method INIT_CAUSE_METHOD = getInitCauseMethod(); + + /** + * Returns a Method allowing access to + * {@link Throwable#initCause(Throwable) initCause} method of {@link Throwable}, + * or null if the method + * does not exist. + * + * @return A Method for Throwable.initCause, or + * null if unavailable. + */ + static private Method getInitCauseMethod() { + try { + final Class[] paramsClasses = new Class[] { Throwable.class }; + return Throwable.class.getMethod("initCause", paramsClasses); + } catch (final NoSuchMethodException e) { + return null; + } + } + + /** + * If we're running on JDK 1.4 or later, initialize the cause for the given throwable. + * + * @param throwable The throwable. + * @param cause The cause of the throwable. + */ + public static void initCause(final Throwable throwable, final Throwable cause) { + if (INIT_CAUSE_METHOD != null) { + try { + INIT_CAUSE_METHOD.invoke(throwable, cause); + } catch (final Exception e) { + // Well, with no logging, the only option is to munch the exception + } + } + } + + private ExceptionUtils() { + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/LangUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/LangUtils.java new file mode 100644 index 000000000..9a4a29f34 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/LangUtils.java @@ -0,0 +1,101 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +/** + * A set of utility methods to help produce consistent + * {@link Object#equals equals} and {@link Object#hashCode hashCode} methods. + * + * + * @since 4.0 + */ +public final class LangUtils { + + public static final int HASH_SEED = 17; + public static final int HASH_OFFSET = 37; + + /** Disabled default constructor. */ + private LangUtils() { + } + + public static int hashCode(final int seed, final int hashcode) { + return seed * HASH_OFFSET + hashcode; + } + + public static int hashCode(final int seed, final boolean b) { + return hashCode(seed, b ? 1 : 0); + } + + public static int hashCode(final int seed, final Object obj) { + return hashCode(seed, obj != null ? obj.hashCode() : 0); + } + + /** + * Check if two objects are equal. + * + * @param obj1 first object to compare, may be {@code null} + * @param obj2 second object to compare, may be {@code null} + * @return {@code true} if the objects are equal or both null + */ + public static boolean equals(final Object obj1, final Object obj2) { + return obj1 == null ? obj2 == null : obj1.equals(obj2); + } + + /** + * Check if two object arrays are equal. + *

    + *

      + *
    • If both parameters are null, return {@code true}
    • + *
    • If one parameter is null, return {@code false}
    • + *
    • If the array lengths are different, return {@code false}
    • + *
    • Compare array elements using .equals(); return {@code false} if any comparisons fail.
    • + *
    • Return {@code true}
    • + *
    + * + * @param a1 first array to compare, may be {@code null} + * @param a2 second array to compare, may be {@code null} + * @return {@code true} if the arrays are equal or both null + */ + public static boolean equals(final Object[] a1, final Object[] a2) { + if (a1 == null) { + return a2 == null; + } else { + if (a2 != null && a1.length == a2.length) { + for (int i = 0; i < a1.length; i++) { + if (!equals(a1[i], a2[i])) { + return false; + } + } + return true; + } else { + return false; + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/NetUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/NetUtils.java new file mode 100644 index 000000000..8d0f4926a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/NetUtils.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +/** + * @since 4.3 + */ +public final class NetUtils { + + public static void formatAddress( + final StringBuilder buffer, + final SocketAddress socketAddress) { + Args.notNull(buffer, "Buffer"); + Args.notNull(socketAddress, "Socket address"); + if (socketAddress instanceof InetSocketAddress) { + final InetSocketAddress socketaddr = ((InetSocketAddress) socketAddress); + final InetAddress inetaddr = socketaddr.getAddress(); + buffer.append(inetaddr != null ? inetaddr.getHostAddress() : inetaddr) + .append(':').append(socketaddr.getPort()); + } else { + buffer.append(socketAddress); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/TextUtils.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/TextUtils.java new file mode 100644 index 000000000..52d8286ff --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/TextUtils.java @@ -0,0 +1,54 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +/** + * @since 4.3 + */ +public final class TextUtils { + + public static boolean isEmpty(final CharSequence s) { + if (s == null) { + return true; + } + return s.length() == 0; + } + + public static boolean isBlank(final CharSequence s) { + if (s == null) { + return true; + } + for (int i = 0; i < s.length(); i++) { + if (!Character.isWhitespace(s.charAt(i))) { + return false; + } + } + return true; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/VersionInfo.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/VersionInfo.java new file mode 100644 index 000000000..61042459a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/VersionInfo.java @@ -0,0 +1,324 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package ch.boye.httpclientandroidlib.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Provides access to version information for HTTP components. + * Static methods are used to extract version information from property + * files that are automatically packaged with HTTP component release JARs. + *
    + * All available version information is provided in strings, where + * the string format is informal and subject to change without notice. + * Version information is provided for debugging output and interpretation + * by humans, not for automated processing in applications. + * + * @since 4.0 + */ +public class VersionInfo { + + /** A string constant for unavailable information. */ + public final static String UNAVAILABLE = "UNAVAILABLE"; + + /** The filename of the version information files. */ + public final static String VERSION_PROPERTY_FILE = "version.properties"; + + // the property names + public final static String PROPERTY_MODULE = "info.module"; + public final static String PROPERTY_RELEASE = "info.release"; + public final static String PROPERTY_TIMESTAMP = "info.timestamp"; + + + /** The package that contains the version information. */ + private final String infoPackage; + + /** The module from the version info. */ + private final String infoModule; + + /** The release from the version info. */ + private final String infoRelease; + + /** The timestamp from the version info. */ + private final String infoTimestamp; + + /** The classloader from which the version info was obtained. */ + private final String infoClassloader; + + + /** + * Instantiates version information. + * + * @param pckg the package + * @param module the module, or null + * @param release the release, or null + * @param time the build time, or null + * @param clsldr the class loader, or null + */ + protected VersionInfo(final String pckg, final String module, + final String release, final String time, final String clsldr) { + Args.notNull(pckg, "Package identifier"); + infoPackage = pckg; + infoModule = (module != null) ? module : UNAVAILABLE; + infoRelease = (release != null) ? release : UNAVAILABLE; + infoTimestamp = (time != null) ? time : UNAVAILABLE; + infoClassloader = (clsldr != null) ? clsldr : UNAVAILABLE; + } + + + /** + * Obtains the package name. + * The package name identifies the module or informal unit. + * + * @return the package name, never null + */ + public final String getPackage() { + return infoPackage; + } + + /** + * Obtains the name of the versioned module or informal unit. + * This data is read from the version information for the package. + * + * @return the module name, never null + */ + public final String getModule() { + return infoModule; + } + + /** + * Obtains the release of the versioned module or informal unit. + * This data is read from the version information for the package. + * + * @return the release version, never null + */ + public final String getRelease() { + return infoRelease; + } + + /** + * Obtains the timestamp of the versioned module or informal unit. + * This data is read from the version information for the package. + * + * @return the timestamp, never null + */ + public final String getTimestamp() { + return infoTimestamp; + } + + /** + * Obtains the classloader used to read the version information. + * This is just the toString output of the classloader, + * since the version information should not keep a reference to + * the classloader itself. That could prevent garbage collection. + * + * @return the classloader description, never null + */ + public final String getClassloader() { + return infoClassloader; + } + + + /** + * Provides the version information in human-readable format. + * + * @return a string holding this version information + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder + (20 + infoPackage.length() + infoModule.length() + + infoRelease.length() + infoTimestamp.length() + + infoClassloader.length()); + + sb.append("VersionInfo(") + .append(infoPackage).append(':').append(infoModule); + + // If version info is missing, a single "UNAVAILABLE" for the module + // is sufficient. Everything else just clutters the output. + if (!UNAVAILABLE.equals(infoRelease)) { + sb.append(':').append(infoRelease); + } + if (!UNAVAILABLE.equals(infoTimestamp)) { + sb.append(':').append(infoTimestamp); + } + + sb.append(')'); + + if (!UNAVAILABLE.equals(infoClassloader)) { + sb.append('@').append(infoClassloader); + } + + return sb.toString(); + } + + + /** + * Loads version information for a list of packages. + * + * @param pckgs the packages for which to load version info + * @param clsldr the classloader to load from, or + * null for the thread context classloader + * + * @return the version information for all packages found, + * never null + */ + public static VersionInfo[] loadVersionInfo(final String[] pckgs, + final ClassLoader clsldr) { + Args.notNull(pckgs, "Package identifier array"); + final List vil = new ArrayList(pckgs.length); + for (final String pckg : pckgs) { + final VersionInfo vi = loadVersionInfo(pckg, clsldr); + if (vi != null) { + vil.add(vi); + } + } + + return vil.toArray(new VersionInfo[vil.size()]); + } + + + /** + * Loads version information for a package. + * + * @param pckg the package for which to load version information, + * for example "ch.boye.httpclientandroidlib". + * The package name should NOT end with a dot. + * @param clsldr the classloader to load from, or + * null for the thread context classloader + * + * @return the version information for the argument package, or + * null if not available + */ + public static VersionInfo loadVersionInfo(final String pckg, + final ClassLoader clsldr) { + Args.notNull(pckg, "Package identifier"); + final ClassLoader cl = clsldr != null ? clsldr : Thread.currentThread().getContextClassLoader(); + + Properties vip = null; // version info properties, if available + try { + // ch.boye.httpclientandroidlib becomes + // org/apache/http/version.properties + final InputStream is = cl.getResourceAsStream + (pckg.replace('.', '/') + "/" + VERSION_PROPERTY_FILE); + if (is != null) { + try { + final Properties props = new Properties(); + props.load(is); + vip = props; + } finally { + is.close(); + } + } + } catch (final IOException ex) { + // shamelessly munch this exception + } + + VersionInfo result = null; + if (vip != null) { + result = fromMap(pckg, vip, cl); + } + + return result; + } + + + /** + * Instantiates version information from properties. + * + * @param pckg the package for the version information + * @param info the map from string keys to string values, + * for example {@link java.util.Properties} + * @param clsldr the classloader, or null + * + * @return the version information + */ + protected static VersionInfo fromMap(final String pckg, final Map info, + final ClassLoader clsldr) { + Args.notNull(pckg, "Package identifier"); + String module = null; + String release = null; + String timestamp = null; + + if (info != null) { + module = (String) info.get(PROPERTY_MODULE); + if ((module != null) && (module.length() < 1)) { + module = null; + } + + release = (String) info.get(PROPERTY_RELEASE); + if ((release != null) && ((release.length() < 1) || + (release.equals("${pom.version}")))) { + release = null; + } + + timestamp = (String) info.get(PROPERTY_TIMESTAMP); + if ((timestamp != null) && + ((timestamp.length() < 1) || + (timestamp.equals("${mvn.timestamp}"))) + ) { + timestamp = null; + } + } // if info + + String clsldrstr = null; + if (clsldr != null) { + clsldrstr = clsldr.toString(); + } + + return new VersionInfo(pckg, module, release, timestamp, clsldrstr); + } + + /** + * Sets the user agent to {@code "/ (Java 1.5 minimum; Java/)"}. + *

    + * For example: + *

    "Apache-HttpClient/4.3 (Java 1.5 minimum; Java/1.6.0_35)"
    + * + * @param name the component name, like "Apache-HttpClient". + * @param pkg + * the package for which to load version information, for example "ch.boye.httpclientandroidlib". The package name + * should NOT end with a dot. + * @param cls + * the class' class loader to load from, or null for the thread context class loader + * @since 4.3 + */ + public static String getUserAgent(final String name, final String pkg, final Class cls) { + // determine the release version from packaged version info + final VersionInfo vi = VersionInfo.loadVersionInfo(pkg, cls.getClassLoader()); + final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; + final String javaVersion = System.getProperty("java.version"); + return name + "/" + release + " (Java 1.5 minimum; Java/" + javaVersion + ")"; + } + +} // class VersionInfo diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/package-info.java new file mode 100644 index 000000000..8cc0a0f2d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/util/package-info.java @@ -0,0 +1,31 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * Core utility classes. + */ +package ch.boye.httpclientandroidlib.util; diff --git a/mobile/android/thirdparty/com/adjust/sdk/ActivityHandler.java b/mobile/android/thirdparty/com/adjust/sdk/ActivityHandler.java new file mode 100644 index 000000000..8c7858b97 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/ActivityHandler.java @@ -0,0 +1,781 @@ +// +// ActivityHandler.java +// Adjust +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +import org.json.JSONObject; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static com.adjust.sdk.Constants.ACTIVITY_STATE_FILENAME; +import static com.adjust.sdk.Constants.ATTRIBUTION_FILENAME; +import static com.adjust.sdk.Constants.LOGTAG; + +public class ActivityHandler extends HandlerThread implements IActivityHandler { + + private static long TIMER_INTERVAL; + private static long TIMER_START; + private static long SESSION_INTERVAL; + private static long SUBSESSION_INTERVAL; + private static final String TIME_TRAVEL = "Time travel!"; + private static final String ADJUST_PREFIX = "adjust_"; + private static final String ACTIVITY_STATE_NAME = "Activity state"; + private static final String ATTRIBUTION_NAME = "Attribution"; + + private SessionHandler sessionHandler; + private IPackageHandler packageHandler; + private ActivityState activityState; + private ILogger logger; + private static ScheduledExecutorService timer; + private boolean enabled; + private boolean offline; + + private DeviceInfo deviceInfo; + private AdjustConfig adjustConfig; // always valid after construction + private AdjustAttribution attribution; + private IAttributionHandler attributionHandler; + + private ActivityHandler(AdjustConfig adjustConfig) { + super(LOGTAG, MIN_PRIORITY); + setDaemon(true); + start(); + + logger = AdjustFactory.getLogger(); + sessionHandler = new SessionHandler(getLooper(), this); + enabled = true; + init(adjustConfig); + + Message message = Message.obtain(); + message.arg1 = SessionHandler.INIT; + sessionHandler.sendMessage(message); + } + + @Override + public void init(AdjustConfig adjustConfig) { + this.adjustConfig = adjustConfig; + } + + public static ActivityHandler getInstance(AdjustConfig adjustConfig) { + if (adjustConfig == null) { + AdjustFactory.getLogger().error("AdjustConfig missing"); + return null; + } + + if (!adjustConfig.isValid()) { + AdjustFactory.getLogger().error("AdjustConfig not initialized correctly"); + return null; + } + + ActivityHandler activityHandler = new ActivityHandler(adjustConfig); + return activityHandler; + } + + @Override + public void trackSubsessionStart() { + Message message = Message.obtain(); + message.arg1 = SessionHandler.START; + sessionHandler.sendMessage(message); + } + + @Override + public void trackSubsessionEnd() { + Message message = Message.obtain(); + message.arg1 = SessionHandler.END; + sessionHandler.sendMessage(message); + } + + @Override + public void trackEvent(AdjustEvent event) { + Message message = Message.obtain(); + message.arg1 = SessionHandler.EVENT; + message.obj = event; + sessionHandler.sendMessage(message); + } + + @Override + public void finishedTrackingActivity(JSONObject jsonResponse) { + if (jsonResponse == null) { + return; + } + + Message message = Message.obtain(); + message.arg1 = SessionHandler.FINISH_TRACKING; + message.obj = jsonResponse; + sessionHandler.sendMessage(message); + } + + @Override + public void setEnabled(boolean enabled) { + if (enabled == this.enabled) { + if (enabled) { + logger.debug("Adjust already enabled"); + } else { + logger.debug("Adjust already disabled"); + } + return; + } + this.enabled = enabled; + if (activityState != null) { + activityState.enabled = enabled; + } + if (enabled) { + if (toPause()) { + logger.info("Package and attribution handler remain paused due to the SDK is offline"); + } else { + logger.info("Resuming package handler and attribution handler to enabled the SDK"); + } + trackSubsessionStart(); + } else { + logger.info("Pausing package handler and attribution handler to disable the SDK"); + trackSubsessionEnd(); + } + } + + @Override + public void setOfflineMode(boolean offline) { + if (offline == this.offline) { + if (offline) { + logger.debug("Adjust already in offline mode"); + } else { + logger.debug("Adjust already in online mode"); + } + return; + } + this.offline = offline; + if (offline) { + logger.info("Pausing package and attribution handler to put in offline mode"); + } else { + if (toPause()) { + logger.info("Package and attribution handler remain paused because the SDK is disabled"); + } else { + logger.info("Resuming package handler and attribution handler to put in online mode"); + } + } + updateStatus(); + } + + @Override + public boolean isEnabled() { + if (activityState != null) { + return activityState.enabled; + } else { + return enabled; + } + } + + @Override + public void readOpenUrl(Uri url, long clickTime) { + Message message = Message.obtain(); + message.arg1 = SessionHandler.DEEP_LINK; + UrlClickTime urlClickTime = new UrlClickTime(url, clickTime); + message.obj = urlClickTime; + sessionHandler.sendMessage(message); + } + + @Override + public boolean tryUpdateAttribution(AdjustAttribution attribution) { + if (attribution == null) return false; + + if (attribution.equals(this.attribution)) { + return false; + } + + saveAttribution(attribution); + launchAttributionListener(); + return true; + } + + private void saveAttribution(AdjustAttribution attribution) { + this.attribution = attribution; + writeAttribution(); + } + + private void launchAttributionListener() { + if (adjustConfig.onAttributionChangedListener == null) { + return; + } + Handler handler = new Handler(adjustConfig.context.getMainLooper()); + Runnable runnable = new Runnable() { + @Override + public void run() { + adjustConfig.onAttributionChangedListener.onAttributionChanged(attribution); + } + }; + handler.post(runnable); + } + + @Override + public void setAskingAttribution(boolean askingAttribution) { + activityState.askingAttribution = askingAttribution; + writeActivityState(); + } + + @Override + public ActivityPackage getAttributionPackage() { + long now = System.currentTimeMillis(); + PackageBuilder attributionBuilder = new PackageBuilder(adjustConfig, + deviceInfo, + activityState, + now); + return attributionBuilder.buildAttributionPackage(); + } + + @Override + public void sendReferrer(String referrer, long clickTime) { + Message message = Message.obtain(); + message.arg1 = SessionHandler.SEND_REFERRER; + ReferrerClickTime referrerClickTime = new ReferrerClickTime(referrer, clickTime); + message.obj = referrerClickTime; + sessionHandler.sendMessage(message); + } + + private class UrlClickTime { + Uri url; + long clickTime; + + UrlClickTime(Uri url, long clickTime) { + this.url = url; + this.clickTime = clickTime; + } + } + + private class ReferrerClickTime { + String referrer; + long clickTime; + + ReferrerClickTime(String referrer, long clickTime) { + this.referrer = referrer; + this.clickTime = clickTime; + } + } + + private void updateStatus() { + Message message = Message.obtain(); + message.arg1 = SessionHandler.UPDATE_STATUS; + sessionHandler.sendMessage(message); + } + + private static final class SessionHandler extends Handler { + private static final int BASE_ADDRESS = 72630; + private static final int INIT = BASE_ADDRESS + 1; + private static final int START = BASE_ADDRESS + 2; + private static final int END = BASE_ADDRESS + 3; + private static final int EVENT = BASE_ADDRESS + 4; + private static final int FINISH_TRACKING = BASE_ADDRESS + 5; + private static final int DEEP_LINK = BASE_ADDRESS + 6; + private static final int SEND_REFERRER = BASE_ADDRESS + 7; + private static final int UPDATE_STATUS = BASE_ADDRESS + 8; + + private final WeakReference sessionHandlerReference; + + protected SessionHandler(Looper looper, ActivityHandler sessionHandler) { + super(looper); + this.sessionHandlerReference = new WeakReference(sessionHandler); + } + + @Override + public void handleMessage(Message message) { + super.handleMessage(message); + + ActivityHandler sessionHandler = sessionHandlerReference.get(); + if (sessionHandler == null) { + return; + } + + switch (message.arg1) { + case INIT: + sessionHandler.initInternal(); + break; + case START: + sessionHandler.startInternal(); + break; + case END: + sessionHandler.endInternal(); + break; + case EVENT: + AdjustEvent event = (AdjustEvent) message.obj; + sessionHandler.trackEventInternal(event); + break; + case FINISH_TRACKING: + JSONObject jsonResponse = (JSONObject) message.obj; + sessionHandler.finishedTrackingActivityInternal(jsonResponse); + break; + case DEEP_LINK: + UrlClickTime urlClickTime = (UrlClickTime) message.obj; + sessionHandler.readOpenUrlInternal(urlClickTime.url, urlClickTime.clickTime); + break; + case SEND_REFERRER: + ReferrerClickTime referrerClickTime = (ReferrerClickTime) message.obj; + sessionHandler.sendReferrerInternal(referrerClickTime.referrer, referrerClickTime.clickTime); + break; + case UPDATE_STATUS: + sessionHandler.updateStatusInternal(); + break; + } + } + } + + private void initInternal() { + TIMER_INTERVAL = AdjustFactory.getTimerInterval(); + TIMER_START = AdjustFactory.getTimerStart(); + SESSION_INTERVAL = AdjustFactory.getSessionInterval(); + SUBSESSION_INTERVAL = AdjustFactory.getSubsessionInterval(); + + deviceInfo = new DeviceInfo(adjustConfig.context, adjustConfig.sdkPrefix); + + if (adjustConfig.environment == AdjustConfig.ENVIRONMENT_PRODUCTION) { + logger.setLogLevel(LogLevel.ASSERT); + } else { + logger.setLogLevel(adjustConfig.logLevel); + } + + if (adjustConfig.eventBufferingEnabled) { + logger.info("Event buffering is enabled"); + } + + String playAdId = Util.getPlayAdId(adjustConfig.context); + if (playAdId == null) { + logger.info("Unable to get Google Play Services Advertising ID at start time"); + } + + if (adjustConfig.defaultTracker != null) { + logger.info("Default tracker: '%s'", adjustConfig.defaultTracker); + } + + if (adjustConfig.referrer != null) { + sendReferrer(adjustConfig.referrer, adjustConfig.referrerClickTime); // send to background queue to make sure that activityState is valid + } + + readAttribution(); + readActivityState(); + + packageHandler = AdjustFactory.getPackageHandler(this, adjustConfig.context, toPause()); + + startInternal(); + } + + private void startInternal() { + // it shouldn't start if it was disabled after a first session + if (activityState != null + && !activityState.enabled) { + return; + } + + updateStatusInternal(); + + processSession(); + + checkAttributionState(); + + startTimer(); + } + + private void processSession() { + long now = System.currentTimeMillis(); + + // very first session + if (activityState == null) { + activityState = new ActivityState(); + activityState.sessionCount = 1; // this is the first session + + transferSessionPackage(now); + activityState.resetSessionAttributes(now); + activityState.enabled = this.enabled; + writeActivityState(); + return; + } + + long lastInterval = now - activityState.lastActivity; + + if (lastInterval < 0) { + logger.error(TIME_TRAVEL); + activityState.lastActivity = now; + writeActivityState(); + return; + } + + // new session + if (lastInterval > SESSION_INTERVAL) { + activityState.sessionCount++; + activityState.lastInterval = lastInterval; + + transferSessionPackage(now); + activityState.resetSessionAttributes(now); + writeActivityState(); + return; + } + + // new subsession + if (lastInterval > SUBSESSION_INTERVAL) { + activityState.subsessionCount++; + activityState.sessionLength += lastInterval; + activityState.lastActivity = now; + writeActivityState(); + logger.info("Started subsession %d of session %d", + activityState.subsessionCount, + activityState.sessionCount); + } + } + + private void checkAttributionState() { + // if there is no attribution saved, or there is one being asked + if (attribution == null || activityState.askingAttribution) { + getAttributionHandler().getAttribution(); + } + } + + private void endInternal() { + packageHandler.pauseSending(); + getAttributionHandler().pauseSending(); + stopTimer(); + if (updateActivityState(System.currentTimeMillis())) { + writeActivityState(); + } + } + + private void trackEventInternal(AdjustEvent event) { + if (!checkEvent(event)) return; + if (!activityState.enabled) return; + + long now = System.currentTimeMillis(); + + activityState.eventCount++; + updateActivityState(now); + + PackageBuilder eventBuilder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now); + ActivityPackage eventPackage = eventBuilder.buildEventPackage(event); + packageHandler.addPackage(eventPackage); + + if (adjustConfig.eventBufferingEnabled) { + logger.info("Buffered event %s", eventPackage.getSuffix()); + } else { + packageHandler.sendFirstPackage(); + } + + writeActivityState(); + } + + private void finishedTrackingActivityInternal(JSONObject jsonResponse) { + if (jsonResponse == null) { + return; + } + + String deeplink = jsonResponse.optString("deeplink", null); + launchDeeplinkMain(deeplink); + getAttributionHandler().checkAttribution(jsonResponse); + } + + private void sendReferrerInternal(String referrer, long clickTime) { + ActivityPackage clickPackage = buildQueryStringClickPackage(referrer, + "reftag", + clickTime); + if (clickPackage == null) { + return; + } + + getAttributionHandler().getAttribution(); + + packageHandler.sendClickPackage(clickPackage); + } + + private void readOpenUrlInternal(Uri url, long clickTime) { + if (url == null) { + return; + } + + String queryString = url.getQuery(); + + ActivityPackage clickPackage = buildQueryStringClickPackage(queryString, "deeplink", clickTime); + if (clickPackage == null) { + return; + } + + getAttributionHandler().getAttribution(); + + packageHandler.sendClickPackage(clickPackage); + } + + private ActivityPackage buildQueryStringClickPackage(String queryString, String source, long clickTime) { + if (queryString == null) { + return null; + } + + long now = System.currentTimeMillis(); + Map queryStringParameters = new HashMap(); + AdjustAttribution queryStringAttribution = new AdjustAttribution(); + boolean hasAdjustTags = false; + + String[] queryPairs = queryString.split("&"); + for (String pair : queryPairs) { + if (readQueryString(pair, queryStringParameters, queryStringAttribution)) { + hasAdjustTags = true; + } + } + + if (!hasAdjustTags) { + return null; + } + + String reftag = queryStringParameters.remove("reftag"); + + PackageBuilder builder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now); + builder.extraParameters = queryStringParameters; + builder.attribution = queryStringAttribution; + builder.reftag = reftag; + ActivityPackage clickPackage = builder.buildClickPackage(source, clickTime); + return clickPackage; + } + + private boolean readQueryString(String queryString, + Map extraParameters, + AdjustAttribution queryStringAttribution) { + String[] pairComponents = queryString.split("="); + if (pairComponents.length != 2) return false; + + String key = pairComponents[0]; + if (!key.startsWith(ADJUST_PREFIX)) return false; + + String value = pairComponents[1]; + if (value.length() == 0) return false; + + String keyWOutPrefix = key.substring(ADJUST_PREFIX.length()); + if (keyWOutPrefix.length() == 0) return false; + + if (!trySetAttribution(queryStringAttribution, keyWOutPrefix, value)) { + extraParameters.put(keyWOutPrefix, value); + } + + return true; + } + + private boolean trySetAttribution(AdjustAttribution queryStringAttribution, + String key, + String value) { + if (key.equals("tracker")) { + queryStringAttribution.trackerName = value; + return true; + } + + if (key.equals("campaign")) { + queryStringAttribution.campaign = value; + return true; + } + + if (key.equals("adgroup")) { + queryStringAttribution.adgroup = value; + return true; + } + + if (key.equals("creative")) { + queryStringAttribution.creative = value; + return true; + } + + return false; + } + + private void updateStatusInternal() { + updateAttributionHandlerStatus(); + updatePackageHandlerStatus(); + } + + private void updateAttributionHandlerStatus() { + if (attributionHandler == null) { + return; + } + if (toPause()) { + attributionHandler.pauseSending(); + } else { + attributionHandler.resumeSending(); + } + } + + private void updatePackageHandlerStatus() { + if (packageHandler == null) { + return; + } + if (toPause()) { + packageHandler.pauseSending(); + } else { + packageHandler.resumeSending(); + } + } + + private void launchDeeplinkMain(String deeplink) { + if (deeplink == null) return; + + Uri location = Uri.parse(deeplink); + Intent mapIntent = new Intent(Intent.ACTION_VIEW, location); + mapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Verify it resolves + PackageManager packageManager = adjustConfig.context.getPackageManager(); + List activities = packageManager.queryIntentActivities(mapIntent, 0); + boolean isIntentSafe = activities.size() > 0; + + // Start an activity if it's safe + if (!isIntentSafe) { + logger.error("Unable to open deep link (%s)", deeplink); + return; + } + + logger.info("Open deep link (%s)", deeplink); + adjustConfig.context.startActivity(mapIntent); + } + + private boolean updateActivityState(long now) { + long lastInterval = now - activityState.lastActivity; + // ignore late updates + if (lastInterval > SESSION_INTERVAL) { + return false; + } + activityState.lastActivity = now; + + if (lastInterval < 0) { + logger.error(TIME_TRAVEL); + } else { + activityState.sessionLength += lastInterval; + activityState.timeSpent += lastInterval; + } + return true; + } + + public static boolean deleteActivityState(Context context) { + return context.deleteFile(ACTIVITY_STATE_FILENAME); + } + + public static boolean deleteAttribution(Context context) { + return context.deleteFile(ATTRIBUTION_FILENAME); + } + + private void transferSessionPackage(long now) { + PackageBuilder builder = new PackageBuilder(adjustConfig, deviceInfo, activityState, now); + ActivityPackage sessionPackage = builder.buildSessionPackage(); + packageHandler.addPackage(sessionPackage); + packageHandler.sendFirstPackage(); + } + + private void startTimer() { + stopTimer(); + + if (!activityState.enabled) { + return; + } + timer = Executors.newSingleThreadScheduledExecutor(); + timer.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + timerFired(); + } + }, TIMER_START, TIMER_INTERVAL, TimeUnit.MILLISECONDS); + } + + private void stopTimer() { + if (timer != null) { + timer.shutdown(); + timer = null; + } + } + + private void timerFired() { + if (!activityState.enabled) { + stopTimer(); + return; + } + + packageHandler.sendFirstPackage(); + + if (updateActivityState(System.currentTimeMillis())) { + writeActivityState(); + } + } + + private void readActivityState() { + try { + /** + * Mozilla: + * readObject is a generic object, and can therefore return arbitrary generic objects + * that might not match the expected type. Therefore there will be an implicit cast + * here, which can fail. Therefore we have to add the catch (ClassCastException) + * Note: this has been fixed in upstream, we only need this for the version we are still shipping. + */ + activityState = Util.readObject(adjustConfig.context, ACTIVITY_STATE_FILENAME, ACTIVITY_STATE_NAME); + } catch (ClassCastException e) { + activityState = null; + } + } + + private void readAttribution() { + try { + /** + * Mozilla: (same as in readActivityState() ) + * readObject is a generic object, and can therefore return arbitrary generic objects + * that might not match the expected type. Therefore there will be an implicit cast + * here, which can fail. Therefore we have to add the catch (ClassCastException) + * Note: this has been fixed in upstream, we only need this for the version we are still shipping. + */ + attribution = Util.readObject(adjustConfig.context, ATTRIBUTION_FILENAME, ATTRIBUTION_NAME); + } catch (ClassCastException e) { + activityState = null; + } + } + + private void writeActivityState() { + Util.writeObject(activityState, adjustConfig.context, ACTIVITY_STATE_FILENAME, ACTIVITY_STATE_NAME); + } + + private void writeAttribution() { + Util.writeObject(attribution, adjustConfig.context, ATTRIBUTION_FILENAME, ATTRIBUTION_NAME); + } + + private boolean checkEvent(AdjustEvent event) { + if (event == null) { + logger.error("Event missing"); + return false; + } + + if (!event.isValid()) { + logger.error("Event not initialized correctly"); + return false; + } + + return true; + } + + // lazy initialization to prevent null activity state before first session + private IAttributionHandler getAttributionHandler() { + if (attributionHandler == null) { + ActivityPackage attributionPackage = getAttributionPackage(); + attributionHandler = AdjustFactory.getAttributionHandler(this, + attributionPackage, + toPause()); + } + return attributionHandler; + } + + private boolean toPause() { + return offline || !isEnabled(); + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/ActivityKind.java b/mobile/android/thirdparty/com/adjust/sdk/ActivityKind.java new file mode 100644 index 000000000..a255b83a9 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/ActivityKind.java @@ -0,0 +1,35 @@ +package com.adjust.sdk; + +public enum ActivityKind { + UNKNOWN, SESSION, EVENT, CLICK, ATTRIBUTION; + + public static ActivityKind fromString(String string) { + if ("session".equals(string)) { + return SESSION; + } else if ("event".equals(string)) { + return EVENT; + } else if ("click".equals(string)) { + return CLICK; + } else if ("attribution".equals(string)) { + return ATTRIBUTION; + } else { + return UNKNOWN; + } + } + + @Override + public String toString() { + switch (this) { + case SESSION: + return "session"; + case EVENT: + return "event"; + case CLICK: + return "click"; + case ATTRIBUTION: + return "attribution"; + default: + return "unknown"; + } + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/ActivityPackage.java b/mobile/android/thirdparty/com/adjust/sdk/ActivityPackage.java new file mode 100644 index 000000000..27ab969fd --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/ActivityPackage.java @@ -0,0 +1,100 @@ +// +// ActivityPackage.java +// Adjust +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import java.io.Serializable; +import java.util.Map; + +public class ActivityPackage implements Serializable { + private static final long serialVersionUID = -35935556512024097L; + + // data + private String path; + private String clientSdk; + private Map parameters; + + // logs + private ActivityKind activityKind; + private String suffix; + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getClientSdk() { + return clientSdk; + } + + public void setClientSdk(String clientSdk) { + this.clientSdk = clientSdk; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public ActivityKind getActivityKind() { + return activityKind; + } + + public void setActivityKind(ActivityKind activityKind) { + this.activityKind = activityKind; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public String toString() { + return String.format("%s%s", activityKind.toString(), suffix); + } + + public String getExtendedString() { + StringBuilder builder = new StringBuilder(); + builder.append(String.format("Path: %s\n", path)); + builder.append(String.format("ClientSdk: %s\n", clientSdk)); + + if (parameters != null) { + builder.append("Parameters:"); + for (Map.Entry entry : parameters.entrySet()) { + builder.append(String.format("\n\t%-16s %s", entry.getKey(), entry.getValue())); + } + } + return builder.toString(); + } + + protected String getSuccessMessage() { + try { + return String.format("Tracked %s%s", activityKind.toString(), suffix); + } catch (NullPointerException e) { + return "Tracked ???"; + } + } + + protected String getFailureMessage() { + try { + return String.format("Failed to track %s%s", activityKind.toString(), suffix); + } catch (NullPointerException e) { + return "Failed to track ???"; + } + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/ActivityState.java b/mobile/android/thirdparty/com/adjust/sdk/ActivityState.java new file mode 100644 index 000000000..41ad2ca3b --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/ActivityState.java @@ -0,0 +1,151 @@ +// +// ActivityState.java +// Adjust +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectInputStream.GetField; +import java.io.Serializable; +import java.util.Calendar; +import java.util.Locale; + +public class ActivityState implements Serializable, Cloneable { + private static final long serialVersionUID = 9039439291143138148L; + private transient String readErrorMessage = "Unable to read '%s' field in migration device with message (%s)"; + private transient ILogger logger; + + // persistent data + protected String uuid; + protected boolean enabled; + protected boolean askingAttribution; + + // global counters + protected int eventCount; + protected int sessionCount; + + // session attributes + protected int subsessionCount; + protected long sessionLength; // all durations in milliseconds + protected long timeSpent; + protected long lastActivity; // all times in milliseconds since 1970 + + protected long lastInterval; + + protected ActivityState() { + logger = AdjustFactory.getLogger(); + // create UUID for new devices + uuid = Util.createUuid(); + enabled = true; + askingAttribution = false; + + eventCount = 0; // no events yet + sessionCount = 0; // the first session just started + subsessionCount = -1; // we don't know how many subsessions this first session will have + sessionLength = -1; // same for session length and time spent + timeSpent = -1; // this information will be collected and attached to the next session + lastActivity = -1; + lastInterval = -1; + } + + protected void resetSessionAttributes(long now) { + subsessionCount = 1; // first subsession + sessionLength = 0; // no session length yet + timeSpent = 0; // no time spent yet + lastActivity = now; + lastInterval = -1; + } + + @Override + public String toString() { + return String.format(Locale.US, + "ec:%d sc:%d ssc:%d sl:%.1f ts:%.1f la:%s uuid:%s", + eventCount, sessionCount, subsessionCount, + sessionLength / 1000.0, timeSpent / 1000.0, + stamp(lastActivity), uuid); + } + + @Override + public ActivityState clone() { + try { + return (ActivityState) super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + GetField fields = stream.readFields(); + + eventCount = readIntField(fields, "eventCount", 0); + sessionCount = readIntField(fields, "sessionCount", 0); + subsessionCount = readIntField(fields, "subsessionCount", -1); + sessionLength = readLongField(fields, "sessionLength", -1l); + timeSpent = readLongField(fields, "timeSpent", -1l); + lastActivity = readLongField(fields, "lastActivity", -1l); + lastInterval = readLongField(fields, "lastInterval", -1l); + + // new fields + uuid = readStringField(fields, "uuid", null); + enabled = readBooleanField(fields, "enabled", true); + askingAttribution = readBooleanField(fields, "askingAttribution", false); + + // create UUID for migrating devices + if (uuid == null) { + uuid = Util.createUuid(); + } + } + + private String readStringField(GetField fields, String name, String defaultValue) { + try { + return (String) fields.get(name, defaultValue); + } catch (Exception e) { + logger.debug(readErrorMessage, name, e.getMessage()); + return defaultValue; + } + } + + private boolean readBooleanField(GetField fields, String name, boolean defaultValue) { + try { + return fields.get(name, defaultValue); + } catch (Exception e) { + logger.debug(readErrorMessage, name, e.getMessage()); + return defaultValue; + } + } + + private int readIntField(GetField fields, String name, int defaultValue) { + try { + return fields.get(name, defaultValue); + } catch (Exception e) { + logger.debug(readErrorMessage, name, e.getMessage()); + return defaultValue; + } + } + + private long readLongField(GetField fields, String name, long defaultValue) { + try { + return fields.get(name, defaultValue); + } catch (Exception e) { + logger.debug(readErrorMessage, name, e.getMessage()); + return defaultValue; + } + } + + private static String stamp(long dateMillis) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(dateMillis); + return String.format(Locale.US, + "%02d:%02d:%02d", + calendar.HOUR_OF_DAY, + calendar.MINUTE, + calendar.SECOND); + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/Adjust.java b/mobile/android/thirdparty/com/adjust/sdk/Adjust.java new file mode 100644 index 000000000..3b81a077b --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/Adjust.java @@ -0,0 +1,79 @@ +// +// Adjust.java +// Adjust +// +// Created by Christian Wellenbrock on 2012-10-11. +// Copyright (c) 2012-2014 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import android.net.Uri; + +/** + * The main interface to Adjust. + * Use the methods of this class to tell Adjust about the usage of your app. + * See the README for details. + */ +public class Adjust { + + private static AdjustInstance defaultInstance; + + private Adjust() { + } + + public static synchronized AdjustInstance getDefaultInstance() { + if (defaultInstance == null) { + defaultInstance = new AdjustInstance(); + } + return defaultInstance; + } + + public static void onCreate(AdjustConfig adjustConfig) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.onCreate(adjustConfig); + } + + public static void trackEvent(AdjustEvent event) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.trackEvent(event); + } + + public static void onResume() { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.onResume(); + } + + public static void onPause() { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.onPause(); + } + + public static void setEnabled(boolean enabled) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.setEnabled(enabled); + } + + public static boolean isEnabled() { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + return adjustInstance.isEnabled(); + } + + public static void appWillOpenUrl(Uri url) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.appWillOpenUrl(url); + } + + public static void setReferrer(String referrer) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.sendReferrer(referrer); + } + + public static void setOfflineMode(boolean enabled) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.setOfflineMode(enabled); + } +} + + diff --git a/mobile/android/thirdparty/com/adjust/sdk/AdjustAttribution.java b/mobile/android/thirdparty/com/adjust/sdk/AdjustAttribution.java new file mode 100644 index 000000000..4e3abb017 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/AdjustAttribution.java @@ -0,0 +1,62 @@ +package com.adjust.sdk; + +import org.json.JSONObject; + +import java.io.Serializable; + +/** + * Created by pfms on 07/11/14. + */ +public class AdjustAttribution implements Serializable { + private static final long serialVersionUID = 1L; + + public String trackerToken; + public String trackerName; + public String network; + public String campaign; + public String adgroup; + public String creative; + + public static AdjustAttribution fromJson(JSONObject jsonObject) { + if (jsonObject == null) return null; + + AdjustAttribution attribution = new AdjustAttribution(); + + attribution.trackerToken = jsonObject.optString("tracker_token", null); + attribution.trackerName = jsonObject.optString("tracker_name", null); + attribution.network = jsonObject.optString("network", null); + attribution.campaign = jsonObject.optString("campaign", null); + attribution.adgroup = jsonObject.optString("adgroup", null); + attribution.creative = jsonObject.optString("creative", null); + + return attribution; + } + + public boolean equals(Object other) { + if (other == this) return true; + if (other == null) return false; + if (getClass() != other.getClass()) return false; + AdjustAttribution otherAttribution = (AdjustAttribution) other; + + if (!equalString(trackerToken, otherAttribution.trackerToken)) return false; + if (!equalString(trackerName, otherAttribution.trackerName)) return false; + if (!equalString(network, otherAttribution.network)) return false; + if (!equalString(campaign, otherAttribution.campaign)) return false; + if (!equalString(adgroup, otherAttribution.adgroup)) return false; + if (!equalString(creative, otherAttribution.creative)) return false; + return true; + } + + private boolean equalString(String first, String second) { + if (first == null || second == null) { + return first == null && second == null; + } + return first.equals(second); + } + + @Override + public String toString() { + return String.format("tt:%s tn:%s net:%s cam:%s adg:%s cre:%s", + trackerToken, trackerName, network, campaign, adgroup, creative); + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/AdjustConfig.java b/mobile/android/thirdparty/com/adjust/sdk/AdjustConfig.java new file mode 100644 index 000000000..148a5f670 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/AdjustConfig.java @@ -0,0 +1,128 @@ +package com.adjust.sdk; + +import android.content.Context; + +/** + * Created by pfms on 06/11/14. + */ +public class AdjustConfig { + Context context; + String appToken; + String environment; + LogLevel logLevel; + String sdkPrefix; + Boolean eventBufferingEnabled; + String defaultTracker; + OnAttributionChangedListener onAttributionChangedListener; + String referrer; + long referrerClickTime; + Boolean knownDevice; + + public static final String ENVIRONMENT_SANDBOX = "sandbox"; + public static final String ENVIRONMENT_PRODUCTION = "production"; + + public AdjustConfig(Context context, String appToken, String environment) { + if (!isValid(context, appToken, environment)) { + return; + } + + this.context = context.getApplicationContext(); + this.appToken = appToken; + this.environment = environment; + + // default values + this.logLevel = LogLevel.INFO; + this.eventBufferingEnabled = false; + } + + public void setEventBufferingEnabled(Boolean eventBufferingEnabled) { + this.eventBufferingEnabled = eventBufferingEnabled; + } + + public void setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; + } + + public void setSdkPrefix(String sdkPrefix) { + this.sdkPrefix = sdkPrefix; + } + + public void setDefaultTracker(String defaultTracker) { + this.defaultTracker = defaultTracker; + } + + public void setOnAttributionChangedListener(OnAttributionChangedListener onAttributionChangedListener) { + this.onAttributionChangedListener = onAttributionChangedListener; + } + + public boolean hasListener() { + return onAttributionChangedListener != null; + } + + public boolean isValid() { + return appToken != null; + } + + private boolean isValid(Context context, String appToken, String environment) { + if (!checkAppToken(appToken)) return false; + if (!checkEnvironment(environment)) return false; + if (!checkContext(context)) return false; + + return true; + } + + private static boolean checkContext(Context context) { + ILogger logger = AdjustFactory.getLogger(); + if (context == null) { + logger.error("Missing context"); + return false; + } + + if (!Util.checkPermission(context, android.Manifest.permission.INTERNET)) { + logger.error("Missing permission: INTERNET"); + return false; + } + + return true; + } + + private static boolean checkAppToken(String appToken) { + ILogger logger = AdjustFactory.getLogger(); + if (appToken == null) { + logger.error("Missing App Token."); + return false; + } + + if (appToken.length() != 12) { + logger.error("Malformed App Token '%s'", appToken); + return false; + } + + return true; + } + + private static boolean checkEnvironment(String environment) { + ILogger logger = AdjustFactory.getLogger(); + if (environment == null) { + logger.error("Missing environment"); + return false; + } + + if (environment == AdjustConfig.ENVIRONMENT_SANDBOX) { + logger.Assert("SANDBOX: Adjust is running in Sandbox mode. " + + "Use this setting for testing. " + + "Don't forget to set the environment to `production` before publishing!"); + return true; + } + if (environment == AdjustConfig.ENVIRONMENT_PRODUCTION) { + logger.Assert( + "PRODUCTION: Adjust is running in Production mode. " + + "Use this setting only for the build that you want to publish. " + + "Set the environment to `sandbox` if you want to test your app!"); + return true; + } + + logger.error("Unknown environment '%s'", environment); + return false; + } +} \ No newline at end of file diff --git a/mobile/android/thirdparty/com/adjust/sdk/AdjustEvent.java b/mobile/android/thirdparty/com/adjust/sdk/AdjustEvent.java new file mode 100644 index 000000000..f03718183 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/AdjustEvent.java @@ -0,0 +1,112 @@ +package com.adjust.sdk; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by pfms on 05/11/14. + */ +public class AdjustEvent { + String eventToken; + Double revenue; + String currency; + Map callbackParameters; + Map partnerParameters; + + private static ILogger logger = AdjustFactory.getLogger(); + + public AdjustEvent(String eventToken) { + if (!checkEventToken(eventToken, logger)) return; + + this.eventToken = eventToken; + } + + public void setRevenue(double revenue, String currency) { + if (!checkRevenue(revenue, currency)) return; + + this.revenue = revenue; + this.currency = currency; + } + + public void addCallbackParameter(String key, String value) { + if (!isValidParameter(key, "key", "Callback")) return; + if (!isValidParameter(value, "value", "Callback")) return; + + if (callbackParameters == null) { + callbackParameters = new HashMap(); + } + + String previousValue = callbackParameters.put(key, value); + + if (previousValue != null) { + logger.warn("key %s was overwritten", key); + } + } + + public void addPartnerParameter(String key, String value) { + if (!isValidParameter(key, "key", "Partner")) return; + if (!isValidParameter(value, "value", "Partner")) return; + + if (partnerParameters == null) { + partnerParameters = new HashMap(); + } + + String previousValue = partnerParameters.put(key, value); + + if (previousValue != null) { + logger.warn("key %s was overwritten", key); + } + } + + public boolean isValid() { + return eventToken != null; + } + + private static boolean checkEventToken(String eventToken, ILogger logger) { + if (eventToken == null) { + logger.error("Missing Event Token"); + return false; + } + if (eventToken.length() != 6) { + logger.error("Malformed Event Token '%s'", eventToken); + return false; + } + return true; + } + + private boolean checkRevenue(Double revenue, String currency) { + if (revenue != null) { + if (revenue < 0.0) { + logger.error("Invalid amount %.4f", revenue); + return false; + } + + if (currency == null) { + logger.error("Currency must be set with revenue"); + return false; + } + if (currency == "") { + logger.error("Currency is empty"); + return false; + } + + } else if (currency != null) { + logger.error("Revenue must be set with currency"); + return false; + } + return true; + } + + private boolean isValidParameter(String attribute, String attributeType, String parameterName) { + if (attribute == null) { + logger.error("%s parameter %s is missing", parameterName, attributeType); + return false; + } + if (attribute == "") { + logger.error("%s parameter %s is empty", parameterName, attributeType); + return false; + } + + return true; + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/AdjustFactory.java b/mobile/android/thirdparty/com/adjust/sdk/AdjustFactory.java new file mode 100644 index 000000000..802af6416 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/AdjustFactory.java @@ -0,0 +1,141 @@ +package com.adjust.sdk; + +import android.content.Context; + +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient; +import ch.boye.httpclientandroidlib.params.HttpParams; + +public class AdjustFactory { + private static IPackageHandler packageHandler = null; + private static IRequestHandler requestHandler = null; + private static IAttributionHandler attributionHandler = null; + private static IActivityHandler activityHandler = null; + private static ILogger logger = null; + private static HttpClient httpClient = null; + + private static long timerInterval = -1; + private static long timerStart = -1; + private static long sessionInterval = -1; + private static long subsessionInterval = -1; + + public static IPackageHandler getPackageHandler(ActivityHandler activityHandler, + Context context, + boolean startPaused) { + if (packageHandler == null) { + return new PackageHandler(activityHandler, context, startPaused); + } + packageHandler.init(activityHandler, context, startPaused); + return packageHandler; + } + + public static IRequestHandler getRequestHandler(IPackageHandler packageHandler) { + if (requestHandler == null) { + return new RequestHandler(packageHandler); + } + requestHandler.init(packageHandler); + return requestHandler; + } + + public static ILogger getLogger() { + if (logger == null) { + // Logger needs to be "static" to retain the configuration throughout the app + logger = new Logger(); + } + return logger; + } + + public static HttpClient getHttpClient(HttpParams params) { + if (httpClient == null) { + return new DefaultHttpClient(params); + } + return httpClient; + } + + public static long getTimerInterval() { + if (timerInterval == -1) { + return Constants.ONE_MINUTE; + } + return timerInterval; + } + + public static long getTimerStart() { + if (timerStart == -1) { + return 0; + } + return timerStart; + } + + public static long getSessionInterval() { + if (sessionInterval == -1) { + return Constants.THIRTY_MINUTES; + } + return sessionInterval; + } + + public static long getSubsessionInterval() { + if (subsessionInterval == -1) { + return Constants.ONE_SECOND; + } + return subsessionInterval; + } + + public static IActivityHandler getActivityHandler(AdjustConfig config) { + if (activityHandler == null) { + return ActivityHandler.getInstance(config); + } + activityHandler.init(config); + return activityHandler; + } + + public static IAttributionHandler getAttributionHandler(IActivityHandler activityHandler, + ActivityPackage attributionPackage, + boolean startPaused) { + if (attributionHandler == null) { + return new AttributionHandler(activityHandler, attributionPackage, startPaused); + } + attributionHandler.init(activityHandler, attributionPackage, startPaused); + return attributionHandler; + } + + public static void setPackageHandler(IPackageHandler packageHandler) { + AdjustFactory.packageHandler = packageHandler; + } + + public static void setRequestHandler(IRequestHandler requestHandler) { + AdjustFactory.requestHandler = requestHandler; + } + + public static void setLogger(ILogger logger) { + AdjustFactory.logger = logger; + } + + public static void setHttpClient(HttpClient httpClient) { + AdjustFactory.httpClient = httpClient; + } + + public static void setTimerInterval(long timerInterval) { + AdjustFactory.timerInterval = timerInterval; + } + + public static void setTimerStart(long timerStart) { + AdjustFactory.timerStart = timerStart; + } + + public static void setSessionInterval(long sessionInterval) { + AdjustFactory.sessionInterval = sessionInterval; + } + + public static void setSubsessionInterval(long subsessionInterval) { + AdjustFactory.subsessionInterval = subsessionInterval; + } + + public static void setActivityHandler(IActivityHandler activityHandler) { + AdjustFactory.activityHandler = activityHandler; + } + + public static void setAttributionHandler(IAttributionHandler attributionHandler) { + AdjustFactory.attributionHandler = attributionHandler; + } + +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/AdjustInstance.java b/mobile/android/thirdparty/com/adjust/sdk/AdjustInstance.java new file mode 100644 index 000000000..158fb7ca1 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/AdjustInstance.java @@ -0,0 +1,86 @@ +package com.adjust.sdk; + +import android.net.Uri; + +/** + * Created by pfms on 04/12/14. + */ +public class AdjustInstance { + + private String referrer; + private long referrerClickTime; + private ActivityHandler activityHandler; + + private static ILogger getLogger() { + return AdjustFactory.getLogger(); + } + + public void onCreate(AdjustConfig adjustConfig) { + if (activityHandler != null) { + getLogger().error("Adjust already initialized"); + return; + } + + adjustConfig.referrer = this.referrer; + adjustConfig.referrerClickTime = this.referrerClickTime; + + activityHandler = ActivityHandler.getInstance(adjustConfig); + } + + public void trackEvent(AdjustEvent event) { + if (!checkActivityHandler()) return; + activityHandler.trackEvent(event); + } + + public void onResume() { + if (!checkActivityHandler()) return; + activityHandler.trackSubsessionStart(); + } + + public void onPause() { + if (!checkActivityHandler()) return; + activityHandler.trackSubsessionEnd(); + } + + public void setEnabled(boolean enabled) { + if (!checkActivityHandler()) return; + activityHandler.setEnabled(enabled); + } + + public boolean isEnabled() { + if (!checkActivityHandler()) return false; + return activityHandler.isEnabled(); + } + + public void appWillOpenUrl(Uri url) { + if (!checkActivityHandler()) return; + long clickTime = System.currentTimeMillis(); + activityHandler.readOpenUrl(url, clickTime); + } + + public void sendReferrer(String referrer) { + long clickTime = System.currentTimeMillis(); + // sendReferrer might be triggered before Adjust + if (activityHandler == null) { + // save it to inject in the config before launch + this.referrer = referrer; + this.referrerClickTime = clickTime; + } else { + activityHandler.sendReferrer(referrer, clickTime); + } + } + + public void setOfflineMode(boolean enabled) { + if (!checkActivityHandler()) return; + activityHandler.setOfflineMode(enabled); + } + + private boolean checkActivityHandler() { + if (activityHandler == null) { + getLogger().error("Please initialize Adjust by calling 'onCreate' before"); + return false; + } else { + return true; + } + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/AdjustReferrerReceiver.java b/mobile/android/thirdparty/com/adjust/sdk/AdjustReferrerReceiver.java new file mode 100644 index 000000000..cfeecd8d0 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/AdjustReferrerReceiver.java @@ -0,0 +1,35 @@ +package com.adjust.sdk; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +import static com.adjust.sdk.Constants.ENCODING; +import static com.adjust.sdk.Constants.MALFORMED; +import static com.adjust.sdk.Constants.REFERRER; + +// support multiple BroadcastReceivers for the INSTALL_REFERRER: +// http://blog.appington.com/2012/08/01/giving-credit-for-android-app-installs + +public class AdjustReferrerReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String rawReferrer = intent.getStringExtra(REFERRER); + if (null == rawReferrer) { + return; + } + + String referrer; + try { + referrer = URLDecoder.decode(rawReferrer, ENCODING); + } catch (UnsupportedEncodingException e) { + referrer = MALFORMED; + } + + AdjustInstance adjust = Adjust.getDefaultInstance(); + adjust.sendReferrer(referrer); + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/AttributionHandler.java b/mobile/android/thirdparty/com/adjust/sdk/AttributionHandler.java new file mode 100644 index 000000000..0d550a83a --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/AttributionHandler.java @@ -0,0 +1,155 @@ +package com.adjust.sdk; + +import android.net.Uri; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.methods.HttpGet; +import org.json.JSONObject; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * Created by pfms on 07/11/14. + */ +public class AttributionHandler implements IAttributionHandler { + private ScheduledExecutorService scheduler; + private IActivityHandler activityHandler; + private ILogger logger; + private ActivityPackage attributionPackage; + private ScheduledFuture waitingTask; + private HttpClient httpClient; + private boolean paused; + + public AttributionHandler(IActivityHandler activityHandler, + ActivityPackage attributionPackage, + boolean startPaused) { + scheduler = Executors.newSingleThreadScheduledExecutor(); + logger = AdjustFactory.getLogger(); + httpClient = Util.getHttpClient(); + init(activityHandler, attributionPackage, startPaused); + } + + @Override + public void init(IActivityHandler activityHandler, + ActivityPackage attributionPackage, + boolean startPaused) { + this.activityHandler = activityHandler; + this.attributionPackage = attributionPackage; + this.paused = startPaused; + } + + @Override + public void getAttribution() { + getAttribution(0); + } + + @Override + public void checkAttribution(final JSONObject jsonResponse) { + scheduler.submit(new Runnable() { + @Override + public void run() { + checkAttributionInternal(jsonResponse); + } + }); + } + + @Override + public void pauseSending() { + paused = true; + } + + @Override + public void resumeSending() { + paused = false; + } + + private void getAttribution(int delayInMilliseconds) { + if (waitingTask != null) { + waitingTask.cancel(false); + } + + if (delayInMilliseconds != 0) { + logger.debug("Waiting to query attribution in %d milliseconds", delayInMilliseconds); + } + + waitingTask = scheduler.schedule(new Runnable() { + @Override + public void run() { + getAttributionInternal(); + } + }, delayInMilliseconds, TimeUnit.MILLISECONDS); + } + + private void checkAttributionInternal(JSONObject jsonResponse) { + if (jsonResponse == null) return; + + JSONObject attributionJson = jsonResponse.optJSONObject("attribution"); + AdjustAttribution attribution = AdjustAttribution.fromJson(attributionJson); + + int timerMilliseconds = jsonResponse.optInt("ask_in", -1); + + // without ask_in attribute + if (timerMilliseconds < 0) { + activityHandler.tryUpdateAttribution(attribution); + + activityHandler.setAskingAttribution(false); + + return; + } + + activityHandler.setAskingAttribution(true); + + getAttribution(timerMilliseconds); + } + + private void getAttributionInternal() { + if (paused) { + logger.debug("Attribution Handler is paused"); + return; + } + logger.verbose("%s", attributionPackage.getExtendedString()); + HttpResponse httpResponse = null; + try { + HttpGet request = getRequest(attributionPackage); + httpResponse = httpClient.execute(request); + } catch (Exception e) { + logger.error("Failed to get attribution (%s)", e.getMessage()); + return; + } + + JSONObject jsonResponse = Util.parseJsonResponse(httpResponse, logger); + + checkAttributionInternal(jsonResponse); + } + + private Uri buildUri(ActivityPackage attributionPackage) { + Uri.Builder uriBuilder = new Uri.Builder(); + + uriBuilder.scheme(Constants.SCHEME); + uriBuilder.authority(Constants.AUTHORITY); + uriBuilder.appendPath(attributionPackage.getPath()); + + for (Map.Entry entry : attributionPackage.getParameters().entrySet()) { + uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue()); + } + + return uriBuilder.build(); + } + + private HttpGet getRequest(ActivityPackage attributionPackage) throws URISyntaxException { + HttpGet request = new HttpGet(); + Uri uri = buildUri(attributionPackage); + request.setURI(new URI(uri.toString())); + + request.addHeader("Client-SDK", attributionPackage.getClientSdk()); + + return request; + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/Constants.java b/mobile/android/thirdparty/com/adjust/sdk/Constants.java new file mode 100644 index 000000000..7a97cb2f4 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/Constants.java @@ -0,0 +1,53 @@ +// +// Constants.java +// Adjust +// +// Created by keyboardsurfer on 2013-11-08. +// Copyright (c) 2012-2014 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import java.util.Arrays; +import java.util.List; + +/** + * @author keyboardsurfer + * @since 8.11.13 + */ +public interface Constants { + int ONE_SECOND = 1000; + int ONE_MINUTE = 60 * ONE_SECOND; + int THIRTY_MINUTES = 30 * ONE_MINUTE; + + int CONNECTION_TIMEOUT = Constants.ONE_MINUTE; + int SOCKET_TIMEOUT = Constants.ONE_MINUTE; + + String BASE_URL = "https://app.adjust.com"; + String SCHEME = "https"; + String AUTHORITY = "app.adjust.com"; + String CLIENT_SDK = "android4.0.0"; + String LOGTAG = "Adjust"; + + String ACTIVITY_STATE_FILENAME = "AdjustIoActivityState"; + String ATTRIBUTION_FILENAME = "AdjustAttribution"; + + String MALFORMED = "malformed"; + String SMALL = "small"; + String NORMAL = "normal"; + String LONG = "long"; + String LARGE = "large"; + String XLARGE = "xlarge"; + String LOW = "low"; + String MEDIUM = "medium"; + String HIGH = "high"; + String REFERRER = "referrer"; + + String ENCODING = "UTF-8"; + String MD5 = "MD5"; + String SHA1 = "SHA-1"; + + // List of known plugins, possibly not active + List PLUGINS = Arrays.asList(); +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/DeviceInfo.java b/mobile/android/thirdparty/com/adjust/sdk/DeviceInfo.java new file mode 100644 index 000000000..5cccb77f4 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/DeviceInfo.java @@ -0,0 +1,290 @@ +package com.adjust.sdk; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.util.DisplayMetrics; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.util.Locale; +import java.util.Map; + +import static com.adjust.sdk.Constants.ENCODING; +import static com.adjust.sdk.Constants.HIGH; +import static com.adjust.sdk.Constants.LARGE; +import static com.adjust.sdk.Constants.LONG; +import static com.adjust.sdk.Constants.LOW; +import static com.adjust.sdk.Constants.MD5; +import static com.adjust.sdk.Constants.MEDIUM; +import static com.adjust.sdk.Constants.NORMAL; +import static com.adjust.sdk.Constants.SHA1; +import static com.adjust.sdk.Constants.SMALL; +import static com.adjust.sdk.Constants.XLARGE; + +/** + * Created by pfms on 06/11/14. + */ +class DeviceInfo { + String macSha1; + String macShortMd5; + String androidId; + String fbAttributionId; + String clientSdk; + String packageName; + String appVersion; + String deviceType; + String deviceName; + String deviceManufacturer; + String osName; + String osVersion; + String language; + String country; + String screenSize; + String screenFormat; + String screenDensity; + String displayWidth; + String displayHeight; + Map pluginKeys; + + DeviceInfo(Context context, String sdkPrefix) { + Resources resources = context.getResources(); + DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + Configuration configuration = resources.getConfiguration(); + Locale locale = configuration.locale; + int screenLayout = configuration.screenLayout; + boolean isGooglePlayServicesAvailable = Reflection.isGooglePlayServicesAvailable(context); + String macAddress = getMacAddress(context, isGooglePlayServicesAvailable); + + packageName = getPackageName(context); + appVersion = getAppVersion(context); + deviceType = getDeviceType(screenLayout); + deviceName = getDeviceName(); + deviceManufacturer = getDeviceManufacturer(); + osName = getOsName(); + osVersion = getOsVersion(); + language = getLanguage(locale); + country = getCountry(locale); + screenSize = getScreenSize(screenLayout); + screenFormat = getScreenFormat(screenLayout); + screenDensity = getScreenDensity(displayMetrics); + displayWidth = getDisplayWidth(displayMetrics); + displayHeight = getDisplayHeight(displayMetrics); + clientSdk = getClientSdk(sdkPrefix); + androidId = getAndroidId(context, isGooglePlayServicesAvailable); + fbAttributionId = getFacebookAttributionId(context); + pluginKeys = Reflection.getPluginKeys(context); + macSha1 = getMacSha1(macAddress); + macShortMd5 = getMacShortMd5(macAddress); + } + + private String getMacAddress(Context context, boolean isGooglePlayServicesAvailable) { + if (!isGooglePlayServicesAvailable) { + if (!!Util.checkPermission(context, android.Manifest.permission.ACCESS_WIFI_STATE)) { + AdjustFactory.getLogger().warn("Missing permission: ACCESS_WIFI_STATE"); + } + return Reflection.getMacAddress(context); + } else { + return null; + } + } + + private String getPackageName(Context context) { + return context.getPackageName(); + } + + private String getAppVersion(Context context) { + try { + PackageManager packageManager = context.getPackageManager(); + String name = context.getPackageName(); + PackageInfo info = packageManager.getPackageInfo(name, 0); + return info.versionName; + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + private String getDeviceType(int screenLayout) { + int screenSize = screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK; + + switch (screenSize) { + case Configuration.SCREENLAYOUT_SIZE_SMALL: + case Configuration.SCREENLAYOUT_SIZE_NORMAL: + return "phone"; + case Configuration.SCREENLAYOUT_SIZE_LARGE: + case 4: + return "tablet"; + default: + return null; + } + } + + private String getDeviceName() { + return Build.MODEL; + } + + private String getDeviceManufacturer() { + return Build.MANUFACTURER; + } + + private String getOsName() { + return "android"; + } + + private String getOsVersion() { + return osVersion = "" + Build.VERSION.SDK_INT; + } + + private String getLanguage(Locale locale) { + return locale.getLanguage(); + } + + private String getCountry(Locale locale) { + return locale.getCountry(); + } + + private String getScreenSize(int screenLayout) { + int screenSize = screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK; + + switch (screenSize) { + case Configuration.SCREENLAYOUT_SIZE_SMALL: + return SMALL; + case Configuration.SCREENLAYOUT_SIZE_NORMAL: + return NORMAL; + case Configuration.SCREENLAYOUT_SIZE_LARGE: + return LARGE; + case 4: + return XLARGE; + default: + return null; + } + } + + private String getScreenFormat(int screenLayout) { + int screenFormat = screenLayout & Configuration.SCREENLAYOUT_LONG_MASK; + + switch (screenFormat) { + case Configuration.SCREENLAYOUT_LONG_YES: + return LONG; + case Configuration.SCREENLAYOUT_LONG_NO: + return NORMAL; + default: + return null; + } + } + + private String getScreenDensity(DisplayMetrics displayMetrics) { + int density = displayMetrics.densityDpi; + int low = (DisplayMetrics.DENSITY_MEDIUM + DisplayMetrics.DENSITY_LOW) / 2; + int high = (DisplayMetrics.DENSITY_MEDIUM + DisplayMetrics.DENSITY_HIGH) / 2; + + if (0 == density) { + return null; + } else if (density < low) { + return LOW; + } else if (density > high) { + return HIGH; + } + return MEDIUM; + } + + private String getDisplayWidth(DisplayMetrics displayMetrics) { + return String.valueOf(displayMetrics.widthPixels); + } + + private String getDisplayHeight(DisplayMetrics displayMetrics) { + return String.valueOf(displayMetrics.heightPixels); + } + + private String getClientSdk(String sdkPrefix) { + if (sdkPrefix == null) { + return Constants.CLIENT_SDK; + } else { + return String.format("%s@%s", sdkPrefix, Constants.CLIENT_SDK); + } + } + + private String getMacSha1(String macAddress) { + if (macAddress == null) { + return null; + } + String macSha1 = sha1(macAddress); + + return macSha1; + } + + private String getMacShortMd5(String macAddress) { + if (macAddress == null) { + return null; + } + String macShort = macAddress.replaceAll(":", ""); + String macShortMd5 = md5(macShort); + + return macShortMd5; + } + + private String getAndroidId(Context context, boolean isGooglePlayServicesAvailable) { + if (!isGooglePlayServicesAvailable) { + return Reflection.getAndroidId(context); + } else { + return null; + } + } + + private String sha1(final String text) { + return hash(text, SHA1); + } + + private String md5(final String text) { + return hash(text, MD5); + } + + private String hash(final String text, final String method) { + String hashString = null; + try { + final byte[] bytes = text.getBytes(ENCODING); + final MessageDigest mesd = MessageDigest.getInstance(method); + mesd.update(bytes, 0, bytes.length); + final byte[] hash = mesd.digest(); + hashString = convertToHex(hash); + } catch (Exception e) { + } + return hashString; + } + + private static String convertToHex(final byte[] bytes) { + final BigInteger bigInt = new BigInteger(1, bytes); + final String formatString = "%0" + (bytes.length << 1) + "x"; + return String.format(formatString, bigInt); + } + + private String getFacebookAttributionId(final Context context) { + try { + final ContentResolver contentResolver = context.getContentResolver(); + final Uri uri = Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider"); + final String columnName = "aid"; + final String[] projection = {columnName}; + final Cursor cursor = contentResolver.query(uri, projection, null, null, null); + + if (null == cursor) { + return null; + } + if (!cursor.moveToFirst()) { + cursor.close(); + return null; + } + + final String attributionId = cursor.getString(cursor.getColumnIndex(columnName)); + cursor.close(); + return attributionId; + } catch (Exception e) { + return null; + } + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/IActivityHandler.java b/mobile/android/thirdparty/com/adjust/sdk/IActivityHandler.java new file mode 100644 index 000000000..10b92205d --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/IActivityHandler.java @@ -0,0 +1,36 @@ +package com.adjust.sdk; + +import android.net.Uri; + +import org.json.JSONObject; + +/** + * Created by pfms on 15/12/14. + */ +public interface IActivityHandler { + public void init(AdjustConfig config); + + public void trackSubsessionStart(); + + public void trackSubsessionEnd(); + + public void trackEvent(AdjustEvent event); + + public void finishedTrackingActivity(JSONObject jsonResponse); + + public void setEnabled(boolean enabled); + + public boolean isEnabled(); + + public void readOpenUrl(Uri url, long clickTime); + + public boolean tryUpdateAttribution(AdjustAttribution attribution); + + public void sendReferrer(String referrer, long clickTime); + + public void setOfflineMode(boolean enabled); + + public void setAskingAttribution(boolean askingAttribution); + + public ActivityPackage getAttributionPackage(); +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/IAttributionHandler.java b/mobile/android/thirdparty/com/adjust/sdk/IAttributionHandler.java new file mode 100644 index 000000000..d4e701f75 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/IAttributionHandler.java @@ -0,0 +1,20 @@ +package com.adjust.sdk; + +import org.json.JSONObject; + +/** + * Created by pfms on 15/12/14. + */ +public interface IAttributionHandler { + public void init(IActivityHandler activityHandler, + ActivityPackage attributionPackage, + boolean startPaused); + + public void getAttribution(); + + public void checkAttribution(JSONObject jsonResponse); + + public void pauseSending(); + + public void resumeSending(); +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/ILogger.java b/mobile/android/thirdparty/com/adjust/sdk/ILogger.java new file mode 100644 index 000000000..28f92af4b --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/ILogger.java @@ -0,0 +1,20 @@ +package com.adjust.sdk; + +public interface ILogger { + public void setLogLevel(LogLevel logLevel); + + public void setLogLevelString(String logLevelString); + + public void verbose(String message, Object... parameters); + + public void debug(String message, Object... parameters); + + public void info(String message, Object... parameters); + + public void warn(String message, Object... parameters); + + public void error(String message, Object... parameters); + + public void Assert(String message, Object... parameters); + +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/IPackageHandler.java b/mobile/android/thirdparty/com/adjust/sdk/IPackageHandler.java new file mode 100644 index 000000000..99c300364 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/IPackageHandler.java @@ -0,0 +1,27 @@ +package com.adjust.sdk; + +import android.content.Context; + +import org.json.JSONObject; + +public interface IPackageHandler { + public void init(IActivityHandler activityHandler, Context context, boolean startPaused); + + public void addPackage(ActivityPackage pack); + + public void sendFirstPackage(); + + public void sendNextPackage(); + + public void closeFirstPackage(); + + public void pauseSending(); + + public void resumeSending(); + + public String getFailureMessage(); + + public void finishedTrackingActivity(JSONObject jsonResponse); + + public void sendClickPackage(ActivityPackage clickPackage); +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/IRequestHandler.java b/mobile/android/thirdparty/com/adjust/sdk/IRequestHandler.java new file mode 100644 index 000000000..5b18e2ee9 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/IRequestHandler.java @@ -0,0 +1,9 @@ +package com.adjust.sdk; + +public interface IRequestHandler { + public void init(IPackageHandler packageHandler); + + public void sendPackage(ActivityPackage pack); + + public void sendClickPackage(ActivityPackage clickPackage); +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/LICENSE b/mobile/android/thirdparty/com/adjust/sdk/LICENSE new file mode 100644 index 000000000..25e1d5eb5 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2012-2014 adjust GmbH, +http://www.adjust.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/mobile/android/thirdparty/com/adjust/sdk/LogLevel.java b/mobile/android/thirdparty/com/adjust/sdk/LogLevel.java new file mode 100644 index 000000000..5c0b410c2 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/LogLevel.java @@ -0,0 +1,19 @@ +package com.adjust.sdk; + +import android.util.Log; + +/** + * Created by pfms on 11/03/15. + */ +public enum LogLevel { + VERBOSE(Log.VERBOSE), DEBUG(Log.DEBUG), INFO(Log.INFO), WARN(Log.WARN), ERROR(Log.ERROR), ASSERT(Log.ASSERT); + final int androidLogLevel; + + LogLevel(final int androidLogLevel) { + this.androidLogLevel = androidLogLevel; + } + + public int getAndroidLogLevel() { + return androidLogLevel; + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/Logger.java b/mobile/android/thirdparty/com/adjust/sdk/Logger.java new file mode 100644 index 000000000..86a644d4a --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/Logger.java @@ -0,0 +1,107 @@ +// +// Logger.java +// Adjust +// +// Created by Christian Wellenbrock on 2013-04-18. +// Copyright (c) 2013 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import android.util.Log; + +import java.util.Arrays; +import java.util.Locale; + +import static com.adjust.sdk.Constants.LOGTAG; + +public class Logger implements ILogger { + + private LogLevel logLevel; + private static String formatErrorMessage = "Error formating log message: %s, with params: %s"; + + public Logger() { + setLogLevel(LogLevel.INFO); + } + + @Override + public void setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; + } + + @Override + public void setLogLevelString(String logLevelString) { + if (null != logLevelString) { + try { + setLogLevel(LogLevel.valueOf(logLevelString.toUpperCase(Locale.US))); + } catch (IllegalArgumentException iae) { + error("Malformed logLevel '%s', falling back to 'info'", logLevelString); + } + } + } + + @Override + public void verbose(String message, Object... parameters) { + if (logLevel.androidLogLevel <= Log.VERBOSE) { + try { + Log.v(LOGTAG, String.format(message, parameters)); + } catch (Exception e) { + Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters))); + } + } + } + + @Override + public void debug(String message, Object... parameters) { + if (logLevel.androidLogLevel <= Log.DEBUG) { + try { + Log.d(LOGTAG, String.format(message, parameters)); + } catch (Exception e) { + Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters))); + } + } + } + + @Override + public void info(String message, Object... parameters) { + if (logLevel.androidLogLevel <= Log.INFO) { + try { + Log.i(LOGTAG, String.format(message, parameters)); + } catch (Exception e) { + Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters))); + } + } + } + + @Override + public void warn(String message, Object... parameters) { + if (logLevel.androidLogLevel <= Log.WARN) { + try { + Log.w(LOGTAG, String.format(message, parameters)); + } catch (Exception e) { + Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters))); + } + } + } + + @Override + public void error(String message, Object... parameters) { + if (logLevel.androidLogLevel <= Log.ERROR) { + try { + Log.e(LOGTAG, String.format(message, parameters)); + } catch (Exception e) { + Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters))); + } + } + } + + @Override + public void Assert(String message, Object... parameters) { + try { + Log.println(Log.ASSERT, LOGTAG, String.format(message, parameters)); + } catch (Exception e) { + Log.e(LOGTAG, String.format(formatErrorMessage, message, Arrays.toString(parameters))); + } + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/OnAttributionChangedListener.java b/mobile/android/thirdparty/com/adjust/sdk/OnAttributionChangedListener.java new file mode 100644 index 000000000..137d50d4d --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/OnAttributionChangedListener.java @@ -0,0 +1,5 @@ +package com.adjust.sdk; + +public interface OnAttributionChangedListener { + public void onAttributionChanged(AdjustAttribution attribution); +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/PackageBuilder.java b/mobile/android/thirdparty/com/adjust/sdk/PackageBuilder.java new file mode 100644 index 000000000..3a43045fd --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/PackageBuilder.java @@ -0,0 +1,291 @@ +// +// PackageBuilder.java +// Adjust +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import android.text.TextUtils; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +class PackageBuilder { + private AdjustConfig adjustConfig; + private DeviceInfo deviceInfo; + private ActivityState activityState; + private long createdAt; + + // reattributions + Map extraParameters; + AdjustAttribution attribution; + String reftag; + + private static ILogger logger = AdjustFactory.getLogger(); + + public PackageBuilder(AdjustConfig adjustConfig, + DeviceInfo deviceInfo, + ActivityState activityState, + long createdAt) { + this.adjustConfig = adjustConfig; + this.deviceInfo = deviceInfo; + this.activityState = activityState.clone(); + this.createdAt = createdAt; + } + + public ActivityPackage buildSessionPackage() { + Map parameters = getDefaultParameters(); + addDuration(parameters, "last_interval", activityState.lastInterval); + addString(parameters, "default_tracker", adjustConfig.defaultTracker); + + ActivityPackage sessionPackage = getDefaultActivityPackage(); + sessionPackage.setPath("/session"); + sessionPackage.setActivityKind(ActivityKind.SESSION); + sessionPackage.setSuffix(""); + sessionPackage.setParameters(parameters); + + return sessionPackage; + } + + public ActivityPackage buildEventPackage(AdjustEvent event) { + Map parameters = getDefaultParameters(); + addInt(parameters, "event_count", activityState.eventCount); + addString(parameters, "event_token", event.eventToken); + addDouble(parameters, "revenue", event.revenue); + addString(parameters, "currency", event.currency); + addMapJson(parameters, "callback_params", event.callbackParameters); + addMapJson(parameters, "partner_params", event.partnerParameters); + + ActivityPackage eventPackage = getDefaultActivityPackage(); + eventPackage.setPath("/event"); + eventPackage.setActivityKind(ActivityKind.EVENT); + eventPackage.setSuffix(getEventSuffix(event)); + eventPackage.setParameters(parameters); + + return eventPackage; + } + + public ActivityPackage buildClickPackage(String source, long clickTime) { + Map parameters = getDefaultParameters(); + + addString(parameters, "source", source); + addDate(parameters, "click_time", clickTime); + addString(parameters, "reftag", reftag); + addMapJson(parameters, "params", extraParameters); + injectAttribution(parameters); + + ActivityPackage clickPackage = getDefaultActivityPackage(); + clickPackage.setPath("/sdk_click"); + clickPackage.setActivityKind(ActivityKind.CLICK); + clickPackage.setSuffix(""); + clickPackage.setParameters(parameters); + + return clickPackage; + } + + public ActivityPackage buildAttributionPackage() { + Map parameters = getIdsParameters(); + + ActivityPackage attributionPackage = getDefaultActivityPackage(); + attributionPackage.setPath("attribution"); // does not contain '/' because of Uri.Builder.appendPath + attributionPackage.setActivityKind(ActivityKind.ATTRIBUTION); + attributionPackage.setSuffix(""); + attributionPackage.setParameters(parameters); + + return attributionPackage; + } + + private ActivityPackage getDefaultActivityPackage() { + ActivityPackage activityPackage = new ActivityPackage(); + activityPackage.setClientSdk(deviceInfo.clientSdk); + return activityPackage; + } + + private Map getDefaultParameters() { + Map parameters = new HashMap(); + + injectDeviceInfo(parameters); + injectConfig(parameters); + injectActivityState(parameters); + addDate(parameters, "created_at", createdAt); + + // general + checkDeviceIds(parameters); + + return parameters; + } + + private Map getIdsParameters() { + Map parameters = new HashMap(); + + injectDeviceInfoIds(parameters); + injectConfig(parameters); + injectActivityStateIds(parameters); + + checkDeviceIds(parameters); + + return parameters; + } + + private void injectDeviceInfo(Map parameters) { + injectDeviceInfoIds(parameters); + addString(parameters, "fb_id", deviceInfo.fbAttributionId); + addString(parameters, "package_name", deviceInfo.packageName); + addString(parameters, "app_version", deviceInfo.appVersion); + addString(parameters, "device_type", deviceInfo.deviceType); + addString(parameters, "device_name", deviceInfo.deviceName); + addString(parameters, "device_manufacturer", deviceInfo.deviceManufacturer); + addString(parameters, "os_name", deviceInfo.osName); + addString(parameters, "os_version", deviceInfo.osVersion); + addString(parameters, "language", deviceInfo.language); + addString(parameters, "country", deviceInfo.country); + addString(parameters, "screen_size", deviceInfo.screenSize); + addString(parameters, "screen_format", deviceInfo.screenFormat); + addString(parameters, "screen_density", deviceInfo.screenDensity); + addString(parameters, "display_width", deviceInfo.displayWidth); + addString(parameters, "display_height", deviceInfo.displayHeight); + fillPluginKeys(parameters); + } + + private void injectDeviceInfoIds(Map parameters) { + addString(parameters, "mac_sha1", deviceInfo.macSha1); + addString(parameters, "mac_md5", deviceInfo.macShortMd5); + addString(parameters, "android_id", deviceInfo.androidId); + } + + private void injectConfig(Map parameters) { + addString(parameters, "app_token", adjustConfig.appToken); + addString(parameters, "environment", adjustConfig.environment); + addBoolean(parameters, "device_known", adjustConfig.knownDevice); + addBoolean(parameters, "needs_attribution_data", adjustConfig.hasListener()); + + String playAdId = Util.getPlayAdId(adjustConfig.context); + addString(parameters, "gps_adid", playAdId); + Boolean isTrackingEnabled = Util.isPlayTrackingEnabled(adjustConfig.context); + addBoolean(parameters, "tracking_enabled", isTrackingEnabled); + } + + private void injectActivityState(Map parameters) { + injectActivityStateIds(parameters); + addInt(parameters, "session_count", activityState.sessionCount); + addInt(parameters, "subsession_count", activityState.subsessionCount); + addDuration(parameters, "session_length", activityState.sessionLength); + addDuration(parameters, "time_spent", activityState.timeSpent); + } + + private void injectActivityStateIds(Map parameters) { + addString(parameters, "android_uuid", activityState.uuid); + } + + private void injectAttribution(Map parameters) { + if (attribution == null) { + return; + } + addString(parameters, "tracker", attribution.trackerName); + addString(parameters, "campaign", attribution.campaign); + addString(parameters, "adgroup", attribution.adgroup); + addString(parameters, "creative", attribution.creative); + } + + private void checkDeviceIds(Map parameters) { + if (!parameters.containsKey("mac_sha1") + && !parameters.containsKey("mac_md5") + && !parameters.containsKey("android_id") + && !parameters.containsKey("gps_adid")) { + logger.error("Missing device id's. Please check if Proguard is correctly set with Adjust SDK"); + } + } + + private void fillPluginKeys(Map parameters) { + if (deviceInfo.pluginKeys == null) { + return; + } + + for (Map.Entry entry : deviceInfo.pluginKeys.entrySet()) { + addString(parameters, entry.getKey(), entry.getValue()); + } + } + + private String getEventSuffix(AdjustEvent event) { + if (event.revenue == null) { + return String.format(" '%s'", event.eventToken); + } else { + return String.format(Locale.US, " (%.4f %s, '%s')", event.revenue, event.currency, event.eventToken); + } + } + + private void addString(Map parameters, String key, String value) { + if (TextUtils.isEmpty(value)) { + return; + } + + parameters.put(key, value); + } + + private void addInt(Map parameters, String key, long value) { + if (value < 0) { + return; + } + + String valueString = Long.toString(value); + addString(parameters, key, valueString); + } + + private void addDate(Map parameters, String key, long value) { + if (value < 0) { + return; + } + + String dateString = Util.dateFormat(value); + addString(parameters, key, dateString); + } + + private void addDuration(Map parameters, String key, long durationInMilliSeconds) { + if (durationInMilliSeconds < 0) { + return; + } + + long durationInSeconds = (durationInMilliSeconds + 500) / 1000; + addInt(parameters, key, durationInSeconds); + } + + private void addMapJson(Map parameters, String key, Map map) { + if (map == null) { + return; + } + + if (map.size() == 0) { + return; + } + + JSONObject jsonObject = new JSONObject(map); + String jsonString = jsonObject.toString(); + + addString(parameters, key, jsonString); + } + + private void addBoolean(Map parameters, String key, Boolean value) { + if (value == null) { + return; + } + + int intValue = value ? 1 : 0; + + addInt(parameters, key, intValue); + } + + private void addDouble(Map parameters, String key, Double value) { + if (value == null) return; + + String doubleString = String.format("%.5f", value); + + addString(parameters, key, doubleString); + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java b/mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java new file mode 100644 index 000000000..d0a84ccd1 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/PackageHandler.java @@ -0,0 +1,274 @@ +// +// PackageHandler.java +// Adjust +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OptionalDataException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +// persistent +public class PackageHandler extends HandlerThread implements IPackageHandler { + private static final String PACKAGE_QUEUE_FILENAME = "AdjustIoPackageQueue"; + + private final InternalHandler internalHandler; + private IRequestHandler requestHandler; + private IActivityHandler activityHandler; + private List packageQueue; + private AtomicBoolean isSending; + private boolean paused; + private Context context; + private ILogger logger; + + public PackageHandler(IActivityHandler activityHandler, + Context context, + boolean startPaused) { + super(Constants.LOGTAG, MIN_PRIORITY); + setDaemon(true); + start(); + this.internalHandler = new InternalHandler(getLooper(), this); + this.logger = AdjustFactory.getLogger(); + + init(activityHandler, context, startPaused); + + Message message = Message.obtain(); + message.arg1 = InternalHandler.INIT; + internalHandler.sendMessage(message); + } + + @Override + public void init(IActivityHandler activityHandler, Context context, boolean startPaused) { + this.activityHandler = activityHandler; + this.context = context; + this.paused = startPaused; + } + + // add a package to the queue + @Override + public void addPackage(ActivityPackage pack) { + Message message = Message.obtain(); + message.arg1 = InternalHandler.ADD; + message.obj = pack; + internalHandler.sendMessage(message); + } + + // try to send the oldest package + @Override + public void sendFirstPackage() { + Message message = Message.obtain(); + message.arg1 = InternalHandler.SEND_FIRST; + internalHandler.sendMessage(message); + } + + // remove oldest package and try to send the next one + // (after success or possibly permanent failure) + @Override + public void sendNextPackage() { + Message message = Message.obtain(); + message.arg1 = InternalHandler.SEND_NEXT; + internalHandler.sendMessage(message); + } + + // close the package to retry in the future (after temporary failure) + @Override + public void closeFirstPackage() { + isSending.set(false); + } + + // interrupt the sending loop after the current request has finished + @Override + public void pauseSending() { + paused = true; + } + + // allow sending requests again + @Override + public void resumeSending() { + paused = false; + } + + // short info about how failing packages are handled + @Override + public String getFailureMessage() { + return "Will retry later."; + } + + @Override + public void finishedTrackingActivity(JSONObject jsonResponse) { + activityHandler.finishedTrackingActivity(jsonResponse); + } + + @Override + public void sendClickPackage(ActivityPackage clickPackage) { + logger.debug("Sending click package (%s)", clickPackage); + logger.verbose("%s", clickPackage.getExtendedString()); + requestHandler.sendClickPackage(clickPackage); + } + + private static final class InternalHandler extends Handler { + private static final int INIT = 1; + private static final int ADD = 2; + private static final int SEND_NEXT = 3; + private static final int SEND_FIRST = 4; + + private final WeakReference packageHandlerReference; + + protected InternalHandler(Looper looper, PackageHandler packageHandler) { + super(looper); + this.packageHandlerReference = new WeakReference(packageHandler); + } + + @Override + public void handleMessage(Message message) { + super.handleMessage(message); + + PackageHandler packageHandler = packageHandlerReference.get(); + if (null == packageHandler) { + return; + } + + switch (message.arg1) { + case INIT: + packageHandler.initInternal(); + break; + case ADD: + ActivityPackage activityPackage = (ActivityPackage) message.obj; + packageHandler.addInternal(activityPackage); + break; + case SEND_FIRST: + packageHandler.sendFirstInternal(); + break; + case SEND_NEXT: + packageHandler.sendNextInternal(); + break; + } + } + } + + // internal methods run in dedicated queue thread + + private void initInternal() { + requestHandler = AdjustFactory.getRequestHandler(this); + + isSending = new AtomicBoolean(); + + readPackageQueue(); + } + + private void addInternal(ActivityPackage newPackage) { + packageQueue.add(newPackage); + logger.debug("Added package %d (%s)", packageQueue.size(), newPackage); + logger.verbose("%s", newPackage.getExtendedString()); + + writePackageQueue(); + } + + private void sendFirstInternal() { + if (packageQueue.isEmpty()) { + return; + } + + if (paused) { + logger.debug("Package handler is paused"); + return; + } + if (isSending.getAndSet(true)) { + logger.verbose("Package handler is already sending"); + return; + } + + ActivityPackage firstPackage = packageQueue.get(0); + requestHandler.sendPackage(firstPackage); + } + + private void sendNextInternal() { + packageQueue.remove(0); + writePackageQueue(); + isSending.set(false); + sendFirstInternal(); + } + + private void readPackageQueue() { + try { + FileInputStream inputStream = context.openFileInput(PACKAGE_QUEUE_FILENAME); + BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); + ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); + + try { + Object object = objectStream.readObject(); + @SuppressWarnings("unchecked") + List packageQueue = (List) object; + logger.debug("Package handler read %d packages", packageQueue.size()); + this.packageQueue = packageQueue; + return; + } catch (ClassNotFoundException e) { + logger.error("Failed to find package queue class"); + } catch (OptionalDataException e) { + /* no-op */ + } catch (IOException e) { + logger.error("Failed to read package queue object"); + } catch (ClassCastException e) { + logger.error("Failed to cast package queue object"); + } finally { + objectStream.close(); + } + } catch (FileNotFoundException e) { + logger.verbose("Package queue file not found"); + } catch (Exception e) { + logger.error("Failed to read package queue file"); + } + + // start with a fresh package queue in case of any exception + packageQueue = new ArrayList(); + } + + public static Boolean deletePackageQueue(Context context) { + return context.deleteFile(PACKAGE_QUEUE_FILENAME); + } + + + private void writePackageQueue() { + try { + FileOutputStream outputStream = context.openFileOutput(PACKAGE_QUEUE_FILENAME, Context.MODE_PRIVATE); + BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); + ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); + + try { + objectStream.writeObject(packageQueue); + logger.debug("Package handler wrote %d packages", packageQueue.size()); + } catch (NotSerializableException e) { + logger.error("Failed to serialize packages"); + } finally { + objectStream.close(); + } + } catch (Exception e) { + logger.error("Failed to write packages (%s)", e.getLocalizedMessage()); + e.printStackTrace(); + } + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/Reflection.java b/mobile/android/thirdparty/com/adjust/sdk/Reflection.java new file mode 100644 index 000000000..d9d9a9dbc --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/Reflection.java @@ -0,0 +1,210 @@ +package com.adjust.sdk; + +import android.content.Context; + +import com.adjust.sdk.plugin.Plugin; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.adjust.sdk.Constants.PLUGINS; + +public class Reflection { + + public static String getPlayAdId(Context context) { + try { + Object AdvertisingInfoObject = getAdvertisingInfoObject(context); + + String playAdid = (String) invokeInstanceMethod(AdvertisingInfoObject, "getId", null); + + return playAdid; + } catch (Throwable t) { + return null; + } + } + + public static Boolean isPlayTrackingEnabled(Context context) { + try { + Object AdvertisingInfoObject = getAdvertisingInfoObject(context); + + Boolean isLimitedTrackingEnabled = (Boolean) invokeInstanceMethod(AdvertisingInfoObject, "isLimitAdTrackingEnabled", null); + + return !isLimitedTrackingEnabled; + } catch (Throwable t) { + return null; + } + } + + public static boolean isGooglePlayServicesAvailable(Context context) { + try { + Integer isGooglePlayServicesAvailableStatusCode = (Integer) invokeStaticMethod( + "com.google.android.gms.common.GooglePlayServicesUtil", + "isGooglePlayServicesAvailable", + new Class[]{Context.class}, context + ); + + boolean isGooglePlayServicesAvailable = (Boolean) isConnectionResultSuccess(isGooglePlayServicesAvailableStatusCode); + + return isGooglePlayServicesAvailable; + } catch (Throwable t) { + return false; + } + } + + public static String getMacAddress(Context context) { + try { + String macSha1 = (String) invokeStaticMethod( + "com.adjust.sdk.plugin.MacAddressUtil", + "getMacAddress", + new Class[]{Context.class}, context + ); + + return macSha1; + } catch (Throwable t) { + return null; + } + } + + public static String getAndroidId(Context context) { + try { + String androidId = (String) invokeStaticMethod("com.adjust.sdk.plugin.AndroidIdUtil", "getAndroidId" + , new Class[]{Context.class}, context); + + return androidId; + } catch (Throwable t) { + return null; + } + } + + public static String getSha1EmailAddress(Context context, String key) { + try { + String sha1EmailAddress = (String) invokeStaticMethod("com.adjust.sdk.plugin.EmailUtil", "getSha1EmailAddress" + , new Class[]{Context.class, String.class}, context, key); + + return sha1EmailAddress; + } catch (Throwable t) { + return null; + } + } + + private static Object getAdvertisingInfoObject(Context context) + throws Exception { + return invokeStaticMethod("com.google.android.gms.ads.identifier.AdvertisingIdClient", + "getAdvertisingIdInfo", + new Class[]{Context.class}, context + ); + } + + private static boolean isConnectionResultSuccess(Integer statusCode) { + if (statusCode == null) { + return false; + } + + try { + Class ConnectionResultClass = Class.forName("com.google.android.gms.common.ConnectionResult"); + + Field SuccessField = ConnectionResultClass.getField("SUCCESS"); + + int successStatusCode = SuccessField.getInt(null); + + return successStatusCode == statusCode; + } catch (Throwable t) { + return false; + } + } + + public static Class forName(String className) { + try { + Class classObject = Class.forName(className); + return classObject; + } catch (Throwable t) { + return null; + } + } + + public static Object createDefaultInstance(String className) { + Class classObject = forName(className); + Object instance = createDefaultInstance(classObject); + return instance; + } + + public static Object createDefaultInstance(Class classObject) { + try { + Object instance = classObject.newInstance(); + return instance; + } catch (Throwable t) { + return null; + } + } + + public static Object createInstance(String className, Class[] cArgs, Object... args) { + try { + Class classObject = Class.forName(className); + @SuppressWarnings("unchecked") + Constructor constructor = classObject.getConstructor(cArgs); + Object instance = constructor.newInstance(args); + return instance; + } catch (Throwable t) { + return null; + } + } + + public static Object invokeStaticMethod(String className, String methodName, Class[] cArgs, Object... args) + throws Exception { + Class classObject = Class.forName(className); + + return invokeMethod(classObject, methodName, null, cArgs, args); + } + + public static Object invokeInstanceMethod(Object instance, String methodName, Class[] cArgs, Object... args) + throws Exception { + Class classObject = instance.getClass(); + + return invokeMethod(classObject, methodName, instance, cArgs, args); + } + + public static Object invokeMethod(Class classObject, String methodName, Object instance, Class[] cArgs, Object... args) + throws Exception { + @SuppressWarnings("unchecked") + Method methodObject = classObject.getMethod(methodName, cArgs); + + Object resultObject = methodObject.invoke(instance, args); + + return resultObject; + } + + public static Map getPluginKeys(Context context) { + Map pluginKeys = new HashMap(); + + for (Plugin plugin : getPlugins()) { + Map.Entry pluginEntry = plugin.getParameter(context); + if (pluginEntry != null) { + pluginKeys.put(pluginEntry.getKey(), pluginEntry.getValue()); + } + } + + if (pluginKeys.size() == 0) { + return null; + } else { + return pluginKeys; + } + } + + private static List getPlugins() { + List plugins = new ArrayList(PLUGINS.size()); + + for (String pluginName : PLUGINS) { + Object pluginObject = Reflection.createDefaultInstance(pluginName); + if (pluginObject != null && pluginObject instanceof Plugin) { + plugins.add((Plugin) pluginObject); + } + } + + return plugins; + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/RequestHandler.java b/mobile/android/thirdparty/com/adjust/sdk/RequestHandler.java new file mode 100644 index 000000000..84d45d0ce --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/RequestHandler.java @@ -0,0 +1,210 @@ +// +// RequestHandler.java +// Adjust +// +// Created by Christian Wellenbrock on 2013-06-25. +// Copyright (c) 2013 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.NameValuePair; +import ch.boye.httpclientandroidlib.client.ClientProtocolException; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.client.entity.UrlEncodedFormEntity; +import ch.boye.httpclientandroidlib.client.methods.HttpPost; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.utils.URLEncodedUtils; +import ch.boye.httpclientandroidlib.message.BasicNameValuePair; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.ref.WeakReference; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class RequestHandler extends HandlerThread implements IRequestHandler { + private InternalHandler internalHandler; + private IPackageHandler packageHandler; + private HttpClient httpClient; + private ILogger logger; + + public RequestHandler(IPackageHandler packageHandler) { + super(Constants.LOGTAG, MIN_PRIORITY); + setDaemon(true); + start(); + + this.logger = AdjustFactory.getLogger(); + this.internalHandler = new InternalHandler(getLooper(), this); + init(packageHandler); + + Message message = Message.obtain(); + message.arg1 = InternalHandler.INIT; + internalHandler.sendMessage(message); + } + + @Override + public void init(IPackageHandler packageHandler) { + this.packageHandler = packageHandler; + } + + @Override + public void sendPackage(ActivityPackage pack) { + Message message = Message.obtain(); + message.arg1 = InternalHandler.SEND; + message.obj = pack; + internalHandler.sendMessage(message); + } + + @Override + public void sendClickPackage(ActivityPackage clickPackage) { + Message message = Message.obtain(); + message.arg1 = InternalHandler.SEND_CLICK; + message.obj = clickPackage; + internalHandler.sendMessage(message); + + } + + private static final class InternalHandler extends Handler { + private static final int INIT = 72401; + private static final int SEND = 72400; + private static final int SEND_CLICK = 72402; + + private final WeakReference requestHandlerReference; + + protected InternalHandler(Looper looper, RequestHandler requestHandler) { + super(looper); + this.requestHandlerReference = new WeakReference(requestHandler); + } + + @Override + public void handleMessage(Message message) { + super.handleMessage(message); + + RequestHandler requestHandler = requestHandlerReference.get(); + if (null == requestHandler) { + return; + } + + switch (message.arg1) { + case INIT: + requestHandler.initInternal(); + break; + case SEND: + ActivityPackage activityPackage = (ActivityPackage) message.obj; + requestHandler.sendInternal(activityPackage, true); + break; + case SEND_CLICK: + ActivityPackage clickPackage = (ActivityPackage) message.obj; + requestHandler.sendInternal(clickPackage, false); + break; + } + } + } + + private void initInternal() { + httpClient = Util.getHttpClient(); + } + + private void sendInternal(ActivityPackage activityPackage, boolean sendToPackageHandler) { + try { + HttpUriRequest request = getRequest(activityPackage); + HttpResponse response = httpClient.execute(request); + requestFinished(response, sendToPackageHandler); + } catch (UnsupportedEncodingException e) { + sendNextPackage(activityPackage, "Failed to encode parameters", e, sendToPackageHandler); + } catch (ClientProtocolException e) { + closePackage(activityPackage, "Client protocol error", e, sendToPackageHandler); + } catch (SocketTimeoutException e) { + closePackage(activityPackage, "Request timed out", e, sendToPackageHandler); + } catch (IOException e) { + closePackage(activityPackage, "Request failed", e, sendToPackageHandler); + } catch (Throwable e) { + sendNextPackage(activityPackage, "Runtime exception", e, sendToPackageHandler); + } + } + + private void requestFinished(HttpResponse response, boolean sendToPackageHandler) { + JSONObject jsonResponse = Util.parseJsonResponse(response, logger); + + if (jsonResponse == null) { + if (sendToPackageHandler) { + packageHandler.closeFirstPackage(); + } + return; + } + + packageHandler.finishedTrackingActivity(jsonResponse); + if (sendToPackageHandler) { + packageHandler.sendNextPackage(); + } + } + + // close current package because it failed + private void closePackage(ActivityPackage activityPackage, String message, Throwable throwable, boolean sendToPackageHandler) { + final String packageMessage = activityPackage.getFailureMessage(); + final String handlerMessage = packageHandler.getFailureMessage(); + final String reasonString = getReasonString(message, throwable); + logger.error("%s. (%s) %s", packageMessage, reasonString, handlerMessage); + + if (sendToPackageHandler) { + packageHandler.closeFirstPackage(); + } + } + + // send next package because the current package failed + private void sendNextPackage(ActivityPackage activityPackage, String message, Throwable throwable, boolean sendToPackageHandler) { + final String failureMessage = activityPackage.getFailureMessage(); + final String reasonString = getReasonString(message, throwable); + logger.error("%s. (%s)", failureMessage, reasonString); + + if (sendToPackageHandler) { + packageHandler.sendNextPackage(); + } + } + + private String getReasonString(String message, Throwable throwable) { + if (throwable != null) { + return String.format("%s: %s", message, throwable); + } else { + return String.format("%s", message); + } + } + + private HttpUriRequest getRequest(ActivityPackage activityPackage) throws UnsupportedEncodingException { + String url = Constants.BASE_URL + activityPackage.getPath(); + HttpPost request = new HttpPost(url); + + String language = Locale.getDefault().getLanguage(); + request.addHeader("Client-SDK", activityPackage.getClientSdk()); + request.addHeader("Accept-Language", language); + + List pairs = new ArrayList(); + for (Map.Entry entry : activityPackage.getParameters().entrySet()) { + NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue()); + pairs.add(pair); + } + + long now = System.currentTimeMillis(); + String dateString = Util.dateFormat(now); + NameValuePair sentAtPair = new BasicNameValuePair("sent_at", dateString); + pairs.add(sentAtPair); + + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs); + entity.setContentType(URLEncodedUtils.CONTENT_TYPE); + request.setEntity(entity); + + return request; + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/UnitTestActivity.java b/mobile/android/thirdparty/com/adjust/sdk/UnitTestActivity.java new file mode 100644 index 000000000..799fb8982 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/UnitTestActivity.java @@ -0,0 +1,38 @@ +package com.adjust.sdk; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +public class UnitTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //setContentView(com.adjust.sdk.test.R.layout.activity_unit_test); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + //getMenuInflater().inflate(com.adjust.sdk.test.R.menu.menu_unit_test, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. +/* int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == com.adjust.sdk.test.R.id.action_settings) { + return true; + } +*/ + return super.onOptionsItemSelected(item); + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/Util.java b/mobile/android/thirdparty/com/adjust/sdk/Util.java new file mode 100644 index 000000000..84c47f87e --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/Util.java @@ -0,0 +1,202 @@ +// +// Util.java +// Adjust +// +// Created by Christian Wellenbrock on 2012-10-11. +// Copyright (c) 2012-2014 adjust GmbH. All rights reserved. +// See the file MIT-LICENSE for copying permission. +// + +package com.adjust.sdk; + +import android.content.Context; +import android.content.pm.PackageManager; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.client.HttpClient; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OptionalDataException; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Collects utility functions used by Adjust. + */ +public class Util { + + private static SimpleDateFormat dateFormat; + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'Z"; + + protected static String createUuid() { + return UUID.randomUUID().toString(); + } + + public static String quote(String string) { + if (string == null) { + return null; + } + + Pattern pattern = Pattern.compile("\\s"); + Matcher matcher = pattern.matcher(string); + if (!matcher.find()) { + return string; + } + + return String.format("'%s'", string); + } + + public static String dateFormat(long date) { + if (null == dateFormat) { + dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.US); + } + return dateFormat.format(date); + } + + public static String getPlayAdId(Context context) { + return Reflection.getPlayAdId(context); + } + + public static Boolean isPlayTrackingEnabled(Context context) { + return Reflection.isPlayTrackingEnabled(context); + } + + public static T readObject(Context context, String filename, String objectName) { + ILogger logger = AdjustFactory.getLogger(); + try { + FileInputStream inputStream = context.openFileInput(filename); + BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); + ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); + + try { + @SuppressWarnings("unchecked") + T t = (T) objectStream.readObject(); + logger.debug("Read %s: %s", objectName, t); + return t; + } catch (ClassNotFoundException e) { + logger.error("Failed to find %s class", objectName); + } catch (OptionalDataException e) { + /* no-op */ + } catch (IOException e) { + logger.error("Failed to read %s object", objectName); + } catch (ClassCastException e) { + logger.error("Failed to cast %s object", objectName); + } finally { + objectStream.close(); + } + + } catch (FileNotFoundException e) { + logger.verbose("%s file not found", objectName); + } catch (Exception e) { + logger.error("Failed to open %s file for reading (%s)", objectName, e); + } + + return null; + } + + public static void writeObject(T object, Context context, String filename, String objectName) { + ILogger logger = AdjustFactory.getLogger(); + try { + FileOutputStream outputStream = context.openFileOutput(filename, Context.MODE_PRIVATE); + BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); + ObjectOutputStream objectStream = new ObjectOutputStream(bufferedStream); + + try { + objectStream.writeObject(object); + logger.debug("Wrote %s: %s", objectName, object); + } catch (NotSerializableException e) { + logger.error("Failed to serialize %s", objectName); + } finally { + objectStream.close(); + } + + } catch (Exception e) { + logger.error("Failed to open %s for writing (%s)", objectName, e); + } + } + + public static String parseResponse(HttpResponse httpResponse, ILogger logger) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + httpResponse.getEntity().writeTo(out); + out.close(); + String response = out.toString().trim(); + logger.verbose("Response: %s", response); + return response; + } catch (Exception e) { + logger.error("Failed to parse response (%s)", e); + return null; + } + } + + public static JSONObject parseJsonResponse(HttpResponse httpResponse, ILogger logger) { + if (httpResponse == null) { + return null; + } + String stringResponse = null; + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + httpResponse.getEntity().writeTo(out); + out.close(); + stringResponse = out.toString().trim(); + } catch (Exception e) { + logger.error("Failed to parse response (%s)", e.getMessage()); + } + + logger.verbose("Response: %s", stringResponse); + if (stringResponse == null) return null; + + JSONObject jsonResponse = null; + try { + jsonResponse = new JSONObject(stringResponse); + } catch (JSONException e) { + logger.error("Failed to parse json response: %s (%s)", stringResponse, e.getMessage()); + } + + if (jsonResponse == null) return null; + + String message = jsonResponse.optString("message", null); + + if (message == null) { + message = "No message found"; + } + + if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + logger.info("%s", message); + } else { + logger.error("%s", message); + } + + return jsonResponse; + } + + public static HttpClient getHttpClient() { + HttpParams httpParams = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, Constants.CONNECTION_TIMEOUT); + HttpConnectionParams.setSoTimeout(httpParams, Constants.SOCKET_TIMEOUT); + return AdjustFactory.getHttpClient(httpParams); + } + + public static boolean checkPermission(Context context, String permission) { + int result = context.checkCallingOrSelfPermission(permission); + return result == PackageManager.PERMISSION_GRANTED; + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/plugin/AndroidIdUtil.java b/mobile/android/thirdparty/com/adjust/sdk/plugin/AndroidIdUtil.java new file mode 100644 index 000000000..96a072287 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/plugin/AndroidIdUtil.java @@ -0,0 +1,10 @@ +package com.adjust.sdk.plugin; + +import android.content.Context; +import android.provider.Settings.Secure; + +public class AndroidIdUtil { + public static String getAndroidId(final Context context) { + return Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/plugin/MacAddressUtil.java b/mobile/android/thirdparty/com/adjust/sdk/plugin/MacAddressUtil.java new file mode 100644 index 000000000..c8bdbadd7 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/plugin/MacAddressUtil.java @@ -0,0 +1,82 @@ +package com.adjust.sdk.plugin; + +import android.content.Context; +import android.net.wifi.WifiManager; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.Locale; + +public class MacAddressUtil { + public static String getMacAddress(Context context) { + final String rawAddress = getRawMacAddress(context); + if (rawAddress == null) { + return null; + } + final String upperAddress = rawAddress.toUpperCase(Locale.US); + return removeSpaceString(upperAddress); + } + + private static String getRawMacAddress(Context context) { + // android devices should have a wlan address + final String wlanAddress = loadAddress("wlan0"); + if (wlanAddress != null) { + return wlanAddress; + } + + // emulators should have an ethernet address + final String ethAddress = loadAddress("eth0"); + if (ethAddress != null) { + return ethAddress; + } + + // query the wifi manager (requires the ACCESS_WIFI_STATE permission) + try { + final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + final String wifiAddress = wifiManager.getConnectionInfo().getMacAddress(); + if (wifiAddress != null) { + return wifiAddress; + } + } catch (Exception e) { + /* no-op */ + } + + return null; + } + + private static String loadAddress(final String interfaceName) { + try { + final String filePath = "/sys/class/net/" + interfaceName + "/address"; + final StringBuilder fileData = new StringBuilder(1000); + final BufferedReader reader = new BufferedReader(new FileReader(filePath), 1024); + final char[] buf = new char[1024]; + int numRead; + + String readData; + while ((numRead = reader.read(buf)) != -1) { + readData = String.valueOf(buf, 0, numRead); + fileData.append(readData); + } + + reader.close(); + return fileData.toString(); + } catch (IOException e) { + return null; + } + } + + private static String removeSpaceString(final String inputString) { + if (inputString == null) { + return null; + } + + String outputString = inputString.replaceAll("\\s", ""); + if (TextUtils.isEmpty(outputString)) { + return null; + } + + return outputString; + } +} diff --git a/mobile/android/thirdparty/com/adjust/sdk/plugin/Plugin.java b/mobile/android/thirdparty/com/adjust/sdk/plugin/Plugin.java new file mode 100644 index 000000000..ab704e6d3 --- /dev/null +++ b/mobile/android/thirdparty/com/adjust/sdk/plugin/Plugin.java @@ -0,0 +1,12 @@ +package com.adjust.sdk.plugin; + +import android.content.Context; + +import java.util.Map; + +/** + * Created by pfms on 18/09/14. + */ +public interface Plugin { + Map.Entry getParameter(Context context); +} diff --git a/mobile/android/thirdparty/com/jakewharton/disklrucache/DiskLruCache.java b/mobile/android/thirdparty/com/jakewharton/disklrucache/DiskLruCache.java new file mode 100644 index 000000000..e81e7fbcc --- /dev/null +++ b/mobile/android/thirdparty/com/jakewharton/disklrucache/DiskLruCache.java @@ -0,0 +1,943 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jakewharton.disklrucache; + +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A cache that uses a bounded amount of space on a filesystem. Each cache + * entry has a string key and a fixed number of values. Each key must match + * the regex [a-z0-9_-]{1,120}. Values are byte sequences, + * accessible as streams or files. Each value must be between {@code 0} and + * {@code Integer.MAX_VALUE} bytes in length. + * + *

    The cache stores its data in a directory on the filesystem. This + * directory must be exclusive to the cache; the cache may delete or overwrite + * files from its directory. It is an error for multiple processes to use the + * same cache directory at the same time. + * + *

    This cache limits the number of bytes that it will store on the + * filesystem. When the number of stored bytes exceeds the limit, the cache will + * remove entries in the background until the limit is satisfied. The limit is + * not strict: the cache may temporarily exceed it while waiting for files to be + * deleted. The limit does not include filesystem overhead or the cache + * journal so space-sensitive applications should set a conservative limit. + * + *

    Clients call {@link #edit} to create or update the values of an entry. An + * entry may have only one editor at one time; if a value is not available to be + * edited then {@link #edit} will return null. + *

      + *
    • When an entry is being created it is necessary to + * supply a full set of values; the empty value should be used as a + * placeholder if necessary. + *
    • When an entry is being edited, it is not necessary + * to supply data for every value; values default to their previous + * value. + *
    + * Every {@link #edit} call must be matched by a call to {@link Editor#commit} + * or {@link Editor#abort}. Committing is atomic: a read observes the full set + * of values as they were before or after the commit, but never a mix of values. + * + *

    Clients call {@link #get} to read a snapshot of an entry. The read will + * observe the value at the time that {@link #get} was called. Updates and + * removals after the call do not impact ongoing reads. + * + *

    This class is tolerant of some I/O errors. If files are missing from the + * filesystem, the corresponding entries will be dropped from the cache. If + * an error occurs while writing a cache value, the edit will fail silently. + * Callers should handle other problems by catching {@code IOException} and + * responding appropriately. + */ +public final class DiskLruCache implements Closeable { + static final String JOURNAL_FILE = "journal"; + static final String JOURNAL_FILE_TEMP = "journal.tmp"; + static final String JOURNAL_FILE_BACKUP = "journal.bkp"; + static final String MAGIC = "libcore.io.DiskLruCache"; + static final String VERSION_1 = "1"; + static final long ANY_SEQUENCE_NUMBER = -1; + static final String STRING_KEY_PATTERN = "[a-z0-9_-]{1,120}"; + static final Pattern LEGAL_KEY_PATTERN = Pattern.compile(STRING_KEY_PATTERN); + private static final String CLEAN = "CLEAN"; + private static final String DIRTY = "DIRTY"; + private static final String REMOVE = "REMOVE"; + private static final String READ = "READ"; + + /* + * This cache uses a journal file named "journal". A typical journal file + * looks like this: + * libcore.io.DiskLruCache + * 1 + * 100 + * 2 + * + * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 + * DIRTY 335c4c6028171cfddfbaae1a9c313c52 + * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 + * REMOVE 335c4c6028171cfddfbaae1a9c313c52 + * DIRTY 1ab96a171faeeee38496d8b330771a7a + * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 + * READ 335c4c6028171cfddfbaae1a9c313c52 + * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 + * + * The first five lines of the journal form its header. They are the + * constant string "libcore.io.DiskLruCache", the disk cache's version, + * the application's version, the value count, and a blank line. + * + * Each of the subsequent lines in the file is a record of the state of a + * cache entry. Each line contains space-separated values: a state, a key, + * and optional state-specific values. + * o DIRTY lines track that an entry is actively being created or updated. + * Every successful DIRTY action should be followed by a CLEAN or REMOVE + * action. DIRTY lines without a matching CLEAN or REMOVE indicate that + * temporary files may need to be deleted. + * o CLEAN lines track a cache entry that has been successfully published + * and may be read. A publish line is followed by the lengths of each of + * its values. + * o READ lines track accesses for LRU. + * o REMOVE lines track entries that have been deleted. + * + * The journal file is appended to as cache operations occur. The journal may + * occasionally be compacted by dropping redundant lines. A temporary file named + * "journal.tmp" will be used during compaction; that file should be deleted if + * it exists when the cache is opened. + */ + + private final File directory; + private final File journalFile; + private final File journalFileTmp; + private final File journalFileBackup; + private final int appVersion; + private long maxSize; + private final int valueCount; + private long size = 0; + private Writer journalWriter; + private final LinkedHashMap lruEntries = + new LinkedHashMap(0, 0.75f, true); + private int redundantOpCount; + + /** + * To differentiate between old and current snapshots, each entry is given + * a sequence number each time an edit is committed. A snapshot is stale if + * its sequence number is not equal to its entry's sequence number. + */ + private long nextSequenceNumber = 0; + + /** This cache uses a single background thread to evict entries. */ + final ThreadPoolExecutor executorService = + new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()); + private final Callable cleanupCallable = new Callable() { + public Void call() throws Exception { + synchronized (DiskLruCache.this) { + if (journalWriter == null) { + return null; // Closed. + } + trimToSize(); + if (journalRebuildRequired()) { + rebuildJournal(); + redundantOpCount = 0; + } + } + return null; + } + }; + + private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) { + this.directory = directory; + this.appVersion = appVersion; + this.journalFile = new File(directory, JOURNAL_FILE); + this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP); + this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP); + this.valueCount = valueCount; + this.maxSize = maxSize; + } + + /** + * Opens the cache in {@code directory}, creating a cache if none exists + * there. + * + * @param directory a writable directory + * @param valueCount the number of values per cache entry. Must be positive. + * @param maxSize the maximum number of bytes this cache should use to store + * @throws IOException if reading or writing the cache directory fails + */ + public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) + throws IOException { + if (maxSize <= 0) { + throw new IllegalArgumentException("maxSize <= 0"); + } + if (valueCount <= 0) { + throw new IllegalArgumentException("valueCount <= 0"); + } + + // If a bkp file exists, use it instead. + File backupFile = new File(directory, JOURNAL_FILE_BACKUP); + if (backupFile.exists()) { + File journalFile = new File(directory, JOURNAL_FILE); + // If journal file also exists just delete backup file. + if (journalFile.exists()) { + backupFile.delete(); + } else { + renameTo(backupFile, journalFile, false); + } + } + + // Prefer to pick up where we left off. + DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); + if (cache.journalFile.exists()) { + try { + cache.readJournal(); + cache.processJournal(); + return cache; + } catch (IOException journalIsCorrupt) { + System.out + .println("DiskLruCache " + + directory + + " is corrupt: " + + journalIsCorrupt.getMessage() + + ", removing"); + cache.delete(); + } + } + + // Create a new empty cache. + directory.mkdirs(); + cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); + cache.rebuildJournal(); + return cache; + } + + private void readJournal() throws IOException { + StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII); + try { + String magic = reader.readLine(); + String version = reader.readLine(); + String appVersionString = reader.readLine(); + String valueCountString = reader.readLine(); + String blank = reader.readLine(); + if (!MAGIC.equals(magic) + || !VERSION_1.equals(version) + || !Integer.toString(appVersion).equals(appVersionString) + || !Integer.toString(valueCount).equals(valueCountString) + || !"".equals(blank)) { + throw new IOException("unexpected journal header: [" + magic + ", " + version + ", " + + valueCountString + ", " + blank + "]"); + } + + int lineCount = 0; + while (true) { + try { + readJournalLine(reader.readLine()); + lineCount++; + } catch (EOFException endOfJournal) { + break; + } + } + redundantOpCount = lineCount - lruEntries.size(); + + // If we ended on a truncated line, rebuild the journal before appending to it. + if (reader.hasUnterminatedLine()) { + rebuildJournal(); + } else { + journalWriter = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(journalFile, true), Util.US_ASCII)); + } + } finally { + Util.closeQuietly(reader); + } + } + + private void readJournalLine(String line) throws IOException { + int firstSpace = line.indexOf(' '); + if (firstSpace == -1) { + throw new IOException("unexpected journal line: " + line); + } + + int keyBegin = firstSpace + 1; + int secondSpace = line.indexOf(' ', keyBegin); + final String key; + if (secondSpace == -1) { + key = line.substring(keyBegin); + if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) { + lruEntries.remove(key); + return; + } + } else { + key = line.substring(keyBegin, secondSpace); + } + + Entry entry = lruEntries.get(key); + if (entry == null) { + entry = new Entry(key); + lruEntries.put(key, entry); + } + + if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) { + String[] parts = line.substring(secondSpace + 1).split(" "); + entry.readable = true; + entry.currentEditor = null; + entry.setLengths(parts); + } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) { + entry.currentEditor = new Editor(entry); + } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) { + // This work was already done by calling lruEntries.get(). + } else { + throw new IOException("unexpected journal line: " + line); + } + } + + /** + * Computes the initial size and collects garbage as a part of opening the + * cache. Dirty entries are assumed to be inconsistent and will be deleted. + */ + private void processJournal() throws IOException { + deleteIfExists(journalFileTmp); + for (Iterator i = lruEntries.values().iterator(); i.hasNext(); ) { + Entry entry = i.next(); + if (entry.currentEditor == null) { + for (int t = 0; t < valueCount; t++) { + size += entry.lengths[t]; + } + } else { + entry.currentEditor = null; + for (int t = 0; t < valueCount; t++) { + deleteIfExists(entry.getCleanFile(t)); + deleteIfExists(entry.getDirtyFile(t)); + } + i.remove(); + } + } + } + + /** + * Creates a new journal that omits redundant information. This replaces the + * current journal if it exists. + */ + private synchronized void rebuildJournal() throws IOException { + if (journalWriter != null) { + journalWriter.close(); + } + + Writer writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII)); + try { + writer.write(MAGIC); + writer.write("\n"); + writer.write(VERSION_1); + writer.write("\n"); + writer.write(Integer.toString(appVersion)); + writer.write("\n"); + writer.write(Integer.toString(valueCount)); + writer.write("\n"); + writer.write("\n"); + + for (Entry entry : lruEntries.values()) { + if (entry.currentEditor != null) { + writer.write(DIRTY + ' ' + entry.key + '\n'); + } else { + writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); + } + } + } finally { + writer.close(); + } + + if (journalFile.exists()) { + renameTo(journalFile, journalFileBackup, true); + } + renameTo(journalFileTmp, journalFile, false); + journalFileBackup.delete(); + + journalWriter = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII)); + } + + private static void deleteIfExists(File file) throws IOException { + if (file.exists() && !file.delete()) { + throw new IOException(); + } + } + + private static void renameTo(File from, File to, boolean deleteDestination) throws IOException { + if (deleteDestination) { + deleteIfExists(to); + } + if (!from.renameTo(to)) { + throw new IOException(); + } + } + + /** + * Returns a snapshot of the entry named {@code key}, or null if it doesn't + * exist is not currently readable. If a value is returned, it is moved to + * the head of the LRU queue. + */ + public synchronized Snapshot get(String key) throws IOException { + checkNotClosed(); + validateKey(key); + Entry entry = lruEntries.get(key); + if (entry == null) { + return null; + } + + if (!entry.readable) { + return null; + } + + // Open all streams eagerly to guarantee that we see a single published + // snapshot. If we opened streams lazily then the streams could come + // from different edits. + InputStream[] ins = new InputStream[valueCount]; + try { + for (int i = 0; i < valueCount; i++) { + ins[i] = new FileInputStream(entry.getCleanFile(i)); + } + } catch (FileNotFoundException e) { + // A file must have been deleted manually! + for (int i = 0; i < valueCount; i++) { + if (ins[i] != null) { + Util.closeQuietly(ins[i]); + } else { + break; + } + } + return null; + } + + redundantOpCount++; + journalWriter.append(READ + ' ' + key + '\n'); + if (journalRebuildRequired()) { + executorService.submit(cleanupCallable); + } + + return new Snapshot(key, entry.sequenceNumber, ins, entry.lengths); + } + + /** + * Returns an editor for the entry named {@code key}, or null if another + * edit is in progress. + */ + public Editor edit(String key) throws IOException { + return edit(key, ANY_SEQUENCE_NUMBER); + } + + private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { + checkNotClosed(); + validateKey(key); + Entry entry = lruEntries.get(key); + if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null + || entry.sequenceNumber != expectedSequenceNumber)) { + return null; // Snapshot is stale. + } + if (entry == null) { + entry = new Entry(key); + lruEntries.put(key, entry); + } else if (entry.currentEditor != null) { + return null; // Another edit is in progress. + } + + Editor editor = new Editor(entry); + entry.currentEditor = editor; + + // Flush the journal before creating files to prevent file leaks. + journalWriter.write(DIRTY + ' ' + key + '\n'); + journalWriter.flush(); + return editor; + } + + /** Returns the directory where this cache stores its data. */ + public File getDirectory() { + return directory; + } + + /** + * Returns the maximum number of bytes that this cache should use to store + * its data. + */ + public synchronized long getMaxSize() { + return maxSize; + } + + /** + * Changes the maximum number of bytes the cache can store and queues a job + * to trim the existing store, if necessary. + */ + public synchronized void setMaxSize(long maxSize) { + this.maxSize = maxSize; + executorService.submit(cleanupCallable); + } + + /** + * Returns the number of bytes currently being used to store the values in + * this cache. This may be greater than the max size if a background + * deletion is pending. + */ + public synchronized long size() { + return size; + } + + private synchronized void completeEdit(Editor editor, boolean success) throws IOException { + Entry entry = editor.entry; + if (entry.currentEditor != editor) { + throw new IllegalStateException(); + } + + // If this edit is creating the entry for the first time, every index must have a value. + if (success && !entry.readable) { + for (int i = 0; i < valueCount; i++) { + if (!editor.written[i]) { + editor.abort(); + throw new IllegalStateException("Newly created entry didn't create value for index " + i); + } + if (!entry.getDirtyFile(i).exists()) { + editor.abort(); + return; + } + } + } + + for (int i = 0; i < valueCount; i++) { + File dirty = entry.getDirtyFile(i); + if (success) { + if (dirty.exists()) { + File clean = entry.getCleanFile(i); + dirty.renameTo(clean); + long oldLength = entry.lengths[i]; + long newLength = clean.length(); + entry.lengths[i] = newLength; + size = size - oldLength + newLength; + } + } else { + deleteIfExists(dirty); + } + } + + redundantOpCount++; + entry.currentEditor = null; + if (entry.readable | success) { + entry.readable = true; + journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); + if (success) { + entry.sequenceNumber = nextSequenceNumber++; + } + } else { + lruEntries.remove(entry.key); + journalWriter.write(REMOVE + ' ' + entry.key + '\n'); + } + journalWriter.flush(); + + if (size > maxSize || journalRebuildRequired()) { + executorService.submit(cleanupCallable); + } + } + + /** + * We only rebuild the journal when it will halve the size of the journal + * and eliminate at least 2000 ops. + */ + private boolean journalRebuildRequired() { + final int redundantOpCompactThreshold = 2000; + return redundantOpCount >= redundantOpCompactThreshold // + && redundantOpCount >= lruEntries.size(); + } + + /** + * Drops the entry for {@code key} if it exists and can be removed. Entries + * actively being edited cannot be removed. + * + * @return true if an entry was removed. + */ + public synchronized boolean remove(String key) throws IOException { + checkNotClosed(); + validateKey(key); + Entry entry = lruEntries.get(key); + if (entry == null || entry.currentEditor != null) { + return false; + } + + for (int i = 0; i < valueCount; i++) { + File file = entry.getCleanFile(i); + if (file.exists() && !file.delete()) { + throw new IOException("failed to delete " + file); + } + size -= entry.lengths[i]; + entry.lengths[i] = 0; + } + + redundantOpCount++; + journalWriter.append(REMOVE + ' ' + key + '\n'); + lruEntries.remove(key); + + if (journalRebuildRequired()) { + executorService.submit(cleanupCallable); + } + + return true; + } + + /** Returns true if this cache has been closed. */ + public synchronized boolean isClosed() { + return journalWriter == null; + } + + private void checkNotClosed() { + if (journalWriter == null) { + throw new IllegalStateException("cache is closed"); + } + } + + /** Force buffered operations to the filesystem. */ + public synchronized void flush() throws IOException { + checkNotClosed(); + trimToSize(); + journalWriter.flush(); + } + + /** Closes this cache. Stored values will remain on the filesystem. */ + public synchronized void close() throws IOException { + if (journalWriter == null) { + return; // Already closed. + } + for (Entry entry : new ArrayList(lruEntries.values())) { + if (entry.currentEditor != null) { + entry.currentEditor.abort(); + } + } + trimToSize(); + journalWriter.close(); + journalWriter = null; + } + + private void trimToSize() throws IOException { + while (size > maxSize) { + Map.Entry toEvict = lruEntries.entrySet().iterator().next(); + remove(toEvict.getKey()); + } + } + + /** + * Closes the cache and deletes all of its stored values. This will delete + * all files in the cache directory including files that weren't created by + * the cache. + */ + public void delete() throws IOException { + close(); + Util.deleteContents(directory); + } + + private void validateKey(String key) { + Matcher matcher = LEGAL_KEY_PATTERN.matcher(key); + if (!matcher.matches()) { + throw new IllegalArgumentException("keys must match regex " + + STRING_KEY_PATTERN + ": \"" + key + "\""); + } + } + + private static String inputStreamToString(InputStream in) throws IOException { + return Util.readFully(new InputStreamReader(in, Util.UTF_8)); + } + + /** A snapshot of the values for an entry. */ + public final class Snapshot implements Closeable { + private final String key; + private final long sequenceNumber; + private final InputStream[] ins; + private final long[] lengths; + + private Snapshot(String key, long sequenceNumber, InputStream[] ins, long[] lengths) { + this.key = key; + this.sequenceNumber = sequenceNumber; + this.ins = ins; + this.lengths = lengths; + } + + /** + * Returns an editor for this snapshot's entry, or null if either the + * entry has changed since this snapshot was created or if another edit + * is in progress. + */ + public Editor edit() throws IOException { + return DiskLruCache.this.edit(key, sequenceNumber); + } + + /** Returns the unbuffered stream with the value for {@code index}. */ + public InputStream getInputStream(int index) { + return ins[index]; + } + + /** Returns the string value for {@code index}. */ + public String getString(int index) throws IOException { + return inputStreamToString(getInputStream(index)); + } + + /** Returns the byte length of the value for {@code index}. */ + public long getLength(int index) { + return lengths[index]; + } + + public void close() { + for (InputStream in : ins) { + Util.closeQuietly(in); + } + } + } + + private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() { + @Override + public void write(int b) throws IOException { + // Eat all writes silently. Nom nom. + } + }; + + /** Edits the values for an entry. */ + public final class Editor { + private final Entry entry; + private final boolean[] written; + private boolean hasErrors; + private boolean committed; + + private Editor(Entry entry) { + this.entry = entry; + this.written = (entry.readable) ? null : new boolean[valueCount]; + } + + /** + * Returns an unbuffered input stream to read the last committed value, + * or null if no value has been committed. + */ + public InputStream newInputStream(int index) throws IOException { + synchronized (DiskLruCache.this) { + if (entry.currentEditor != this) { + throw new IllegalStateException(); + } + if (!entry.readable) { + return null; + } + try { + return new FileInputStream(entry.getCleanFile(index)); + } catch (FileNotFoundException e) { + return null; + } + } + } + + /** + * Returns the last committed value as a string, or null if no value + * has been committed. + */ + public String getString(int index) throws IOException { + InputStream in = newInputStream(index); + return in != null ? inputStreamToString(in) : null; + } + + /** + * Returns a new unbuffered output stream to write the value at + * {@code index}. If the underlying output stream encounters errors + * when writing to the filesystem, this edit will be aborted when + * {@link #commit} is called. The returned output stream does not throw + * IOExceptions. + */ + public OutputStream newOutputStream(int index) throws IOException { + if (index < 0 || index >= valueCount) { + throw new IllegalArgumentException("Expected index " + index + " to " + + "be greater than 0 and less than the maximum value count " + + "of " + valueCount); + } + synchronized (DiskLruCache.this) { + if (entry.currentEditor != this) { + throw new IllegalStateException(); + } + if (!entry.readable) { + written[index] = true; + } + File dirtyFile = entry.getDirtyFile(index); + FileOutputStream outputStream; + try { + outputStream = new FileOutputStream(dirtyFile); + } catch (FileNotFoundException e) { + // Attempt to recreate the cache directory. + directory.mkdirs(); + try { + outputStream = new FileOutputStream(dirtyFile); + } catch (FileNotFoundException e2) { + // We are unable to recover. Silently eat the writes. + return NULL_OUTPUT_STREAM; + } + } + return new FaultHidingOutputStream(outputStream); + } + } + + /** Sets the value at {@code index} to {@code value}. */ + public void set(int index, String value) throws IOException { + Writer writer = null; + try { + writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8); + writer.write(value); + } finally { + Util.closeQuietly(writer); + } + } + + /** + * Commits this edit so it is visible to readers. This releases the + * edit lock so another edit may be started on the same key. + */ + public void commit() throws IOException { + if (hasErrors) { + completeEdit(this, false); + remove(entry.key); // The previous entry is stale. + } else { + completeEdit(this, true); + } + committed = true; + } + + /** + * Aborts this edit. This releases the edit lock so another edit may be + * started on the same key. + */ + public void abort() throws IOException { + completeEdit(this, false); + } + + public void abortUnlessCommitted() { + if (!committed) { + try { + abort(); + } catch (IOException ignored) { + } + } + } + + private class FaultHidingOutputStream extends FilterOutputStream { + private FaultHidingOutputStream(OutputStream out) { + super(out); + } + + @Override public void write(int oneByte) { + try { + out.write(oneByte); + } catch (IOException e) { + hasErrors = true; + } + } + + @Override public void write(byte[] buffer, int offset, int length) { + try { + out.write(buffer, offset, length); + } catch (IOException e) { + hasErrors = true; + } + } + + @Override public void close() { + try { + out.close(); + } catch (IOException e) { + hasErrors = true; + } + } + + @Override public void flush() { + try { + out.flush(); + } catch (IOException e) { + hasErrors = true; + } + } + } + } + + private final class Entry { + private final String key; + + /** Lengths of this entry's files. */ + private final long[] lengths; + + /** True if this entry has ever been published. */ + private boolean readable; + + /** The ongoing edit or null if this entry is not being edited. */ + private Editor currentEditor; + + /** The sequence number of the most recently committed edit to this entry. */ + private long sequenceNumber; + + private Entry(String key) { + this.key = key; + this.lengths = new long[valueCount]; + } + + public String getLengths() throws IOException { + StringBuilder result = new StringBuilder(); + for (long size : lengths) { + result.append(' ').append(size); + } + return result.toString(); + } + + /** Set lengths using decimal numbers like "10123". */ + private void setLengths(String[] strings) throws IOException { + if (strings.length != valueCount) { + throw invalidLengths(strings); + } + + try { + for (int i = 0; i < strings.length; i++) { + lengths[i] = Long.parseLong(strings[i]); + } + } catch (NumberFormatException e) { + throw invalidLengths(strings); + } + } + + private IOException invalidLengths(String[] strings) throws IOException { + throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings)); + } + + public File getCleanFile(int i) { + return new File(directory, key + "." + i); + } + + public File getDirtyFile(int i) { + return new File(directory, key + "." + i + ".tmp"); + } + } +} diff --git a/mobile/android/thirdparty/com/jakewharton/disklrucache/StrictLineReader.java b/mobile/android/thirdparty/com/jakewharton/disklrucache/StrictLineReader.java new file mode 100644 index 000000000..c90691ce4 --- /dev/null +++ b/mobile/android/thirdparty/com/jakewharton/disklrucache/StrictLineReader.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jakewharton.disklrucache; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + * Buffers input from an {@link InputStream} for reading lines. + * + *

    This class is used for buffered reading of lines. For purposes of this class, a line ends + * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated + * line at end of input is invalid and will be ignored, the caller may use {@code + * hasUnterminatedLine()} to detect it after catching the {@code EOFException}. + * + *

    This class is intended for reading input that strictly consists of lines, such as line-based + * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction + * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different + * end-of-input reporting and a more restrictive definition of a line. + * + *

    This class supports only charsets that encode '\r' and '\n' as a single byte with value 13 + * and 10, respectively, and the representation of no other character contains these values. + * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1. + * The default charset is US_ASCII. + */ +class StrictLineReader implements Closeable { + private static final byte CR = (byte) '\r'; + private static final byte LF = (byte) '\n'; + + private final InputStream in; + private final Charset charset; + + /* + * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end + * and the data in the range [pos, end) is buffered for reading. At end of input, if there is + * an unterminated line, we set end == -1, otherwise end == pos. If the underlying + * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1. + */ + private byte[] buf; + private int pos; + private int end; + + /** + * Constructs a new {@code LineReader} with the specified charset and the default capacity. + * + * @param in the {@code InputStream} to read data from. + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are + * supported. + * @throws NullPointerException if {@code in} or {@code charset} is null. + * @throws IllegalArgumentException if the specified charset is not supported. + */ + public StrictLineReader(InputStream in, Charset charset) { + this(in, 8192, charset); + } + + /** + * Constructs a new {@code LineReader} with the specified capacity and charset. + * + * @param in the {@code InputStream} to read data from. + * @param capacity the capacity of the buffer. + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are + * supported. + * @throws NullPointerException if {@code in} or {@code charset} is null. + * @throws IllegalArgumentException if {@code capacity} is negative or zero + * or the specified charset is not supported. + */ + public StrictLineReader(InputStream in, int capacity, Charset charset) { + if (in == null || charset == null) { + throw new NullPointerException(); + } + if (capacity < 0) { + throw new IllegalArgumentException("capacity <= 0"); + } + if (!(charset.equals(Util.US_ASCII))) { + throw new IllegalArgumentException("Unsupported encoding"); + } + + this.in = in; + this.charset = charset; + buf = new byte[capacity]; + } + + /** + * Closes the reader by closing the underlying {@code InputStream} and + * marking this reader as closed. + * + * @throws IOException for errors when closing the underlying {@code InputStream}. + */ + public void close() throws IOException { + synchronized (in) { + if (buf != null) { + buf = null; + in.close(); + } + } + } + + /** + * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"}, + * this end of line marker is not included in the result. + * + * @return the next line from the input. + * @throws IOException for underlying {@code InputStream} errors. + * @throws EOFException for the end of source stream. + */ + public String readLine() throws IOException { + synchronized (in) { + if (buf == null) { + throw new IOException("LineReader is closed"); + } + + // Read more data if we are at the end of the buffered data. + // Though it's an error to read after an exception, we will let {@code fillBuf()} + // throw again if that happens; thus we need to handle end == -1 as well as end == pos. + if (pos >= end) { + fillBuf(); + } + // Try to find LF in the buffered data and return the line if successful. + for (int i = pos; i != end; ++i) { + if (buf[i] == LF) { + int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i; + String res = new String(buf, pos, lineEnd - pos, charset.name()); + pos = i + 1; + return res; + } + } + + // Let's anticipate up to 80 characters on top of those already read. + ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) { + @Override + public String toString() { + int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count; + try { + return new String(buf, 0, length, charset.name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); // Since we control the charset this will never happen. + } + } + }; + + while (true) { + out.write(buf, pos, end - pos); + // Mark unterminated line in case fillBuf throws EOFException or IOException. + end = -1; + fillBuf(); + // Try to find LF in the buffered data and return the line if successful. + for (int i = pos; i != end; ++i) { + if (buf[i] == LF) { + if (i != pos) { + out.write(buf, pos, i - pos); + } + pos = i + 1; + return out.toString(); + } + } + } + } + } + + public boolean hasUnterminatedLine() { + return end == -1; + } + + /** + * Reads new input data into the buffer. Call only with pos == end or end == -1, + * depending on the desired outcome if the function throws. + */ + private void fillBuf() throws IOException { + int result = in.read(buf, 0, buf.length); + if (result == -1) { + throw new EOFException(); + } + pos = 0; + end = result; + } +} + diff --git a/mobile/android/thirdparty/com/jakewharton/disklrucache/Util.java b/mobile/android/thirdparty/com/jakewharton/disklrucache/Util.java new file mode 100644 index 000000000..0a7dba9bc --- /dev/null +++ b/mobile/android/thirdparty/com/jakewharton/disklrucache/Util.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.jakewharton.disklrucache; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.StringWriter; +import java.nio.charset.Charset; + +/** Junk drawer of utility methods. */ +final class Util { + static final Charset US_ASCII = Charset.forName("US-ASCII"); + static final Charset UTF_8 = Charset.forName("UTF-8"); + + private Util() { + } + + static String readFully(Reader reader) throws IOException { + try { + StringWriter writer = new StringWriter(); + char[] buffer = new char[1024]; + int count; + while ((count = reader.read(buffer)) != -1) { + writer.write(buffer, 0, count); + } + return writer.toString(); + } finally { + reader.close(); + } + } + + /** + * Deletes the contents of {@code dir}. Throws an IOException if any file + * could not be deleted, or if {@code dir} is not a readable directory. + */ + static void deleteContents(File dir) throws IOException { + File[] files = dir.listFiles(); + if (files == null) { + throw new IOException("not a readable directory: " + dir); + } + for (File file : files) { + if (file.isDirectory()) { + deleteContents(file); + } + if (!file.delete()) { + throw new IOException("failed to delete file: " + file); + } + } + } + + static void closeQuietly(/*Auto*/Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (RuntimeException rethrown) { + throw rethrown; + } catch (Exception ignored) { + } + } + } +} diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/AsyncConfigLoader.java b/mobile/android/thirdparty/com/keepsafe/switchboard/AsyncConfigLoader.java new file mode 100644 index 000000000..2cff4b4c3 --- /dev/null +++ b/mobile/android/thirdparty/com/keepsafe/switchboard/AsyncConfigLoader.java @@ -0,0 +1,54 @@ +/* + Copyright 2012 KeepSafe Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.keepsafe.switchboard; + + +import android.content.Context; +import android.os.AsyncTask; + +/** + * An async loader to load user config in background thread based on internal generated UUID. + * + * Call AsyncConfigLoader.execute() to load SwitchBoard.loadConfig() with own ID. + * To use your custom UUID call AsyncConfigLoader.execute(uuid) with uuid being your unique user id + * as a String + * + * @author Philipp Berner + * + */ +public class AsyncConfigLoader extends AsyncTask { + + private Context context; + private String defaultServerUrl; + + /** + * Sets the params for async loading either SwitchBoard.updateConfigServerUrl() + * or SwitchBoard.loadConfig. + * Loads config with a custom UUID + * @param c Application context + * @param defaultServerUrl Default URL endpoint for Switchboard config. + */ + public AsyncConfigLoader(Context c, String defaultServerUrl) { + this.context = c; + this.defaultServerUrl = defaultServerUrl; + } + + @Override + protected Void doInBackground(Void... params) { + SwitchBoard.loadConfig(context, defaultServerUrl); + return null; + } +} diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/DeviceUuidFactory.java b/mobile/android/thirdparty/com/keepsafe/switchboard/DeviceUuidFactory.java new file mode 100644 index 000000000..c4476d2cd --- /dev/null +++ b/mobile/android/thirdparty/com/keepsafe/switchboard/DeviceUuidFactory.java @@ -0,0 +1,70 @@ +/* + Copyright 2012 KeepSafe Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.keepsafe.switchboard; + +import java.util.UUID; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Generates a UUID and stores is persistent as in the apps shared preferences. + * + * @author Philipp Berner + */ +public class DeviceUuidFactory { + protected static final String PREFS_FILE = "com.keepsafe.switchboard.uuid"; + protected static final String PREFS_DEVICE_ID = "device_id"; + + private static UUID uuid = null; + + public DeviceUuidFactory(Context context) { + if (uuid == null) { + synchronized (DeviceUuidFactory.class) { + if (uuid == null) { + final SharedPreferences prefs = context + .getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE); + final String id = prefs.getString(PREFS_DEVICE_ID, null); + + if (id != null) { + // Use the ids previously computed and stored in the prefs file + uuid = UUID.fromString(id); + } else { + uuid = UUID.randomUUID(); + + // Write the value out to the prefs file + prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString()).apply(); + } + } + } + } + } + + /** + * Returns a unique UUID for the current android device. As with all UUIDs, + * this unique ID is "very highly likely" to be unique across all Android + * devices. Much more so than ANDROID_ID is. + * + * The UUID is generated with UUID.randomUUID(). + * + * @return a UUID that may be used to uniquely identify your device for most + * purposes. + */ + public UUID getDeviceUuid() { + return uuid; + } + +} \ No newline at end of file diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/Preferences.java b/mobile/android/thirdparty/com/keepsafe/switchboard/Preferences.java new file mode 100644 index 000000000..f7f6f7cb7 --- /dev/null +++ b/mobile/android/thirdparty/com/keepsafe/switchboard/Preferences.java @@ -0,0 +1,105 @@ +/* + Copyright 2012 KeepSafe Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.keepsafe.switchboard; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.Nullable; + +/** + * Application preferences for SwitchBoard. + * @author Philipp Berner + * + */ +public class Preferences { + + private static final String switchBoardSettings = "com.keepsafe.switchboard.settings"; + + private static final String CONFIG_JSON = "config-json"; + private static final String OVERRIDE_PREFIX = "experiment.override."; + + + /** + * Gets the user config as a JSON string. + * @param c Context + * @return Config JSON + */ + @Nullable public static String getDynamicConfigJson(Context c) { + final SharedPreferences prefs = c.getApplicationContext().getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE); + return prefs.getString(CONFIG_JSON, null); + } + + /** + * Saves the user config as a JSON sting. + * @param c Context + * @param configJson Config JSON + */ + public static void setDynamicConfigJson(Context c, String configJson) { + final SharedPreferences.Editor editor = c.getApplicationContext(). + getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE).edit(); + editor.putString(CONFIG_JSON, configJson); + editor.apply(); + } + + /** + * Gets the override value for an experiment. + * + * @param c Context + * @param experimentName Experiment name + * @return Whether or not the experiment should be enabled, or null if there is no override. + */ + @Nullable public static Boolean getOverrideValue(Context c, String experimentName) { + final SharedPreferences prefs = c.getApplicationContext(). + getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE); + + final String key = OVERRIDE_PREFIX + experimentName; + if (prefs.contains(key)) { + // This will never fall back to the default value. + return prefs.getBoolean(key, false); + } + + // Default to returning null if no override was found. + return null; + } + + /** + * Saves an override value for an experiment. + * + * @param c Context + * @param experimentName Experiment name + * @param isEnabled Whether or not to enable the experiment + */ + public static void setOverrideValue(Context c, String experimentName, boolean isEnabled) { + final SharedPreferences.Editor editor = c.getApplicationContext(). + getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE).edit(); + editor.putBoolean(OVERRIDE_PREFIX + experimentName, isEnabled); + editor.apply(); + } + + /** + * Clears the override value for an experiment. + * + * @param c Context + * @param experimentName Experiment name + */ + public static void clearOverrideValue(Context c, String experimentName) { + final SharedPreferences.Editor editor = c.getApplicationContext(). + getSharedPreferences(switchBoardSettings, Context.MODE_PRIVATE).edit(); + editor.remove(OVERRIDE_PREFIX + experimentName); + editor.apply(); + } +} diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/Switch.java b/mobile/android/thirdparty/com/keepsafe/switchboard/Switch.java new file mode 100644 index 000000000..5307750bb --- /dev/null +++ b/mobile/android/thirdparty/com/keepsafe/switchboard/Switch.java @@ -0,0 +1,72 @@ +/* + Copyright 2012 KeepSafe Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.keepsafe.switchboard; + +import org.json.JSONObject; + +import android.content.Context; + +/** + * Single instance of an existing experiment for easier and cleaner code. + * + * @author Philipp Berner + * + */ +public class Switch { + + private Context context; + private String experimentName; + + /** + * Creates an instance of a single experiment to give more convenient access to its values. + * When the given experiment does not exist, it will give back default valued that can be found + * in Switchboard. Developer has to know that experiment exists when using it. + * @param c Application context + * @param experimentName Name of the experiment as defined on the server + */ + public Switch(Context c, String experimentName) { + this.context = c; + this.experimentName = experimentName; + } + + /** + * Returns true if the experiment is active for this particular user. + * @return Status of the experiment and false when experiment does not exist. + */ + public boolean isActive() { + return SwitchBoard.isInExperiment(context, experimentName); + } + + /** + * Returns true if the experiment has additional values. + * @return true when values exist + */ + public boolean hasValues() { + return SwitchBoard.hasExperimentValues(context, experimentName); + } + + /** + * Gives back all the experiment values in a JSONObject. This function checks if + * values exists. If no values exist, it returns null. + * @return Values in JSONObject or null if non + */ + public JSONObject getValues() { + if(hasValues()) + return SwitchBoard.getExperimentValuesFromJson(context, experimentName); + else + return null; + } +} diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java b/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java new file mode 100644 index 000000000..e99144045 --- /dev/null +++ b/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java @@ -0,0 +1,390 @@ +/* + Copyright 2012 KeepSafe Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.keepsafe.switchboard; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.zip.CRC32; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONArray; + +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + + +/** + * SwitchBoard is the core class of the KeepSafe Switchboard mobile A/B testing framework. + * This class provides a bunch of static methods that can be used in your app to run A/B tests. + * + * The SwitchBoard supports production and staging environment. + * + * For usage initDefaultServerUrls for first time usage. Server URLs can be updates from + * a remote location with initConfigServerUrl. + * + * To run a experiment use isInExperiment(). The experiment name has to match the one you + * setup on the server. + * All functions are design to be safe for programming mistakes and network connection issues. If the + * experiment does not exists it will return false and pretend the user is not part of it. + * + * @author Philipp Berner + * + */ +public class SwitchBoard { + + private static final String TAG = "SwitchBoard"; + + /** Set if the application is run in debug mode. */ + public static boolean DEBUG = true; + + // Top-level experiment keys. + private static final String KEY_DATA = "data"; + private static final String KEY_NAME = "name"; + private static final String KEY_MATCH = "match"; + private static final String KEY_BUCKETS = "buckets"; + private static final String KEY_VALUES = "values"; + + // Match keys. + private static final String KEY_APP_ID = "appId"; + private static final String KEY_COUNTRY = "country"; + private static final String KEY_DEVICE = "device"; + private static final String KEY_LANG = "lang"; + private static final String KEY_MANUFACTURER = "manufacturer"; + private static final String KEY_VERSION = "version"; + + // Bucket keys. + private static final String KEY_MIN = "min"; + private static final String KEY_MAX = "max"; + + /** + * Loads a new config for a user. This method does network I/O, so it + * should not be called on the main thread. + * + * @param c ApplicationContext + * @param serverUrl Server URL endpoint. + */ + static void loadConfig(Context c, @NonNull String serverUrl) { + final URL url; + try { + url = new URL(serverUrl); + } catch (MalformedURLException e) { + Log.e(TAG, "Exception creating server URL", e); + return; + } + + final String result = readFromUrlGET(url); + if (DEBUG) Log.d(TAG, "Result: " + result); + if (result == null) { + return; + } + + // Cache result locally in shared preferences. + Preferences.setDynamicConfigJson(c, result); + } + + public static boolean isInBucket(Context c, int low, int high) { + final int userBucket = getUserBucket(c); + return (userBucket >= low) && (userBucket < high); + } + + /** + * Looks up in config if user is in certain experiment. Returns false as a default value when experiment + * does not exist. + * Experiment names are defined server side as Key in array for return values. + * @param experimentName Name of the experiment to lookup + * @return returns value for experiment or false if experiment does not exist. + */ + public static boolean isInExperiment(Context c, String experimentName) { + final Boolean override = Preferences.getOverrideValue(c, experimentName); + if (override != null) { + return override; + } + + final String config = Preferences.getDynamicConfigJson(c); + if (config == null) { + return false; + } + + try { + // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key + final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA); + JSONObject experiment = null; + + for (int i = 0; i < experiments.length(); i++) { + JSONObject entry = experiments.getJSONObject(i); + final String name = entry.getString(KEY_NAME); + if (name.equals(experimentName)) { + experiment = entry; + break; + } + } + + if (experiment == null) { + return false; + } + + if (!isMatch(c, experiment.optJSONObject(KEY_MATCH))) { + return false; + } + + final JSONObject buckets = experiment.getJSONObject(KEY_BUCKETS); + final boolean inExperiment = isInBucket(c, buckets.getInt(KEY_MIN), buckets.getInt(KEY_MAX)); + + if (DEBUG) { + Log.d(TAG, experimentName + " = " + inExperiment); + } + return inExperiment; + } catch (JSONException e) { + // If the experiment name is not found in the JSON, just return false. + // There is no need to log an error, since we don't really care if an + // inactive experiment is missing from the config. + return false; + } + } + + private static List getExperimentNames(Context c) throws JSONException { + // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key + final List returnList = new ArrayList<>(); + final String config = Preferences.getDynamicConfigJson(c); + final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA); + + for (int i = 0; i < experiments.length(); i++) { + JSONObject entry = experiments.getJSONObject(i); + returnList.add(entry.getString(KEY_NAME)); + } + return returnList; + } + + @Nullable + private static JSONObject getExperiment(Context c, String experimentName) throws JSONException { + // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key + final String config = Preferences.getDynamicConfigJson(c); + final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA); + JSONObject experiment = null; + + for (int i = 0; i < experiments.length(); i++) { + JSONObject entry = experiments.getJSONObject(i); + if (entry.getString(KEY_NAME).equals(experimentName)) { + experiment = entry; + break; + } + } + return experiment; + } + + private static boolean isMatch(Context c, @Nullable JSONObject matchKeys) { + // If no match keys are specified, default to enabling the experiment. + if (matchKeys == null) { + return true; + } + + if (matchKeys.has(KEY_APP_ID)) { + final String packageName = c.getPackageName(); + try { + if (!packageName.matches(matchKeys.getString(KEY_APP_ID))) { + return false; + } + } catch (JSONException e) { + Log.e(TAG, "Exception matching appId", e); + } + } + + if (matchKeys.has(KEY_COUNTRY)) { + try { + final String country = Locale.getDefault().getISO3Country(); + if (!country.matches(matchKeys.getString(KEY_COUNTRY))) { + return false; + } + } catch (MissingResourceException|JSONException e) { + Log.e(TAG, "Exception matching country", e); + } + } + + if (matchKeys.has(KEY_DEVICE)) { + final String device = Build.DEVICE; + try { + if (!device.matches(matchKeys.getString(KEY_DEVICE))) { + return false; + } + } catch (JSONException e) { + Log.e(TAG, "Exception matching device", e); + } + + } + if (matchKeys.has(KEY_LANG)) { + try { + final String lang = Locale.getDefault().getISO3Language(); + if (!lang.matches(matchKeys.getString(KEY_LANG))) { + return false; + } + } catch (MissingResourceException|JSONException e) { + Log.e(TAG, "Exception matching lang", e); + } + } + if (matchKeys.has(KEY_MANUFACTURER)) { + final String manufacturer = Build.MANUFACTURER; + try { + if (!manufacturer.matches(matchKeys.getString(KEY_MANUFACTURER))) { + return false; + } + } catch (JSONException e) { + Log.e(TAG, "Exception matching manufacturer", e); + } + } + + if (matchKeys.has(KEY_VERSION)) { + try { + final String version = c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName; + if (!version.matches(matchKeys.getString(KEY_VERSION))) { + return false; + } + } catch (NameNotFoundException|JSONException e) { + Log.e(TAG, "Exception matching version", e); + } + } + + // Default to return true if no matches failed. + return true; + } + + /** + * @return a list of all active experiments. + */ + public static List getActiveExperiments(Context c) { + final List returnList = new ArrayList<>(); + + final String config = Preferences.getDynamicConfigJson(c); + if (config == null) { + return returnList; + } + + try { + final JSONObject data = new JSONObject(config); + final List experiments = getExperimentNames(c); + + for (int i = 0; i < experiments.size(); i++) { + final String name = experiments.get(i); + + // Check override value before reading saved JSON. + Boolean isActive = Preferences.getOverrideValue(c, name); + if (isActive == null) { + // TODO: This is inefficient because it will check all the match cases on all experiments. + isActive = isInExperiment(c, name); + } + if (isActive) { + returnList.add(name); + } + } + } catch (JSONException e) { + // Something went wrong! + } + + return returnList; + } + + /** + * Checks if a certain experiment has additional values. + * @param c ApplicationContext + * @param experimentName Name of the experiment + * @return true when experiment exists + */ + public static boolean hasExperimentValues(Context c, String experimentName) { + return getExperimentValuesFromJson(c, experimentName) != null; + } + + /** + * Returns the experiment value as a JSONObject. + * @param experimentName Name of the experiment + * @return Experiment value as String, null if experiment does not exist. + */ + @Nullable + public static JSONObject getExperimentValuesFromJson(Context c, String experimentName) { + final String config = Preferences.getDynamicConfigJson(c); + + if (config == null) { + return null; + } + + try { + final JSONObject experiment = getExperiment(c, experimentName); + if (experiment == null) { + return null; + } + return experiment.getJSONObject(KEY_VALUES); + } catch (JSONException e) { + Log.e(TAG, "Could not create JSON object from config string", e); + } + + return null; + } + + /** + * Returns a String containing the server response from a GET request + * @param url URL for GET request. + * @return Returns String from server or null when failed. + */ + @Nullable private static String readFromUrlGET(URL url) { + try { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setUseCaches(false); + + InputStream is = connection.getInputStream(); + InputStreamReader inputStreamReader = new InputStreamReader(is); + BufferedReader bufferReader = new BufferedReader(inputStreamReader, 8192); + String line; + StringBuilder resultContent = new StringBuilder(); + while ((line = bufferReader.readLine()) != null) { + resultContent.append(line); + } + bufferReader.close(); + + return resultContent.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + /** + * Return the bucket number of the user. There are 100 possible buckets. + */ + private static int getUserBucket(Context c) { + final DeviceUuidFactory df = new DeviceUuidFactory(c); + final String uuid = df.getDeviceUuid().toString(); + + CRC32 crc = new CRC32(); + crc.update(uuid.getBytes()); + long checksum = crc.getValue(); + return (int)(checksum % 100L); + } +} diff --git a/mobile/android/thirdparty/com/squareup/leakcanary/LeakCanary.java b/mobile/android/thirdparty/com/squareup/leakcanary/LeakCanary.java new file mode 100644 index 000000000..a9e44437d --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/leakcanary/LeakCanary.java @@ -0,0 +1,21 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.squareup.leakcanary; + +import android.app.Application; + +/** + * A no-op version of {@link LeakCanary} that can be used in release builds. + */ +public final class LeakCanary { + public static RefWatcher install(Application application) { + return RefWatcher.DISABLED; + } + + private LeakCanary() { + throw new AssertionError(); + } +} diff --git a/mobile/android/thirdparty/com/squareup/leakcanary/RefWatcher.java b/mobile/android/thirdparty/com/squareup/leakcanary/RefWatcher.java new file mode 100644 index 000000000..3c75dcdee --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/leakcanary/RefWatcher.java @@ -0,0 +1,20 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package com.squareup.leakcanary; + +/** + * No-op implementation of {@link RefWatcher} for release builds. Please use {@link + * RefWatcher#DISABLED}. + */ +public final class RefWatcher { + public static final RefWatcher DISABLED = new RefWatcher(); + + private RefWatcher() {} + + public void watch(Object watchedReference) {} + + public void watch(Object watchedReference, String referenceName) {} +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Action.java b/mobile/android/thirdparty/com/squareup/picasso/Action.java new file mode 100644 index 000000000..5f176d770 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Action.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +abstract class Action { + static class RequestWeakReference extends WeakReference { + final Action action; + + public RequestWeakReference(Action action, T referent, ReferenceQueue q) { + super(referent, q); + this.action = action; + } + } + + final Picasso picasso; + final Request data; + final WeakReference target; + final boolean skipCache; + final boolean noFade; + final int errorResId; + final Drawable errorDrawable; + final String key; + + boolean cancelled; + + Action(Picasso picasso, T target, Request data, boolean skipCache, boolean noFade, + int errorResId, Drawable errorDrawable, String key) { + this.picasso = picasso; + this.data = data; + this.target = new RequestWeakReference(this, target, picasso.referenceQueue); + this.skipCache = skipCache; + this.noFade = noFade; + this.errorResId = errorResId; + this.errorDrawable = errorDrawable; + this.key = key; + } + + abstract void complete(Bitmap result, Picasso.LoadedFrom from); + + abstract void error(); + + void cancel() { + cancelled = true; + } + + Request getData() { + return data; + } + + T getTarget() { + return target.get(); + } + + String getKey() { + return key; + } + + boolean isCancelled() { + return cancelled; + } + + Picasso getPicasso() { + return picasso; + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/AssetBitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/AssetBitmapHunter.java new file mode 100644 index 000000000..c0245ed3f --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/AssetBitmapHunter.java @@ -0,0 +1,51 @@ +package com.squareup.picasso; + +import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import java.io.IOException; +import java.io.InputStream; + +import static com.squareup.picasso.Picasso.LoadedFrom.DISK; + +class AssetBitmapHunter extends BitmapHunter { + private AssetManager assetManager; + + public AssetBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, + Stats stats, Action action) { + super(picasso, dispatcher, cache, stats, action); + assetManager = context.getAssets(); + } + + @Override Bitmap decode(Request data) throws IOException { + String filePath = data.uri.toString().substring(ASSET_PREFIX_LENGTH); + return decodeAsset(filePath); + } + + @Override Picasso.LoadedFrom getLoadedFrom() { + return DISK; + } + + Bitmap decodeAsset(String filePath) throws IOException { + BitmapFactory.Options options = null; + if (data.hasSize()) { + options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + InputStream is = null; + try { + is = assetManager.open(filePath); + BitmapFactory.decodeStream(is, null, options); + } finally { + Utils.closeQuietly(is); + } + calculateInSampleSize(data.targetWidth, data.targetHeight, options); + } + InputStream is = assetManager.open(filePath); + try { + return BitmapFactory.decodeStream(is, null, options); + } finally { + Utils.closeQuietly(is); + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/BitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/BitmapHunter.java new file mode 100644 index 000000000..b8aa87c48 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/BitmapHunter.java @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.net.NetworkInfo; +import android.net.Uri; +import android.provider.MediaStore; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE; +import static android.content.ContentResolver.SCHEME_CONTENT; +import static android.content.ContentResolver.SCHEME_FILE; +import static android.provider.ContactsContract.Contacts; +import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY; + +abstract class BitmapHunter implements Runnable { + + /** + * Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since + * this will only ever happen in background threads we help avoid excessive memory thrashing as + * well as potential OOMs. Shamelessly stolen from Volley. + */ + private static final Object DECODE_LOCK = new Object(); + private static final String ANDROID_ASSET = "android_asset"; + protected static final int ASSET_PREFIX_LENGTH = + (SCHEME_FILE + ":///" + ANDROID_ASSET + "/").length(); + + final Picasso picasso; + final Dispatcher dispatcher; + final Cache cache; + final Stats stats; + final String key; + final Request data; + final List actions; + final boolean skipMemoryCache; + + Bitmap result; + Future future; + Picasso.LoadedFrom loadedFrom; + Exception exception; + int exifRotation; // Determined during decoding of original resource. + + BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) { + this.picasso = picasso; + this.dispatcher = dispatcher; + this.cache = cache; + this.stats = stats; + this.key = action.getKey(); + this.data = action.getData(); + this.skipMemoryCache = action.skipCache; + this.actions = new ArrayList(4); + attach(action); + } + + protected void setExifRotation(int exifRotation) { + this.exifRotation = exifRotation; + } + + @Override public void run() { + try { + Thread.currentThread().setName(Utils.THREAD_PREFIX + data.getName()); + + result = hunt(); + + if (result == null) { + dispatcher.dispatchFailed(this); + } else { + dispatcher.dispatchComplete(this); + } + } catch (Downloader.ResponseException e) { + exception = e; + dispatcher.dispatchFailed(this); + } catch (IOException e) { + exception = e; + dispatcher.dispatchRetry(this); + } catch (OutOfMemoryError e) { + StringWriter writer = new StringWriter(); + stats.createSnapshot().dump(new PrintWriter(writer)); + exception = new RuntimeException(writer.toString(), e); + dispatcher.dispatchFailed(this); + } catch (Exception e) { + exception = e; + dispatcher.dispatchFailed(this); + } finally { + Thread.currentThread().setName(Utils.THREAD_IDLE_NAME); + } + } + + abstract Bitmap decode(Request data) throws IOException; + + Bitmap hunt() throws IOException { + Bitmap bitmap; + + if (!skipMemoryCache) { + bitmap = cache.get(key); + if (bitmap != null) { + stats.dispatchCacheHit(); + loadedFrom = MEMORY; + return bitmap; + } + } + + bitmap = decode(data); + + if (bitmap != null) { + stats.dispatchBitmapDecoded(bitmap); + if (data.needsTransformation() || exifRotation != 0) { + synchronized (DECODE_LOCK) { + if (data.needsMatrixTransform() || exifRotation != 0) { + bitmap = transformResult(data, bitmap, exifRotation); + } + if (data.hasCustomTransformations()) { + bitmap = applyCustomTransformations(data.transformations, bitmap); + } + } + stats.dispatchBitmapTransformed(bitmap); + } + } + + return bitmap; + } + + void attach(Action action) { + actions.add(action); + } + + void detach(Action action) { + actions.remove(action); + } + + boolean cancel() { + return actions.isEmpty() && future != null && future.cancel(false); + } + + boolean isCancelled() { + return future != null && future.isCancelled(); + } + + boolean shouldSkipMemoryCache() { + return skipMemoryCache; + } + + boolean shouldRetry(boolean airplaneMode, NetworkInfo info) { + return false; + } + + Bitmap getResult() { + return result; + } + + String getKey() { + return key; + } + + Request getData() { + return data; + } + + List getActions() { + return actions; + } + + Exception getException() { + return exception; + } + + Picasso.LoadedFrom getLoadedFrom() { + return loadedFrom; + } + + static BitmapHunter forRequest(Context context, Picasso picasso, Dispatcher dispatcher, + Cache cache, Stats stats, Action action, Downloader downloader) { + if (action.getData().resourceId != 0) { + return new ResourceBitmapHunter(context, picasso, dispatcher, cache, stats, action); + } + Uri uri = action.getData().uri; + String scheme = uri.getScheme(); + if (SCHEME_CONTENT.equals(scheme)) { + if (Contacts.CONTENT_URI.getHost().equals(uri.getHost()) // + && !uri.getPathSegments().contains(Contacts.Photo.CONTENT_DIRECTORY)) { + return new ContactsPhotoBitmapHunter(context, picasso, dispatcher, cache, stats, action); + } else if (MediaStore.AUTHORITY.equals(uri.getAuthority())) { + return new MediaStoreBitmapHunter(context, picasso, dispatcher, cache, stats, action); + } else { + return new ContentStreamBitmapHunter(context, picasso, dispatcher, cache, stats, action); + } + } else if (SCHEME_FILE.equals(scheme)) { + if (!uri.getPathSegments().isEmpty() && ANDROID_ASSET.equals(uri.getPathSegments().get(0))) { + return new AssetBitmapHunter(context, picasso, dispatcher, cache, stats, action); + } + return new FileBitmapHunter(context, picasso, dispatcher, cache, stats, action); + } else if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { + return new ResourceBitmapHunter(context, picasso, dispatcher, cache, stats, action); + } else { + return new NetworkBitmapHunter(picasso, dispatcher, cache, stats, action, downloader); + } + } + + static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options) { + calculateInSampleSize(reqWidth, reqHeight, options.outWidth, options.outHeight, options); + } + + static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height, + BitmapFactory.Options options) { + int sampleSize = 1; + if (height > reqHeight || width > reqWidth) { + final int heightRatio = Math.round((float) height / (float) reqHeight); + final int widthRatio = Math.round((float) width / (float) reqWidth); + sampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; + } + + options.inSampleSize = sampleSize; + options.inJustDecodeBounds = false; + } + + static Bitmap applyCustomTransformations(List transformations, Bitmap result) { + for (int i = 0, count = transformations.size(); i < count; i++) { + final Transformation transformation = transformations.get(i); + Bitmap newResult = transformation.transform(result); + + if (newResult == null) { + final StringBuilder builder = new StringBuilder() // + .append("Transformation ") + .append(transformation.key()) + .append(" returned null after ") + .append(i) + .append(" previous transformation(s).\n\nTransformation list:\n"); + for (Transformation t : transformations) { + builder.append(t.key()).append('\n'); + } + Picasso.HANDLER.post(new Runnable() { + @Override public void run() { + throw new NullPointerException(builder.toString()); + } + }); + return null; + } + + if (newResult == result && result.isRecycled()) { + Picasso.HANDLER.post(new Runnable() { + @Override public void run() { + throw new IllegalStateException("Transformation " + + transformation.key() + + " returned input Bitmap but recycled it."); + } + }); + return null; + } + + // If the transformation returned a new bitmap ensure they recycled the original. + if (newResult != result && !result.isRecycled()) { + Picasso.HANDLER.post(new Runnable() { + @Override public void run() { + throw new IllegalStateException("Transformation " + + transformation.key() + + " mutated input Bitmap but failed to recycle the original."); + } + }); + return null; + } + + result = newResult; + } + return result; + } + + static Bitmap transformResult(Request data, Bitmap result, int exifRotation) { + int inWidth = result.getWidth(); + int inHeight = result.getHeight(); + + int drawX = 0; + int drawY = 0; + int drawWidth = inWidth; + int drawHeight = inHeight; + + Matrix matrix = new Matrix(); + + if (data.needsMatrixTransform()) { + int targetWidth = data.targetWidth; + int targetHeight = data.targetHeight; + + float targetRotation = data.rotationDegrees; + if (targetRotation != 0) { + if (data.hasRotationPivot) { + matrix.setRotate(targetRotation, data.rotationPivotX, data.rotationPivotY); + } else { + matrix.setRotate(targetRotation); + } + } + + if (data.centerCrop) { + float widthRatio = targetWidth / (float) inWidth; + float heightRatio = targetHeight / (float) inHeight; + float scale; + if (widthRatio > heightRatio) { + scale = widthRatio; + int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio)); + drawY = (inHeight - newSize) / 2; + drawHeight = newSize; + } else { + scale = heightRatio; + int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio)); + drawX = (inWidth - newSize) / 2; + drawWidth = newSize; + } + matrix.preScale(scale, scale); + } else if (data.centerInside) { + float widthRatio = targetWidth / (float) inWidth; + float heightRatio = targetHeight / (float) inHeight; + float scale = widthRatio < heightRatio ? widthRatio : heightRatio; + matrix.preScale(scale, scale); + } else if (targetWidth != 0 && targetHeight != 0 // + && (targetWidth != inWidth || targetHeight != inHeight)) { + // If an explicit target size has been specified and they do not match the results bounds, + // pre-scale the existing matrix appropriately. + float sx = targetWidth / (float) inWidth; + float sy = targetHeight / (float) inHeight; + matrix.preScale(sx, sy); + } + } + + if (exifRotation != 0) { + matrix.preRotate(exifRotation); + } + + Bitmap newResult = + Bitmap.createBitmap(result, drawX, drawY, drawWidth, drawHeight, matrix, true); + if (newResult != result) { + result.recycle(); + result = newResult; + } + + return result; + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Cache.java b/mobile/android/thirdparty/com/squareup/picasso/Cache.java new file mode 100644 index 000000000..bce297f9e --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Cache.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; + +/** + * A memory cache for storing the most recently used images. + *

    + * Note: The {@link Cache} is accessed by multiple threads. You must ensure + * your {@link Cache} implementation is thread safe when {@link Cache#get(String)} or {@link + * Cache#set(String, android.graphics.Bitmap)} is called. + */ +public interface Cache { + /** Retrieve an image for the specified {@code key} or {@code null}. */ + Bitmap get(String key); + + /** Store an image in the cache for the specified {@code key}. */ + void set(String key, Bitmap bitmap); + + /** Returns the current size of the cache in bytes. */ + int size(); + + /** Returns the maximum size in bytes that the cache can hold. */ + int maxSize(); + + /** Clears the cache. */ + void clear(); + + /** A cache which does not store any values. */ + Cache NONE = new Cache() { + @Override public Bitmap get(String key) { + return null; + } + + @Override public void set(String key, Bitmap bitmap) { + // Ignore. + } + + @Override public int size() { + return 0; + } + + @Override public int maxSize() { + return 0; + } + + @Override public void clear() { + } + }; +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Callback.java b/mobile/android/thirdparty/com/squareup/picasso/Callback.java new file mode 100644 index 000000000..d93620889 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Callback.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +public interface Callback { + void onSuccess(); + + void onError(); + + public static class EmptyCallback implements Callback { + + @Override public void onSuccess() { + } + + @Override public void onError() { + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/ContactsPhotoBitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/ContactsPhotoBitmapHunter.java new file mode 100644 index 000000000..9444167b4 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/ContactsPhotoBitmapHunter.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.content.Context; +import android.content.UriMatcher; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.provider.ContactsContract; + +import java.io.IOException; +import java.io.InputStream; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; +import static android.provider.ContactsContract.Contacts.openContactPhotoInputStream; +import static com.squareup.picasso.Picasso.LoadedFrom.DISK; + +class ContactsPhotoBitmapHunter extends BitmapHunter { + /** A lookup uri (e.g. content://com.android.contacts/contacts/lookup/3570i61d948d30808e537) */ + private static final int ID_LOOKUP = 1; + /** A contact thumbnail uri (e.g. content://com.android.contacts/contacts/38/photo) */ + private static final int ID_THUMBNAIL = 2; + /** A contact uri (e.g. content://com.android.contacts/contacts/38) */ + private static final int ID_CONTACT = 3; + /** + * A contact display photo (high resolution) uri + * (e.g. content://com.android.contacts/display_photo/5) + */ + private static final int ID_DISPLAY_PHOTO = 4; + + private static final UriMatcher matcher; + + static { + matcher = new UriMatcher(UriMatcher.NO_MATCH); + matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", ID_LOOKUP); + matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", ID_LOOKUP); + matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", ID_THUMBNAIL); + matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", ID_CONTACT); + matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", ID_DISPLAY_PHOTO); + } + + final Context context; + + ContactsPhotoBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, + Stats stats, Action action) { + super(picasso, dispatcher, cache, stats, action); + this.context = context; + } + + @Override Bitmap decode(Request data) throws IOException { + InputStream is = null; + try { + is = getInputStream(); + return decodeStream(is, data); + } finally { + Utils.closeQuietly(is); + } + } + + @Override Picasso.LoadedFrom getLoadedFrom() { + return DISK; + } + + private InputStream getInputStream() throws IOException { + ContentResolver contentResolver = context.getContentResolver(); + Uri uri = getData().uri; + switch (matcher.match(uri)) { + case ID_LOOKUP: + uri = ContactsContract.Contacts.lookupContact(contentResolver, uri); + if (uri == null) { + return null; + } + // Resolved the uri to a contact uri, intentionally fall through to process the resolved uri + case ID_CONTACT: + if (SDK_INT < ICE_CREAM_SANDWICH) { + return openContactPhotoInputStream(contentResolver, uri); + } else { + return ContactPhotoStreamIcs.get(contentResolver, uri); + } + case ID_THUMBNAIL: + case ID_DISPLAY_PHOTO: + return contentResolver.openInputStream(uri); + default: + throw new IllegalStateException("Invalid uri: " + uri); + } + } + + private Bitmap decodeStream(InputStream stream, Request data) throws IOException { + if (stream == null) { + return null; + } + BitmapFactory.Options options = null; + if (data.hasSize()) { + options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + InputStream is = getInputStream(); + try { + BitmapFactory.decodeStream(is, null, options); + } finally { + Utils.closeQuietly(is); + } + calculateInSampleSize(data.targetWidth, data.targetHeight, options); + } + return BitmapFactory.decodeStream(stream, null, options); + } + + @TargetApi(ICE_CREAM_SANDWICH) + private static class ContactPhotoStreamIcs { + static InputStream get(ContentResolver contentResolver, Uri uri) { + return openContactPhotoInputStream(contentResolver, uri, true); + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/ContentStreamBitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/ContentStreamBitmapHunter.java new file mode 100644 index 000000000..624ffe078 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/ContentStreamBitmapHunter.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.ContentResolver; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import java.io.IOException; +import java.io.InputStream; + +import static com.squareup.picasso.Picasso.LoadedFrom.DISK; + +class ContentStreamBitmapHunter extends BitmapHunter { + final Context context; + + ContentStreamBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, + Stats stats, Action action) { + super(picasso, dispatcher, cache, stats, action); + this.context = context; + } + + @Override Bitmap decode(Request data) + throws IOException { + return decodeContentStream(data); + } + + @Override Picasso.LoadedFrom getLoadedFrom() { + return DISK; + } + + protected Bitmap decodeContentStream(Request data) throws IOException { + ContentResolver contentResolver = context.getContentResolver(); + BitmapFactory.Options options = null; + if (data.hasSize()) { + options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + InputStream is = null; + try { + is = contentResolver.openInputStream(data.uri); + BitmapFactory.decodeStream(is, null, options); + } finally { + Utils.closeQuietly(is); + } + calculateInSampleSize(data.targetWidth, data.targetHeight, options); + } + InputStream is = contentResolver.openInputStream(data.uri); + try { + return BitmapFactory.decodeStream(is, null, options); + } finally { + Utils.closeQuietly(is); + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/DeferredRequestCreator.java b/mobile/android/thirdparty/com/squareup/picasso/DeferredRequestCreator.java new file mode 100644 index 000000000..fbdaab1c3 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/DeferredRequestCreator.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import java.lang.ref.WeakReference; + +class DeferredRequestCreator implements ViewTreeObserver.OnPreDrawListener { + + final RequestCreator creator; + final WeakReference target; + Callback callback; + + DeferredRequestCreator(RequestCreator creator, ImageView target, Callback callback) { + this.creator = creator; + this.target = new WeakReference(target); + this.callback = callback; + target.getViewTreeObserver().addOnPreDrawListener(this); + } + + @Override public boolean onPreDraw() { + ImageView target = this.target.get(); + if (target == null) { + return true; + } + ViewTreeObserver vto = target.getViewTreeObserver(); + if (!vto.isAlive()) { + return true; + } + + int width = target.getMeasuredWidth(); + int height = target.getMeasuredHeight(); + + if (width <= 0 || height <= 0) { + return true; + } + + vto.removeOnPreDrawListener(this); + + this.creator.unfit().resize(width, height).into(target, callback); + return true; + } + + void cancel() { + callback = null; + ImageView target = this.target.get(); + if (target == null) { + return; + } + ViewTreeObserver vto = target.getViewTreeObserver(); + if (!vto.isAlive()) { + return; + } + vto.removeOnPreDrawListener(this); + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java b/mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java new file mode 100644 index 000000000..6401431fb --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; + +import static android.content.Context.CONNECTIVITY_SERVICE; +import static android.content.Intent.ACTION_AIRPLANE_MODE_CHANGED; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; +import static com.squareup.picasso.BitmapHunter.forRequest; + +class Dispatcher { + private static final int RETRY_DELAY = 500; + private static final int AIRPLANE_MODE_ON = 1; + private static final int AIRPLANE_MODE_OFF = 0; + + static final int REQUEST_SUBMIT = 1; + static final int REQUEST_CANCEL = 2; + static final int REQUEST_GCED = 3; + static final int HUNTER_COMPLETE = 4; + static final int HUNTER_RETRY = 5; + static final int HUNTER_DECODE_FAILED = 6; + static final int HUNTER_DELAY_NEXT_BATCH = 7; + static final int HUNTER_BATCH_COMPLETE = 8; + static final int NETWORK_STATE_CHANGE = 9; + static final int AIRPLANE_MODE_CHANGE = 10; + + private static final String DISPATCHER_THREAD_NAME = "Dispatcher"; + private static final int BATCH_DELAY = 200; // ms + + final DispatcherThread dispatcherThread; + final Context context; + final ExecutorService service; + final Downloader downloader; + final Map hunterMap; + final Handler handler; + final Handler mainThreadHandler; + final Cache cache; + final Stats stats; + final List batch; + final NetworkBroadcastReceiver receiver; + + NetworkInfo networkInfo; + boolean airplaneMode; + + Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler, + Downloader downloader, Cache cache, Stats stats) { + this.dispatcherThread = new DispatcherThread(); + this.dispatcherThread.start(); + this.context = context; + this.service = service; + this.hunterMap = new LinkedHashMap(); + this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this); + this.downloader = downloader; + this.mainThreadHandler = mainThreadHandler; + this.cache = cache; + this.stats = stats; + this.batch = new ArrayList(4); + this.airplaneMode = Utils.isAirplaneModeOn(this.context); + this.receiver = new NetworkBroadcastReceiver(this.context); + receiver.register(); + } + + void shutdown() { + service.shutdown(); + dispatcherThread.quit(); + receiver.unregister(); + } + + void dispatchSubmit(Action action) { + handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action)); + } + + void dispatchCancel(Action action) { + handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action)); + } + + void dispatchComplete(BitmapHunter hunter) { + handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter)); + } + + void dispatchRetry(BitmapHunter hunter) { + handler.sendMessageDelayed(handler.obtainMessage(HUNTER_RETRY, hunter), RETRY_DELAY); + } + + void dispatchFailed(BitmapHunter hunter) { + handler.sendMessage(handler.obtainMessage(HUNTER_DECODE_FAILED, hunter)); + } + + void dispatchNetworkStateChange(NetworkInfo info) { + handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info)); + } + + void dispatchAirplaneModeChange(boolean airplaneMode) { + handler.sendMessage(handler.obtainMessage(AIRPLANE_MODE_CHANGE, + airplaneMode ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF, 0)); + } + + void performSubmit(Action action) { + BitmapHunter hunter = hunterMap.get(action.getKey()); + if (hunter != null) { + hunter.attach(action); + return; + } + + if (service.isShutdown()) { + return; + } + + hunter = forRequest(context, action.getPicasso(), this, cache, stats, action, downloader); + hunter.future = service.submit(hunter); + hunterMap.put(action.getKey(), hunter); + } + + void performCancel(Action action) { + String key = action.getKey(); + BitmapHunter hunter = hunterMap.get(key); + if (hunter != null) { + hunter.detach(action); + if (hunter.cancel()) { + hunterMap.remove(key); + } + } + } + + void performRetry(BitmapHunter hunter) { + if (hunter.isCancelled()) return; + + if (service.isShutdown()) { + performError(hunter); + return; + } + + if (hunter.shouldRetry(airplaneMode, networkInfo)) { + hunter.future = service.submit(hunter); + } else { + performError(hunter); + } + } + + void performComplete(BitmapHunter hunter) { + if (!hunter.shouldSkipMemoryCache()) { + cache.set(hunter.getKey(), hunter.getResult()); + } + hunterMap.remove(hunter.getKey()); + batch(hunter); + } + + void performBatchComplete() { + List copy = new ArrayList(batch); + batch.clear(); + mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy)); + } + + void performError(BitmapHunter hunter) { + hunterMap.remove(hunter.getKey()); + batch(hunter); + } + + void performAirplaneModeChange(boolean airplaneMode) { + this.airplaneMode = airplaneMode; + } + + void performNetworkStateChange(NetworkInfo info) { + networkInfo = info; + if (service instanceof PicassoExecutorService) { + ((PicassoExecutorService) service).adjustThreadCount(info); + } + } + + private void batch(BitmapHunter hunter) { + if (hunter.isCancelled()) { + return; + } + batch.add(hunter); + if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) { + handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY); + } + } + + private static class DispatcherHandler extends Handler { + private final Dispatcher dispatcher; + + public DispatcherHandler(Looper looper, Dispatcher dispatcher) { + super(looper); + this.dispatcher = dispatcher; + } + + @Override public void handleMessage(final Message msg) { + switch (msg.what) { + case REQUEST_SUBMIT: { + Action action = (Action) msg.obj; + dispatcher.performSubmit(action); + break; + } + case REQUEST_CANCEL: { + Action action = (Action) msg.obj; + dispatcher.performCancel(action); + break; + } + case HUNTER_COMPLETE: { + BitmapHunter hunter = (BitmapHunter) msg.obj; + dispatcher.performComplete(hunter); + break; + } + case HUNTER_RETRY: { + BitmapHunter hunter = (BitmapHunter) msg.obj; + dispatcher.performRetry(hunter); + break; + } + case HUNTER_DECODE_FAILED: { + BitmapHunter hunter = (BitmapHunter) msg.obj; + dispatcher.performError(hunter); + break; + } + case HUNTER_DELAY_NEXT_BATCH: { + dispatcher.performBatchComplete(); + break; + } + case NETWORK_STATE_CHANGE: { + NetworkInfo info = (NetworkInfo) msg.obj; + dispatcher.performNetworkStateChange(info); + break; + } + case AIRPLANE_MODE_CHANGE: { + dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON); + break; + } + default: + Picasso.HANDLER.post(new Runnable() { + @Override public void run() { + throw new AssertionError("Unknown handler message received: " + msg.what); + } + }); + } + } + } + + static class DispatcherThread extends HandlerThread { + DispatcherThread() { + super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND); + } + } + + private class NetworkBroadcastReceiver extends BroadcastReceiver { + private static final String EXTRA_AIRPLANE_STATE = "state"; + + private final ConnectivityManager connectivityManager; + + NetworkBroadcastReceiver(Context context) { + connectivityManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE); + } + + void register() { + boolean shouldScanState = service instanceof PicassoExecutorService && // + Utils.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE); + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_AIRPLANE_MODE_CHANGED); + if (shouldScanState) { + filter.addAction(CONNECTIVITY_ACTION); + } + context.registerReceiver(this, filter); + } + + void unregister() { + context.unregisterReceiver(this); + } + + @Override public void onReceive(Context context, Intent intent) { + // On some versions of Android this may be called with a null Intent + if (null == intent) { + return; + } + + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + + if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { + dispatchAirplaneModeChange(extras.getBoolean(EXTRA_AIRPLANE_STATE, false)); + } else if (CONNECTIVITY_ACTION.equals(action)) { + dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo()); + } + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Downloader.java b/mobile/android/thirdparty/com/squareup/picasso/Downloader.java new file mode 100644 index 000000000..33a909371 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Downloader.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; +import android.net.Uri; +import java.io.IOException; +import java.io.InputStream; + +/** A mechanism to load images from external resources such as a disk cache and/or the internet. */ +public interface Downloader { + /** + * Download the specified image {@code url} from the internet. + * + * @param uri Remote image URL. + * @param localCacheOnly If {@code true} the URL should only be loaded if available in a local + * disk cache. + * @return {@link Response} containing either a {@link Bitmap} representation of the request or an + * {@link InputStream} for the image data. {@code null} can be returned to indicate a problem + * loading the bitmap. + * @throws IOException if the requested URL cannot successfully be loaded. + */ + Response load(Uri uri, boolean localCacheOnly) throws IOException; + + /** Thrown for non-2XX responses. */ + class ResponseException extends IOException { + public ResponseException(String message) { + super(message); + } + } + + /** Response stream or bitmap and info. */ + class Response { + final InputStream stream; + final Bitmap bitmap; + final boolean cached; + + /** + * Response image and info. + * + * @param bitmap Image. + * @param loadedFromCache {@code true} if the source of the image is from a local disk cache. + */ + public Response(Bitmap bitmap, boolean loadedFromCache) { + if (bitmap == null) { + throw new IllegalArgumentException("Bitmap may not be null."); + } + this.stream = null; + this.bitmap = bitmap; + this.cached = loadedFromCache; + } + + /** + * Response stream and info. + * + * @param stream Image data stream. + * @param loadedFromCache {@code true} if the source of the stream is from a local disk cache. + */ + public Response(InputStream stream, boolean loadedFromCache) { + if (stream == null) { + throw new IllegalArgumentException("Stream may not be null."); + } + this.stream = stream; + this.bitmap = null; + this.cached = loadedFromCache; + } + + /** + * Input stream containing image data. + *

    + * If this returns {@code null}, image data will be available via {@link #getBitmap()}. + */ + public InputStream getInputStream() { + return stream; + } + + /** + * Bitmap representing the image. + *

    + * If this returns {@code null}, image data will be available via {@link #getInputStream()}. + */ + public Bitmap getBitmap() { + return bitmap; + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/FetchAction.java b/mobile/android/thirdparty/com/squareup/picasso/FetchAction.java new file mode 100644 index 000000000..d8f1c3fb4 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/FetchAction.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; + +class FetchAction extends Action { + FetchAction(Picasso picasso, Request data, boolean skipCache, String key) { + super(picasso, null, data, skipCache, false, 0, null, key); + } + + @Override void complete(Bitmap result, Picasso.LoadedFrom from) { + } + + @Override public void error() { + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/FileBitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/FileBitmapHunter.java new file mode 100644 index 000000000..dac38fb80 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/FileBitmapHunter.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.graphics.Bitmap; +import android.media.ExifInterface; +import android.net.Uri; +import java.io.IOException; + +import static android.media.ExifInterface.ORIENTATION_NORMAL; +import static android.media.ExifInterface.ORIENTATION_ROTATE_180; +import static android.media.ExifInterface.ORIENTATION_ROTATE_270; +import static android.media.ExifInterface.ORIENTATION_ROTATE_90; +import static android.media.ExifInterface.TAG_ORIENTATION; + +class FileBitmapHunter extends ContentStreamBitmapHunter { + + FileBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, + Stats stats, Action action) { + super(context, picasso, dispatcher, cache, stats, action); + } + + @Override Bitmap decode(Request data) + throws IOException { + setExifRotation(getFileExifRotation(data.uri)); + return super.decode(data); + } + + static int getFileExifRotation(Uri uri) throws IOException { + ExifInterface exifInterface = new ExifInterface(uri.getPath()); + int orientation = exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL); + switch (orientation) { + case ORIENTATION_ROTATE_90: + return 90; + case ORIENTATION_ROTATE_180: + return 180; + case ORIENTATION_ROTATE_270: + return 270; + default: + return 0; + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/GetAction.java b/mobile/android/thirdparty/com/squareup/picasso/GetAction.java new file mode 100644 index 000000000..e30024e89 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/GetAction.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; + +class GetAction extends Action { + GetAction(Picasso picasso, Request data, boolean skipCache, String key) { + super(picasso, null, data, skipCache, false, 0, null, key); + } + + @Override void complete(Bitmap result, Picasso.LoadedFrom from) { + } + + @Override public void error() { + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/ImageViewAction.java b/mobile/android/thirdparty/com/squareup/picasso/ImageViewAction.java new file mode 100644 index 000000000..16f550907 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/ImageViewAction.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.widget.ImageView; + +class ImageViewAction extends Action { + + Callback callback; + + ImageViewAction(Picasso picasso, ImageView imageView, Request data, boolean skipCache, + boolean noFade, int errorResId, Drawable errorDrawable, String key, Callback callback) { + super(picasso, imageView, data, skipCache, noFade, errorResId, errorDrawable, key); + this.callback = callback; + } + + @Override public void complete(Bitmap result, Picasso.LoadedFrom from) { + if (result == null) { + throw new AssertionError( + String.format("Attempted to complete action with no result!\n%s", this)); + } + + ImageView target = this.target.get(); + if (target == null) { + return; + } + + Context context = picasso.context; + boolean debugging = picasso.debugging; + PicassoDrawable.setBitmap(target, context, result, from, noFade, debugging); + + if (callback != null) { + callback.onSuccess(); + } + } + + @Override public void error() { + ImageView target = this.target.get(); + if (target == null) { + return; + } + if (errorResId != 0) { + target.setImageResource(errorResId); + } else if (errorDrawable != null) { + target.setImageDrawable(errorDrawable); + } + + if (callback != null) { + callback.onError(); + } + } + + @Override void cancel() { + super.cancel(); + if (callback != null) { + callback = null; + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/LruCache.java b/mobile/android/thirdparty/com/squareup/picasso/LruCache.java new file mode 100644 index 000000000..5d5f07fcb --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/LruCache.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.graphics.Bitmap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** A memory cache which uses a least-recently used eviction policy. */ +public class LruCache implements Cache { + final LinkedHashMap map; + private final int maxSize; + + private int size; + private int putCount; + private int evictionCount; + private int hitCount; + private int missCount; + + /** Create a cache using an appropriate portion of the available RAM as the maximum size. */ + public LruCache(Context context) { + this(Utils.calculateMemoryCacheSize(context)); + } + + /** Create a cache with a given maximum size in bytes. */ + public LruCache(int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("Max size must be positive."); + } + this.maxSize = maxSize; + this.map = new LinkedHashMap(0, 0.75f, true); + } + + @Override public Bitmap get(String key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + + Bitmap mapValue; + synchronized (this) { + mapValue = map.get(key); + if (mapValue != null) { + hitCount++; + return mapValue; + } + missCount++; + } + + return null; + } + + @Override public void set(String key, Bitmap bitmap) { + if (key == null || bitmap == null) { + throw new NullPointerException("key == null || bitmap == null"); + } + + Bitmap previous; + synchronized (this) { + putCount++; + size += Utils.getBitmapBytes(bitmap); + previous = map.put(key, bitmap); + if (previous != null) { + size -= Utils.getBitmapBytes(previous); + } + } + + trimToSize(maxSize); + } + + private void trimToSize(int maxSize) { + while (true) { + String key; + Bitmap value; + synchronized (this) { + if (size < 0 || (map.isEmpty() && size != 0)) { + throw new IllegalStateException( + getClass().getName() + ".sizeOf() is reporting inconsistent results!"); + } + + if (size <= maxSize || map.isEmpty()) { + break; + } + + Map.Entry toEvict = map.entrySet().iterator().next(); + key = toEvict.getKey(); + value = toEvict.getValue(); + map.remove(key); + size -= Utils.getBitmapBytes(value); + evictionCount++; + } + } + } + + /** Clear the cache. */ + public final void evictAll() { + trimToSize(-1); // -1 will evict 0-sized elements + } + + /** Returns the sum of the sizes of the entries in this cache. */ + public final synchronized int size() { + return size; + } + + /** Returns the maximum sum of the sizes of the entries in this cache. */ + public final synchronized int maxSize() { + return maxSize; + } + + public final synchronized void clear() { + evictAll(); + } + + /** Returns the number of times {@link #get} returned a value. */ + public final synchronized int hitCount() { + return hitCount; + } + + /** Returns the number of times {@link #get} returned {@code null}. */ + public final synchronized int missCount() { + return missCount; + } + + /** Returns the number of times {@link #set(String, Bitmap)} was called. */ + public final synchronized int putCount() { + return putCount; + } + + /** Returns the number of values that have been evicted. */ + public final synchronized int evictionCount() { + return evictionCount; + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/MarkableInputStream.java b/mobile/android/thirdparty/com/squareup/picasso/MarkableInputStream.java new file mode 100644 index 000000000..17043a1b0 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/MarkableInputStream.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream wrapper that supports unlimited independent cursors for + * marking and resetting. Each cursor is a token, and it's the caller's + * responsibility to keep track of these. + */ +final class MarkableInputStream extends InputStream { + private final InputStream in; + + private long offset; + private long reset; + private long limit; + + private long defaultMark = -1; + + public MarkableInputStream(InputStream in) { + if (!in.markSupported()) { + in = new BufferedInputStream(in); + } + this.in = in; + } + + /** Marks this place in the stream so we can reset back to it later. */ + @Override public void mark(int readLimit) { + defaultMark = savePosition(readLimit); + } + + /** + * Returns an opaque token representing the current position in the stream. + * Call {@link #reset(long)} to return to this position in the stream later. + * It is an error to call {@link #reset(long)} after consuming more than + * {@code readLimit} bytes from this stream. + */ + public long savePosition(int readLimit) { + long offsetLimit = offset + readLimit; + if (limit < offsetLimit) { + setLimit(offsetLimit); + } + return offset; + } + + /** + * Makes sure that the underlying stream can backtrack the full range from + * {@code reset} thru {@code limit}. Since we can't call {@code mark()} + * without also adjusting the reset-to-position on the underlying stream this + * method resets first and then marks the union of the two byte ranges. On + * buffered streams this additional cursor motion shouldn't result in any + * additional I/O. + */ + private void setLimit(long limit) { + try { + if (reset < offset && offset <= this.limit) { + in.reset(); + in.mark((int) (limit - reset)); + skip(reset, offset); + } else { + reset = offset; + in.mark((int) (limit - offset)); + } + this.limit = limit; + } catch (IOException e) { + throw new IllegalStateException("Unable to mark: " + e); + } + } + + /** Resets the stream to the most recent {@link #mark mark}. */ + @Override public void reset() throws IOException { + reset(defaultMark); + } + + /** Resets the stream to the position recorded by {@code token}. */ + public void reset(long token) throws IOException { + if (offset > limit || token < reset) { + throw new IOException("Cannot reset"); + } + in.reset(); + skip(reset, token); + offset = token; + } + + /** Skips {@code target - current} bytes and returns. */ + private void skip(long current, long target) throws IOException { + while (current < target) { + long skipped = in.skip(target - current); + if (skipped == 0) { + if (read() == -1) { + break; // EOF + } else { + skipped = 1; + } + } + current += skipped; + } + } + + @Override public int read() throws IOException { + int result = in.read(); + if (result != -1) { + offset++; + } + return result; + } + + @Override public int read(byte[] buffer) throws IOException { + int count = in.read(buffer); + if (count != -1) { + offset += count; + } + return count; + } + + @Override public int read(byte[] buffer, int offset, int length) throws IOException { + int count = in.read(buffer, offset, length); + if (count != -1) { + this.offset += count; + } + return count; + } + + @Override public long skip(long byteCount) throws IOException { + long skipped = in.skip(byteCount); + offset += skipped; + return skipped; + } + + @Override public int available() throws IOException { + return in.available(); + } + + @Override public void close() throws IOException { + in.close(); + } + + @Override public boolean markSupported() { + return in.markSupported(); + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/MediaStoreBitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/MediaStoreBitmapHunter.java new file mode 100644 index 000000000..4f8ae1c24 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/MediaStoreBitmapHunter.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.provider.MediaStore; +import java.io.IOException; + +import static android.content.ContentUris.parseId; +import static android.provider.MediaStore.Images.Thumbnails.FULL_SCREEN_KIND; +import static android.provider.MediaStore.Images.Thumbnails.MICRO_KIND; +import static android.provider.MediaStore.Images.Thumbnails.MINI_KIND; +import static android.provider.MediaStore.Images.Thumbnails.getThumbnail; +import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.FULL; +import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.MICRO; +import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.MINI; + +class MediaStoreBitmapHunter extends ContentStreamBitmapHunter { + private static final String[] CONTENT_ORIENTATION = new String[] { + MediaStore.Images.ImageColumns.ORIENTATION + }; + + MediaStoreBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, + Stats stats, Action action) { + super(context, picasso, dispatcher, cache, stats, action); + } + + @Override Bitmap decode(Request data) throws IOException { + ContentResolver contentResolver = context.getContentResolver(); + setExifRotation(getExitOrientation(contentResolver, data.uri)); + + if (data.hasSize()) { + PicassoKind picassoKind = getPicassoKind(data.targetWidth, data.targetHeight); + if (picassoKind == FULL) { + return super.decode(data); + } + + long id = parseId(data.uri); + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + + calculateInSampleSize(data.targetWidth, data.targetHeight, picassoKind.width, + picassoKind.height, options); + + Bitmap result = getThumbnail(contentResolver, id, picassoKind.androidKind, options); + + if (result != null) { + return result; + } + } + + return super.decode(data); + } + + static PicassoKind getPicassoKind(int targetWidth, int targetHeight) { + if (targetWidth <= MICRO.width && targetHeight <= MICRO.height) { + return MICRO; + } else if (targetWidth <= MINI.width && targetHeight <= MINI.height) { + return MINI; + } + return FULL; + } + + static int getExitOrientation(ContentResolver contentResolver, Uri uri) { + Cursor cursor = null; + try { + cursor = contentResolver.query(uri, CONTENT_ORIENTATION, null, null, null); + if (cursor == null || !cursor.moveToFirst()) { + return 0; + } + return cursor.getInt(0); + } catch (RuntimeException ignored) { + // If the orientation column doesn't exist, assume no rotation. + return 0; + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + + enum PicassoKind { + MICRO(MICRO_KIND, 96, 96), + MINI(MINI_KIND, 512, 384), + FULL(FULL_SCREEN_KIND, -1, -1); + + final int androidKind; + final int width; + final int height; + + PicassoKind(int androidKind, int width, int height) { + this.androidKind = androidKind; + this.width = width; + this.height = height; + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/NetworkBitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/NetworkBitmapHunter.java new file mode 100644 index 000000000..6d148211d --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/NetworkBitmapHunter.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.NetworkInfo; +import java.io.IOException; +import java.io.InputStream; + +import static com.squareup.picasso.Downloader.Response; +import static com.squareup.picasso.Picasso.LoadedFrom.DISK; +import static com.squareup.picasso.Picasso.LoadedFrom.NETWORK; + +class NetworkBitmapHunter extends BitmapHunter { + static final int DEFAULT_RETRY_COUNT = 2; + private static final int MARKER = 65536; + + private final Downloader downloader; + + int retryCount; + + public NetworkBitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, + Action action, Downloader downloader) { + super(picasso, dispatcher, cache, stats, action); + this.downloader = downloader; + this.retryCount = DEFAULT_RETRY_COUNT; + } + + @Override Bitmap decode(Request data) throws IOException { + boolean loadFromLocalCacheOnly = retryCount == 0; + + Response response = downloader.load(data.uri, loadFromLocalCacheOnly); + if (response == null) { + return null; + } + + loadedFrom = response.cached ? DISK : NETWORK; + + Bitmap result = response.getBitmap(); + if (result != null) { + return result; + } + + InputStream is = response.getInputStream(); + try { + return decodeStream(is, data); + } finally { + Utils.closeQuietly(is); + } + } + + @Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) { + boolean hasRetries = retryCount > 0; + if (!hasRetries) { + return false; + } + retryCount--; + return info == null || info.isConnectedOrConnecting(); + } + + private Bitmap decodeStream(InputStream stream, Request data) throws IOException { + if (stream == null) { + return null; + } + MarkableInputStream markStream = new MarkableInputStream(stream); + stream = markStream; + + long mark = markStream.savePosition(MARKER); + + boolean isWebPFile = Utils.isWebPFile(stream); + markStream.reset(mark); + // When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash. + // Decode byte array instead + if (isWebPFile) { + byte[] bytes = Utils.toByteArray(stream); + BitmapFactory.Options options = null; + if (data.hasSize()) { + options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + + BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + calculateInSampleSize(data.targetWidth, data.targetHeight, options); + } + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + } else { + BitmapFactory.Options options = null; + if (data.hasSize()) { + options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + + BitmapFactory.decodeStream(stream, null, options); + calculateInSampleSize(data.targetWidth, data.targetHeight, options); + + markStream.reset(mark); + } + return BitmapFactory.decodeStream(stream, null, options); + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Picasso.java b/mobile/android/thirdparty/com/squareup/picasso/Picasso.java new file mode 100644 index 000000000..9b510f977 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Picasso.java @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.widget.ImageView; +import java.io.File; +import java.lang.ref.ReferenceQueue; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ExecutorService; + +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; +import static com.squareup.picasso.Action.RequestWeakReference; +import static com.squareup.picasso.Dispatcher.HUNTER_BATCH_COMPLETE; +import static com.squareup.picasso.Dispatcher.REQUEST_GCED; +import static com.squareup.picasso.Utils.THREAD_PREFIX; + +/** + * Image downloading, transformation, and caching manager. + *

    + * Use {@link #with(android.content.Context)} for the global singleton instance or construct your + * own instance with {@link Builder}. + */ +public class Picasso { + + /** Callbacks for Picasso events. */ + public interface Listener { + /** + * Invoked when an image has failed to load. This is useful for reporting image failures to a + * remote analytics service, for example. + */ + void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception); + } + + /** + * A transformer that is called immediately before every request is submitted. This can be used to + * modify any information about a request. + *

    + * For example, if you use a CDN you can change the hostname for the image based on the current + * location of the user in order to get faster download speeds. + *

    + * NOTE: This is a beta feature. The API is subject to change in a backwards incompatible + * way at any time. + */ + public interface RequestTransformer { + /** + * Transform a request before it is submitted to be processed. + * + * @return The original request or a new request to replace it. Must not be null. + */ + Request transformRequest(Request request); + + /** A {@link RequestTransformer} which returns the original request. */ + RequestTransformer IDENTITY = new RequestTransformer() { + @Override public Request transformRequest(Request request) { + return request; + } + }; + } + + static final Handler HANDLER = new Handler(Looper.getMainLooper()) { + @Override public void handleMessage(Message msg) { + switch (msg.what) { + case HUNTER_BATCH_COMPLETE: { + @SuppressWarnings("unchecked") List batch = (List) msg.obj; + for (BitmapHunter hunter : batch) { + hunter.picasso.complete(hunter); + } + break; + } + case REQUEST_GCED: { + Action action = (Action) msg.obj; + action.picasso.cancelExistingRequest(action.getTarget()); + break; + } + default: + throw new AssertionError("Unknown handler message received: " + msg.what); + } + } + }; + + static Picasso singleton = null; + + private final Listener listener; + private final RequestTransformer requestTransformer; + private final CleanupThread cleanupThread; + + final Context context; + final Dispatcher dispatcher; + final Cache cache; + final Stats stats; + final Map targetToAction; + final Map targetToDeferredRequestCreator; + final ReferenceQueue referenceQueue; + + boolean debugging; + boolean shutdown; + + Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener, + RequestTransformer requestTransformer, Stats stats, boolean debugging) { + this.context = context; + this.dispatcher = dispatcher; + this.cache = cache; + this.listener = listener; + this.requestTransformer = requestTransformer; + this.stats = stats; + this.targetToAction = new WeakHashMap(); + this.targetToDeferredRequestCreator = new WeakHashMap(); + this.debugging = debugging; + this.referenceQueue = new ReferenceQueue(); + this.cleanupThread = new CleanupThread(referenceQueue, HANDLER); + this.cleanupThread.start(); + } + + /** Cancel any existing requests for the specified target {@link ImageView}. */ + public void cancelRequest(ImageView view) { + cancelExistingRequest(view); + } + + /** Cancel any existing requests for the specified {@link Target} instance. */ + public void cancelRequest(Target target) { + cancelExistingRequest(target); + } + + /** + * Start an image request using the specified URI. + *

    + * Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder, + * if one is specified. + * + * @see #load(File) + * @see #load(String) + * @see #load(int) + */ + public RequestCreator load(Uri uri) { + return new RequestCreator(this, uri, 0); + } + + /** + * Start an image request using the specified path. This is a convenience method for calling + * {@link #load(Uri)}. + *

    + * This path may be a remote URL, file resource (prefixed with {@code file:}), content resource + * (prefixed with {@code content:}), or android resource (prefixed with {@code + * android.resource:}. + *

    + * Passing {@code null} as a {@code path} will not trigger any request but will set a + * placeholder, if one is specified. + * + * @see #load(Uri) + * @see #load(File) + * @see #load(int) + */ + public RequestCreator load(String path) { + if (path == null) { + return new RequestCreator(this, null, 0); + } + if (path.trim().length() == 0) { + throw new IllegalArgumentException("Path must not be empty."); + } + return load(Uri.parse(path)); + } + + /** + * Start an image request using the specified image file. This is a convenience method for + * calling {@link #load(Uri)}. + *

    + * Passing {@code null} as a {@code file} will not trigger any request but will set a + * placeholder, if one is specified. + * + * @see #load(Uri) + * @see #load(String) + * @see #load(int) + */ + public RequestCreator load(File file) { + if (file == null) { + return new RequestCreator(this, null, 0); + } + return load(Uri.fromFile(file)); + } + + /** + * Start an image request using the specified drawable resource ID. + * + * @see #load(Uri) + * @see #load(String) + * @see #load(File) + */ + public RequestCreator load(int resourceId) { + if (resourceId == 0) { + throw new IllegalArgumentException("Resource ID must not be zero."); + } + return new RequestCreator(this, null, resourceId); + } + + /** {@code true} if debug display, logging, and statistics are enabled. */ + @SuppressWarnings("UnusedDeclaration") public boolean isDebugging() { + return debugging; + } + + /** Toggle whether debug display, logging, and statistics are enabled. */ + @SuppressWarnings("UnusedDeclaration") public void setDebugging(boolean debugging) { + this.debugging = debugging; + } + + /** Creates a {@link StatsSnapshot} of the current stats for this instance. */ + @SuppressWarnings("UnusedDeclaration") public StatsSnapshot getSnapshot() { + return stats.createSnapshot(); + } + + /** Stops this instance from accepting further requests. */ + public void shutdown() { + if (this == singleton) { + throw new UnsupportedOperationException("Default singleton instance cannot be shutdown."); + } + if (shutdown) { + return; + } + cache.clear(); + cleanupThread.shutdown(); + stats.shutdown(); + dispatcher.shutdown(); + for (DeferredRequestCreator deferredRequestCreator : targetToDeferredRequestCreator.values()) { + deferredRequestCreator.cancel(); + } + targetToDeferredRequestCreator.clear(); + shutdown = true; + } + + Request transformRequest(Request request) { + Request transformed = requestTransformer.transformRequest(request); + if (transformed == null) { + throw new IllegalStateException("Request transformer " + + requestTransformer.getClass().getCanonicalName() + + " returned null for " + + request); + } + return transformed; + } + + void defer(ImageView view, DeferredRequestCreator request) { + targetToDeferredRequestCreator.put(view, request); + } + + void enqueueAndSubmit(Action action) { + Object target = action.getTarget(); + if (target != null) { + cancelExistingRequest(target); + targetToAction.put(target, action); + } + submit(action); + } + + void submit(Action action) { + dispatcher.dispatchSubmit(action); + } + + Bitmap quickMemoryCacheCheck(String key) { + Bitmap cached = cache.get(key); + if (cached != null) { + stats.dispatchCacheHit(); + } else { + stats.dispatchCacheMiss(); + } + return cached; + } + + void complete(BitmapHunter hunter) { + List joined = hunter.getActions(); + if (joined.isEmpty()) { + return; + } + + Uri uri = hunter.getData().uri; + Exception exception = hunter.getException(); + Bitmap result = hunter.getResult(); + LoadedFrom from = hunter.getLoadedFrom(); + + for (Action join : joined) { + if (join.isCancelled()) { + continue; + } + targetToAction.remove(join.getTarget()); + if (result != null) { + if (from == null) { + throw new AssertionError("LoadedFrom cannot be null."); + } + join.complete(result, from); + } else { + join.error(); + } + } + + if (listener != null && exception != null) { + listener.onImageLoadFailed(this, uri, exception); + } + } + + private void cancelExistingRequest(Object target) { + Action action = targetToAction.remove(target); + if (action != null) { + action.cancel(); + dispatcher.dispatchCancel(action); + } + if (target instanceof ImageView) { + ImageView targetImageView = (ImageView) target; + DeferredRequestCreator deferredRequestCreator = + targetToDeferredRequestCreator.remove(targetImageView); + if (deferredRequestCreator != null) { + deferredRequestCreator.cancel(); + } + } + } + + private static class CleanupThread extends Thread { + private final ReferenceQueue referenceQueue; + private final Handler handler; + + CleanupThread(ReferenceQueue referenceQueue, Handler handler) { + this.referenceQueue = referenceQueue; + this.handler = handler; + setDaemon(true); + setName(THREAD_PREFIX + "refQueue"); + } + + @Override public void run() { + Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND); + while (true) { + try { + RequestWeakReference remove = (RequestWeakReference) referenceQueue.remove(); + handler.sendMessage(handler.obtainMessage(REQUEST_GCED, remove.action)); + } catch (InterruptedException e) { + break; + } catch (final Exception e) { + handler.post(new Runnable() { + @Override public void run() { + throw new RuntimeException(e); + } + }); + break; + } + } + } + + void shutdown() { + interrupt(); + } + } + + /** + * The global default {@link Picasso} instance. + *

    + * This instance is automatically initialized with defaults that are suitable to most + * implementations. + *

      + *
    • LRU memory cache of 15% the available application RAM
    • + *
    • Disk cache of 2% storage space up to 50MB but no less than 5MB. (Note: this is only + * available on API 14+ or if you are using a standalone library that provides a disk + * cache on all API levels like OkHttp)
    • + *
    • Three download threads for disk and network access.
    • + *
    + *

    + * If these settings do not meet the requirements of your application you can construct your own + * instance with full control over the configuration by using {@link Picasso.Builder}. + */ + public static Picasso with(Context context) { + if (singleton == null) { + singleton = new Builder(context).build(); + } + return singleton; + } + + /** Fluent API for creating {@link Picasso} instances. */ + @SuppressWarnings("UnusedDeclaration") // Public API. + public static class Builder { + private final Context context; + private Downloader downloader; + private ExecutorService service; + private Cache cache; + private Listener listener; + private RequestTransformer transformer; + private boolean debugging; + + /** Start building a new {@link Picasso} instance. */ + public Builder(Context context) { + if (context == null) { + throw new IllegalArgumentException("Context must not be null."); + } + this.context = context.getApplicationContext(); + } + + /** Specify the {@link Downloader} that will be used for downloading images. */ + public Builder downloader(Downloader downloader) { + if (downloader == null) { + throw new IllegalArgumentException("Downloader must not be null."); + } + if (this.downloader != null) { + throw new IllegalStateException("Downloader already set."); + } + this.downloader = downloader; + return this; + } + + /** Specify the executor service for loading images in the background. */ + public Builder executor(ExecutorService executorService) { + if (executorService == null) { + throw new IllegalArgumentException("Executor service must not be null."); + } + if (this.service != null) { + throw new IllegalStateException("Executor service already set."); + } + this.service = executorService; + return this; + } + + /** Specify the memory cache used for the most recent images. */ + public Builder memoryCache(Cache memoryCache) { + if (memoryCache == null) { + throw new IllegalArgumentException("Memory cache must not be null."); + } + if (this.cache != null) { + throw new IllegalStateException("Memory cache already set."); + } + this.cache = memoryCache; + return this; + } + + /** Specify a listener for interesting events. */ + public Builder listener(Listener listener) { + if (listener == null) { + throw new IllegalArgumentException("Listener must not be null."); + } + if (this.listener != null) { + throw new IllegalStateException("Listener already set."); + } + this.listener = listener; + return this; + } + + /** + * Specify a transformer for all incoming requests. + *

    + * NOTE: This is a beta feature. The API is subject to change in a backwards incompatible + * way at any time. + */ + public Builder requestTransformer(RequestTransformer transformer) { + if (transformer == null) { + throw new IllegalArgumentException("Transformer must not be null."); + } + if (this.transformer != null) { + throw new IllegalStateException("Transformer already set."); + } + this.transformer = transformer; + return this; + } + + /** Whether debugging is enabled or not. */ + public Builder debugging(boolean debugging) { + this.debugging = debugging; + return this; + } + + /** Create the {@link Picasso} instance. */ + public Picasso build() { + Context context = this.context; + + if (downloader == null) { + downloader = Utils.createDefaultDownloader(context); + } + if (cache == null) { + cache = new LruCache(context); + } + if (service == null) { + service = new PicassoExecutorService(); + } + if (transformer == null) { + transformer = RequestTransformer.IDENTITY; + } + + Stats stats = new Stats(cache); + + Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats); + + return new Picasso(context, dispatcher, cache, listener, transformer, stats, debugging); + } + } + + /** Describes where the image was loaded from. */ + public enum LoadedFrom { + MEMORY(Color.GREEN), + DISK(Color.YELLOW), + NETWORK(Color.RED); + + final int debugColor; + + private LoadedFrom(int debugColor) { + this.debugColor = debugColor; + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/PicassoDrawable.java b/mobile/android/thirdparty/com/squareup/picasso/PicassoDrawable.java new file mode 100644 index 000000000..07f762c31 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/PicassoDrawable.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.AnimationDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.SystemClock; +import android.widget.ImageView; + +import static android.graphics.Color.WHITE; +import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY; + +final class PicassoDrawable extends Drawable { + // Only accessed from main thread. + private static final Paint DEBUG_PAINT = new Paint(); + + private static final float FADE_DURATION = 200f; //ms + + /** + * Create or update the drawable on the target {@link ImageView} to display the supplied bitmap + * image. + */ + static void setBitmap(ImageView target, Context context, Bitmap bitmap, + Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) { + Drawable placeholder = target.getDrawable(); + if (placeholder instanceof AnimationDrawable) { + ((AnimationDrawable) placeholder).stop(); + } + PicassoDrawable drawable = + new PicassoDrawable(context, placeholder, bitmap, loadedFrom, noFade, debugging); + target.setImageDrawable(drawable); + } + + /** + * Create or update the drawable on the target {@link ImageView} to display the supplied + * placeholder image. + */ + static void setPlaceholder(ImageView target, int placeholderResId, Drawable placeholderDrawable) { + if (placeholderResId != 0) { + target.setImageResource(placeholderResId); + } else { + target.setImageDrawable(placeholderDrawable); + } + if (target.getDrawable() instanceof AnimationDrawable) { + ((AnimationDrawable) target.getDrawable()).start(); + } + } + + private final boolean debugging; + private final float density; + private final Picasso.LoadedFrom loadedFrom; + final BitmapDrawable image; + + Drawable placeholder; + + long startTimeMillis; + boolean animating; + int alpha = 0xFF; + + PicassoDrawable(Context context, Drawable placeholder, Bitmap bitmap, + Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) { + Resources res = context.getResources(); + + this.debugging = debugging; + this.density = res.getDisplayMetrics().density; + + this.loadedFrom = loadedFrom; + + this.image = new BitmapDrawable(res, bitmap); + + boolean fade = loadedFrom != MEMORY && !noFade; + if (fade) { + this.placeholder = placeholder; + animating = true; + startTimeMillis = SystemClock.uptimeMillis(); + } + } + + @Override public void draw(Canvas canvas) { + if (!animating) { + image.draw(canvas); + } else { + float normalized = (SystemClock.uptimeMillis() - startTimeMillis) / FADE_DURATION; + if (normalized >= 1f) { + animating = false; + placeholder = null; + image.draw(canvas); + } else { + if (placeholder != null) { + placeholder.draw(canvas); + } + + int partialAlpha = (int) (alpha * normalized); + image.setAlpha(partialAlpha); + image.draw(canvas); + image.setAlpha(alpha); + invalidateSelf(); + } + } + + if (debugging) { + drawDebugIndicator(canvas); + } + } + + @Override public int getIntrinsicWidth() { + return image.getIntrinsicWidth(); + } + + @Override public int getIntrinsicHeight() { + return image.getIntrinsicHeight(); + } + + @Override public void setAlpha(int alpha) { + this.alpha = alpha; + if (placeholder != null) { + placeholder.setAlpha(alpha); + } + image.setAlpha(alpha); + } + + @Override public void setColorFilter(ColorFilter cf) { + if (placeholder != null) { + placeholder.setColorFilter(cf); + } + image.setColorFilter(cf); + } + + @Override public int getOpacity() { + return image.getOpacity(); + } + + @Override protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + + image.setBounds(bounds); + if (placeholder != null) { + placeholder.setBounds(bounds); + } + } + + private void drawDebugIndicator(Canvas canvas) { + DEBUG_PAINT.setColor(WHITE); + Path path = getTrianglePath(new Point(0, 0), (int) (16 * density)); + canvas.drawPath(path, DEBUG_PAINT); + + DEBUG_PAINT.setColor(loadedFrom.debugColor); + path = getTrianglePath(new Point(0, 0), (int) (15 * density)); + canvas.drawPath(path, DEBUG_PAINT); + } + + private static Path getTrianglePath(Point p1, int width) { + Point p2 = new Point(p1.x + width, p1.y); + Point p3 = new Point(p1.x, p1.y + width); + + Path path = new Path(); + path.moveTo(p1.x, p1.y); + path.lineTo(p2.x, p2.y); + path.lineTo(p3.x, p3.y); + + return path; + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/PicassoExecutorService.java b/mobile/android/thirdparty/com/squareup/picasso/PicassoExecutorService.java new file mode 100644 index 000000000..875dd2dda --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/PicassoExecutorService.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.telephony.TelephonyManager; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * The default {@link java.util.concurrent.ExecutorService} used for new {@link Picasso} instances. + *

    + * Exists as a custom type so that we can differentiate the use of defaults versus a user-supplied + * instance. + */ +class PicassoExecutorService extends ThreadPoolExecutor { + private static final int DEFAULT_THREAD_COUNT = 3; + + PicassoExecutorService() { + super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(), new Utils.PicassoThreadFactory()); + } + + void adjustThreadCount(NetworkInfo info) { + if (info == null || !info.isConnectedOrConnecting()) { + setThreadCount(DEFAULT_THREAD_COUNT); + return; + } + switch (info.getType()) { + case ConnectivityManager.TYPE_WIFI: + case ConnectivityManager.TYPE_WIMAX: + case ConnectivityManager.TYPE_ETHERNET: + setThreadCount(4); + break; + case ConnectivityManager.TYPE_MOBILE: + switch (info.getSubtype()) { + case TelephonyManager.NETWORK_TYPE_LTE: // 4G + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_EHRPD: + setThreadCount(3); + break; + case TelephonyManager.NETWORK_TYPE_UMTS: // 3G + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + setThreadCount(2); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: // 2G + case TelephonyManager.NETWORK_TYPE_EDGE: + setThreadCount(1); + break; + default: + setThreadCount(DEFAULT_THREAD_COUNT); + } + break; + default: + setThreadCount(DEFAULT_THREAD_COUNT); + } + } + + private void setThreadCount(int threadCount) { + setCorePoolSize(threadCount); + setMaximumPoolSize(threadCount); + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Request.java b/mobile/android/thirdparty/com/squareup/picasso/Request.java new file mode 100644 index 000000000..8e9c32460 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Request.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.net.Uri; +import java.util.ArrayList; +import java.util.List; + +import static java.util.Collections.unmodifiableList; + +/** Immutable data about an image and the transformations that will be applied to it. */ +public final class Request { + /** + * The image URI. + *

    + * This is mutually exclusive with {@link #resourceId}. + */ + public final Uri uri; + /** + * The image resource ID. + *

    + * This is mutually exclusive with {@link #uri}. + */ + public final int resourceId; + /** List of custom transformations to be applied after the built-in transformations. */ + public final List transformations; + /** Target image width for resizing. */ + public final int targetWidth; + /** Target image height for resizing. */ + public final int targetHeight; + /** + * True if the final image should use the 'centerCrop' scale technique. + *

    + * This is mutually exclusive with {@link #centerInside}. + */ + public final boolean centerCrop; + /** + * True if the final image should use the 'centerInside' scale technique. + *

    + * This is mutually exclusive with {@link #centerCrop}. + */ + public final boolean centerInside; + /** Amount to rotate the image in degrees. */ + public final float rotationDegrees; + /** Rotation pivot on the X axis. */ + public final float rotationPivotX; + /** Rotation pivot on the Y axis. */ + public final float rotationPivotY; + /** Whether or not {@link #rotationPivotX} and {@link #rotationPivotY} are set. */ + public final boolean hasRotationPivot; + + private Request(Uri uri, int resourceId, List transformations, int targetWidth, + int targetHeight, boolean centerCrop, boolean centerInside, float rotationDegrees, + float rotationPivotX, float rotationPivotY, boolean hasRotationPivot) { + this.uri = uri; + this.resourceId = resourceId; + if (transformations == null) { + this.transformations = null; + } else { + this.transformations = unmodifiableList(transformations); + } + this.targetWidth = targetWidth; + this.targetHeight = targetHeight; + this.centerCrop = centerCrop; + this.centerInside = centerInside; + this.rotationDegrees = rotationDegrees; + this.rotationPivotX = rotationPivotX; + this.rotationPivotY = rotationPivotY; + this.hasRotationPivot = hasRotationPivot; + } + + String getName() { + if (uri != null) { + return uri.getPath(); + } + return Integer.toHexString(resourceId); + } + + public boolean hasSize() { + return targetWidth != 0; + } + + boolean needsTransformation() { + return needsMatrixTransform() || hasCustomTransformations(); + } + + boolean needsMatrixTransform() { + return targetWidth != 0 || rotationDegrees != 0; + } + + boolean hasCustomTransformations() { + return transformations != null; + } + + public Builder buildUpon() { + return new Builder(this); + } + + /** Builder for creating {@link Request} instances. */ + public static final class Builder { + private Uri uri; + private int resourceId; + private int targetWidth; + private int targetHeight; + private boolean centerCrop; + private boolean centerInside; + private float rotationDegrees; + private float rotationPivotX; + private float rotationPivotY; + private boolean hasRotationPivot; + private List transformations; + + /** Start building a request using the specified {@link Uri}. */ + public Builder(Uri uri) { + setUri(uri); + } + + /** Start building a request using the specified resource ID. */ + public Builder(int resourceId) { + setResourceId(resourceId); + } + + Builder(Uri uri, int resourceId) { + this.uri = uri; + this.resourceId = resourceId; + } + + private Builder(Request request) { + uri = request.uri; + resourceId = request.resourceId; + targetWidth = request.targetWidth; + targetHeight = request.targetHeight; + centerCrop = request.centerCrop; + centerInside = request.centerInside; + rotationDegrees = request.rotationDegrees; + rotationPivotX = request.rotationPivotX; + rotationPivotY = request.rotationPivotY; + hasRotationPivot = request.hasRotationPivot; + if (request.transformations != null) { + transformations = new ArrayList(request.transformations); + } + } + + boolean hasImage() { + return uri != null || resourceId != 0; + } + + boolean hasSize() { + return targetWidth != 0; + } + + /** + * Set the target image Uri. + *

    + * This will clear an image resource ID if one is set. + */ + public Builder setUri(Uri uri) { + if (uri == null) { + throw new IllegalArgumentException("Image URI may not be null."); + } + this.uri = uri; + this.resourceId = 0; + return this; + } + + /** + * Set the target image resource ID. + *

    + * This will clear an image Uri if one is set. + */ + public Builder setResourceId(int resourceId) { + if (resourceId == 0) { + throw new IllegalArgumentException("Image resource ID may not be 0."); + } + this.resourceId = resourceId; + this.uri = null; + return this; + } + + /** Resize the image to the specified size in pixels. */ + public Builder resize(int targetWidth, int targetHeight) { + if (targetWidth <= 0) { + throw new IllegalArgumentException("Width must be positive number."); + } + if (targetHeight <= 0) { + throw new IllegalArgumentException("Height must be positive number."); + } + this.targetWidth = targetWidth; + this.targetHeight = targetHeight; + return this; + } + + /** Clear the resize transformation, if any. This will also clear center crop/inside if set. */ + public Builder clearResize() { + targetWidth = 0; + targetHeight = 0; + centerCrop = false; + centerInside = false; + return this; + } + + /** + * Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than + * distorting the aspect ratio. This cropping technique scales the image so that it fills the + * requested bounds and then crops the extra. + */ + public Builder centerCrop() { + if (centerInside) { + throw new IllegalStateException("Center crop can not be used after calling centerInside"); + } + centerCrop = true; + return this; + } + + /** Clear the center crop transformation flag, if set. */ + public Builder clearCenterCrop() { + centerCrop = false; + return this; + } + + /** + * Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales + * the image so that both dimensions are equal to or less than the requested bounds. + */ + public Builder centerInside() { + if (centerCrop) { + throw new IllegalStateException("Center inside can not be used after calling centerCrop"); + } + centerInside = true; + return this; + } + + /** Clear the center inside transformation flag, if set. */ + public Builder clearCenterInside() { + centerInside = false; + return this; + } + + /** Rotate the image by the specified degrees. */ + public Builder rotate(float degrees) { + rotationDegrees = degrees; + return this; + } + + /** Rotate the image by the specified degrees around a pivot point. */ + public Builder rotate(float degrees, float pivotX, float pivotY) { + rotationDegrees = degrees; + rotationPivotX = pivotX; + rotationPivotY = pivotY; + hasRotationPivot = true; + return this; + } + + /** Clear the rotation transformation, if any. */ + public Builder clearRotation() { + rotationDegrees = 0; + rotationPivotX = 0; + rotationPivotY = 0; + hasRotationPivot = false; + return this; + } + + /** + * Add a custom transformation to be applied to the image. + *

    + * Custom transformations will always be run after the built-in transformations. + */ + public Builder transform(Transformation transformation) { + if (transformation == null) { + throw new IllegalArgumentException("Transformation must not be null."); + } + if (transformations == null) { + transformations = new ArrayList(2); + } + transformations.add(transformation); + return this; + } + + /** Create the immutable {@link Request} object. */ + public Request build() { + if (centerInside && centerCrop) { + throw new IllegalStateException("Center crop and center inside can not be used together."); + } + if (centerCrop && targetWidth == 0) { + throw new IllegalStateException("Center crop requires calling resize."); + } + if (centerInside && targetWidth == 0) { + throw new IllegalStateException("Center inside requires calling resize."); + } + return new Request(uri, resourceId, transformations, targetWidth, targetHeight, centerCrop, + centerInside, rotationDegrees, rotationPivotX, rotationPivotY, hasRotationPivot); + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/RequestCreator.java b/mobile/android/thirdparty/com/squareup/picasso/RequestCreator.java new file mode 100644 index 000000000..3a5ca3a9f --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/RequestCreator.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.widget.ImageView; +import java.io.IOException; + +import static com.squareup.picasso.BitmapHunter.forRequest; +import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY; +import static com.squareup.picasso.Utils.checkNotMain; +import static com.squareup.picasso.Utils.createKey; + +/** Fluent API for building an image download request. */ +@SuppressWarnings("UnusedDeclaration") // Public API. +public class RequestCreator { + private final Picasso picasso; + private final Request.Builder data; + + private boolean skipMemoryCache; + private boolean noFade; + private boolean deferred; + private int placeholderResId; + private Drawable placeholderDrawable; + private int errorResId; + private Drawable errorDrawable; + + RequestCreator(Picasso picasso, Uri uri, int resourceId) { + if (picasso.shutdown) { + throw new IllegalStateException( + "Picasso instance already shut down. Cannot submit new requests."); + } + this.picasso = picasso; + this.data = new Request.Builder(uri, resourceId); + } + + /** + * A placeholder drawable to be used while the image is being loaded. If the requested image is + * not immediately available in the memory cache then this resource will be set on the target + * {@link ImageView}. + */ + public RequestCreator placeholder(int placeholderResId) { + if (placeholderResId == 0) { + throw new IllegalArgumentException("Placeholder image resource invalid."); + } + if (placeholderDrawable != null) { + throw new IllegalStateException("Placeholder image already set."); + } + this.placeholderResId = placeholderResId; + return this; + } + + /** + * A placeholder drawable to be used while the image is being loaded. If the requested image is + * not immediately available in the memory cache then this resource will be set on the target + * {@link ImageView}. + *

    + * If you are not using a placeholder image but want to clear an existing image (such as when + * used in an {@link android.widget.Adapter adapter}), pass in {@code null}. + */ + public RequestCreator placeholder(Drawable placeholderDrawable) { + if (placeholderResId != 0) { + throw new IllegalStateException("Placeholder image already set."); + } + this.placeholderDrawable = placeholderDrawable; + return this; + } + + /** An error drawable to be used if the request image could not be loaded. */ + public RequestCreator error(int errorResId) { + if (errorResId == 0) { + throw new IllegalArgumentException("Error image resource invalid."); + } + if (errorDrawable != null) { + throw new IllegalStateException("Error image already set."); + } + this.errorResId = errorResId; + return this; + } + + /** An error drawable to be used if the request image could not be loaded. */ + public RequestCreator error(Drawable errorDrawable) { + if (errorDrawable == null) { + throw new IllegalArgumentException("Error image may not be null."); + } + if (errorResId != 0) { + throw new IllegalStateException("Error image already set."); + } + this.errorDrawable = errorDrawable; + return this; + } + + /** + * Attempt to resize the image to fit exactly into the target {@link ImageView}'s bounds. This + * will result in delayed execution of the request until the {@link ImageView} has been measured. + *

    + * Note: This method works only when your target is an {@link ImageView}. + */ + public RequestCreator fit() { + deferred = true; + return this; + } + + /** Internal use only. Used by {@link DeferredRequestCreator}. */ + RequestCreator unfit() { + deferred = false; + return this; + } + + /** Resize the image to the specified dimension size. */ + public RequestCreator resizeDimen(int targetWidthResId, int targetHeightResId) { + Resources resources = picasso.context.getResources(); + int targetWidth = resources.getDimensionPixelSize(targetWidthResId); + int targetHeight = resources.getDimensionPixelSize(targetHeightResId); + return resize(targetWidth, targetHeight); + } + + /** Resize the image to the specified size in pixels. */ + public RequestCreator resize(int targetWidth, int targetHeight) { + data.resize(targetWidth, targetHeight); + return this; + } + + /** + * Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than + * distorting the aspect ratio. This cropping technique scales the image so that it fills the + * requested bounds and then crops the extra. + */ + public RequestCreator centerCrop() { + data.centerCrop(); + return this; + } + + /** + * Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales + * the image so that both dimensions are equal to or less than the requested bounds. + */ + public RequestCreator centerInside() { + data.centerInside(); + return this; + } + + /** Rotate the image by the specified degrees. */ + public RequestCreator rotate(float degrees) { + data.rotate(degrees); + return this; + } + + /** Rotate the image by the specified degrees around a pivot point. */ + public RequestCreator rotate(float degrees, float pivotX, float pivotY) { + data.rotate(degrees, pivotX, pivotY); + return this; + } + + /** + * Add a custom transformation to be applied to the image. + *

    + * Custom transformations will always be run after the built-in transformations. + */ + // TODO show example of calling resize after a transform in the javadoc + public RequestCreator transform(Transformation transformation) { + data.transform(transformation); + return this; + } + + /** + * Indicate that this action should not use the memory cache for attempting to load or save the + * image. This can be useful when you know an image will only ever be used once (e.g., loading + * an image from the filesystem and uploading to a remote server). + */ + public RequestCreator skipMemoryCache() { + skipMemoryCache = true; + return this; + } + + /** Disable brief fade in of images loaded from the disk cache or network. */ + public RequestCreator noFade() { + noFade = true; + return this; + } + + /** Synchronously fulfill this request. Must not be called from the main thread. */ + public Bitmap get() throws IOException { + checkNotMain(); + if (deferred) { + throw new IllegalStateException("Fit cannot be used with get."); + } + if (!data.hasImage()) { + return null; + } + + Request finalData = picasso.transformRequest(data.build()); + String key = createKey(finalData); + + Action action = new GetAction(picasso, finalData, skipMemoryCache, key); + return forRequest(picasso.context, picasso, picasso.dispatcher, picasso.cache, picasso.stats, + action, picasso.dispatcher.downloader).hunt(); + } + + /** + * Asynchronously fulfills the request without a {@link ImageView} or {@link Target}. This is + * useful when you want to warm up the cache with an image. + */ + public void fetch() { + if (deferred) { + throw new IllegalStateException("Fit cannot be used with fetch."); + } + if (data.hasImage()) { + Request finalData = picasso.transformRequest(data.build()); + String key = createKey(finalData); + + Action action = new FetchAction(picasso, finalData, skipMemoryCache, key); + picasso.enqueueAndSubmit(action); + } + } + + /** + * Asynchronously fulfills the request into the specified {@link Target}. In most cases, you + * should use this when you are dealing with a custom {@link android.view.View View} or view + * holder which should implement the {@link Target} interface. + *

    + * Implementing on a {@link android.view.View View}: + *

    +   * public class ProfileView extends FrameLayout implements Target {
    +   *   {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
    +   *     setBackgroundDrawable(new BitmapDrawable(bitmap));
    +   *   }
    +   *
    +   *   {@literal @}Override public void onBitmapFailed() {
    +   *     setBackgroundResource(R.drawable.profile_error);
    +   *   }
    +   * }
    +   * 
    + * Implementing on a view holder object for use inside of an adapter: + *
    +   * public class ViewHolder implements Target {
    +   *   public FrameLayout frame;
    +   *   public TextView name;
    +   *
    +   *   {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
    +   *     frame.setBackgroundDrawable(new BitmapDrawable(bitmap));
    +   *   }
    +   *
    +   *   {@literal @}Override public void onBitmapFailed() {
    +   *     frame.setBackgroundResource(R.drawable.profile_error);
    +   *   }
    +   * }
    +   * 
    + *

    + * Note: This method keeps a weak reference to the {@link Target} instance and will be + * garbage collected if you do not keep a strong reference to it. To receive callbacks when an + * image is loaded use {@link #into(android.widget.ImageView, Callback)}. + */ + public void into(Target target) { + if (target == null) { + throw new IllegalArgumentException("Target must not be null."); + } + if (deferred) { + throw new IllegalStateException("Fit cannot be used with a Target."); + } + + Drawable drawable = + placeholderResId != 0 ? picasso.context.getResources().getDrawable(placeholderResId) + : placeholderDrawable; + + if (!data.hasImage()) { + picasso.cancelRequest(target); + target.onPrepareLoad(drawable); + return; + } + + Request finalData = picasso.transformRequest(data.build()); + String requestKey = createKey(finalData); + + if (!skipMemoryCache) { + Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey); + if (bitmap != null) { + picasso.cancelRequest(target); + target.onBitmapLoaded(bitmap, MEMORY); + return; + } + } + + target.onPrepareLoad(drawable); + + Action action = new TargetAction(picasso, target, finalData, skipMemoryCache, requestKey); + picasso.enqueueAndSubmit(action); + } + + /** + * Asynchronously fulfills the request into the specified {@link ImageView}. + *

    + * Note: This method keeps a weak reference to the {@link ImageView} instance and will + * automatically support object recycling. + */ + public void into(ImageView target) { + into(target, null); + } + + /** + * Asynchronously fulfills the request into the specified {@link ImageView} and invokes the + * target {@link Callback} if it's not {@code null}. + *

    + * Note: The {@link Callback} param is a strong reference and will prevent your + * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If + * you use this method, it is strongly recommended you invoke an adjacent + * {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking. + */ + public void into(ImageView target, Callback callback) { + if (target == null) { + throw new IllegalArgumentException("Target must not be null."); + } + + if (!data.hasImage()) { + picasso.cancelRequest(target); + PicassoDrawable.setPlaceholder(target, placeholderResId, placeholderDrawable); + return; + } + + if (deferred) { + if (data.hasSize()) { + throw new IllegalStateException("Fit cannot be used with resize."); + } + int measuredWidth = target.getMeasuredWidth(); + int measuredHeight = target.getMeasuredHeight(); + if (measuredWidth == 0 || measuredHeight == 0) { + PicassoDrawable.setPlaceholder(target, placeholderResId, placeholderDrawable); + picasso.defer(target, new DeferredRequestCreator(this, target, callback)); + return; + } + data.resize(measuredWidth, measuredHeight); + } + + Request finalData = picasso.transformRequest(data.build()); + String requestKey = createKey(finalData); + + if (!skipMemoryCache) { + Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey); + if (bitmap != null) { + picasso.cancelRequest(target); + PicassoDrawable.setBitmap(target, picasso.context, bitmap, MEMORY, noFade, + picasso.debugging); + if (callback != null) { + callback.onSuccess(); + } + return; + } + } + + PicassoDrawable.setPlaceholder(target, placeholderResId, placeholderDrawable); + + Action action = + new ImageViewAction(picasso, target, finalData, skipMemoryCache, noFade, errorResId, + errorDrawable, requestKey, callback); + + picasso.enqueueAndSubmit(action); + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/ResourceBitmapHunter.java b/mobile/android/thirdparty/com/squareup/picasso/ResourceBitmapHunter.java new file mode 100644 index 000000000..fee76b200 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/ResourceBitmapHunter.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import java.io.IOException; + +import static com.squareup.picasso.Picasso.LoadedFrom.DISK; + +class ResourceBitmapHunter extends BitmapHunter { + private final Context context; + + ResourceBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, + Stats stats, Action action) { + super(picasso, dispatcher, cache, stats, action); + this.context = context; + } + + @Override Bitmap decode(Request data) throws IOException { + Resources res = Utils.getResources(context, data); + int id = Utils.getResourceId(res, data); + return decodeResource(res, id, data); + } + + @Override Picasso.LoadedFrom getLoadedFrom() { + return DISK; + } + + private Bitmap decodeResource(Resources resources, int id, Request data) { + BitmapFactory.Options bitmapOptions = null; + if (data.hasSize()) { + bitmapOptions = new BitmapFactory.Options(); + bitmapOptions.inJustDecodeBounds = true; + BitmapFactory.decodeResource(resources, id, bitmapOptions); + calculateInSampleSize(data.targetWidth, data.targetHeight, bitmapOptions); + } + return BitmapFactory.decodeResource(resources, id, bitmapOptions); + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Stats.java b/mobile/android/thirdparty/com/squareup/picasso/Stats.java new file mode 100644 index 000000000..3eaac0249 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Stats.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; + +class Stats { + private static final int CACHE_HIT = 0; + private static final int CACHE_MISS = 1; + private static final int BITMAP_DECODE_FINISHED = 2; + private static final int BITMAP_TRANSFORMED_FINISHED = 3; + + private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX + "Stats"; + + final HandlerThread statsThread; + final Cache cache; + final Handler handler; + + long cacheHits; + long cacheMisses; + long totalOriginalBitmapSize; + long totalTransformedBitmapSize; + long averageOriginalBitmapSize; + long averageTransformedBitmapSize; + int originalBitmapCount; + int transformedBitmapCount; + + Stats(Cache cache) { + this.cache = cache; + this.statsThread = new HandlerThread(STATS_THREAD_NAME, THREAD_PRIORITY_BACKGROUND); + this.statsThread.start(); + this.handler = new StatsHandler(statsThread.getLooper(), this); + } + + void dispatchBitmapDecoded(Bitmap bitmap) { + processBitmap(bitmap, BITMAP_DECODE_FINISHED); + } + + void dispatchBitmapTransformed(Bitmap bitmap) { + processBitmap(bitmap, BITMAP_TRANSFORMED_FINISHED); + } + + void dispatchCacheHit() { + handler.sendEmptyMessage(CACHE_HIT); + } + + void dispatchCacheMiss() { + handler.sendEmptyMessage(CACHE_MISS); + } + + void shutdown() { + statsThread.quit(); + } + + void performCacheHit() { + cacheHits++; + } + + void performCacheMiss() { + cacheMisses++; + } + + void performBitmapDecoded(long size) { + originalBitmapCount++; + totalOriginalBitmapSize += size; + averageOriginalBitmapSize = getAverage(originalBitmapCount, totalOriginalBitmapSize); + } + + void performBitmapTransformed(long size) { + transformedBitmapCount++; + totalTransformedBitmapSize += size; + averageTransformedBitmapSize = getAverage(originalBitmapCount, totalTransformedBitmapSize); + } + + synchronized StatsSnapshot createSnapshot() { + return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits, cacheMisses, + totalOriginalBitmapSize, totalTransformedBitmapSize, averageOriginalBitmapSize, + averageTransformedBitmapSize, originalBitmapCount, transformedBitmapCount, + System.currentTimeMillis()); + } + + private void processBitmap(Bitmap bitmap, int what) { + // Never send bitmaps to the handler as they could be recycled before we process them. + int bitmapSize = Utils.getBitmapBytes(bitmap); + handler.sendMessage(handler.obtainMessage(what, bitmapSize, 0)); + } + + private static long getAverage(int count, long totalSize) { + return totalSize / count; + } + + private static class StatsHandler extends Handler { + + private final Stats stats; + + public StatsHandler(Looper looper, Stats stats) { + super(looper); + this.stats = stats; + } + + @Override public void handleMessage(final Message msg) { + switch (msg.what) { + case CACHE_HIT: + stats.performCacheHit(); + break; + case CACHE_MISS: + stats.performCacheMiss(); + break; + case BITMAP_DECODE_FINISHED: + stats.performBitmapDecoded(msg.arg1); + break; + case BITMAP_TRANSFORMED_FINISHED: + stats.performBitmapTransformed(msg.arg1); + break; + default: + Picasso.HANDLER.post(new Runnable() { + @Override public void run() { + throw new AssertionError("Unhandled stats message." + msg.what); + } + }); + } + } + } +} \ No newline at end of file diff --git a/mobile/android/thirdparty/com/squareup/picasso/StatsSnapshot.java b/mobile/android/thirdparty/com/squareup/picasso/StatsSnapshot.java new file mode 100644 index 000000000..5f276ebf2 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/StatsSnapshot.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.util.Log; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** Represents all stats for a {@link Picasso} instance at a single point in time. */ +public class StatsSnapshot { + private static final String TAG = "Picasso"; + + public final int maxSize; + public final int size; + public final long cacheHits; + public final long cacheMisses; + public final long totalOriginalBitmapSize; + public final long totalTransformedBitmapSize; + public final long averageOriginalBitmapSize; + public final long averageTransformedBitmapSize; + public final int originalBitmapCount; + public final int transformedBitmapCount; + + public final long timeStamp; + + public StatsSnapshot(int maxSize, int size, long cacheHits, long cacheMisses, + long totalOriginalBitmapSize, long totalTransformedBitmapSize, long averageOriginalBitmapSize, + long averageTransformedBitmapSize, int originalBitmapCount, int transformedBitmapCount, + long timeStamp) { + this.maxSize = maxSize; + this.size = size; + this.cacheHits = cacheHits; + this.cacheMisses = cacheMisses; + this.totalOriginalBitmapSize = totalOriginalBitmapSize; + this.totalTransformedBitmapSize = totalTransformedBitmapSize; + this.averageOriginalBitmapSize = averageOriginalBitmapSize; + this.averageTransformedBitmapSize = averageTransformedBitmapSize; + this.originalBitmapCount = originalBitmapCount; + this.transformedBitmapCount = transformedBitmapCount; + this.timeStamp = timeStamp; + } + + /** Prints out this {@link StatsSnapshot} into log. */ + public void dump() { + StringWriter logWriter = new StringWriter(); + dump(new PrintWriter(logWriter)); + Log.i(TAG, logWriter.toString()); + } + + /** Prints out this {@link StatsSnapshot} with the the provided {@link PrintWriter}. */ + public void dump(PrintWriter writer) { + writer.println("===============BEGIN PICASSO STATS ==============="); + writer.println("Memory Cache Stats"); + writer.print(" Max Cache Size: "); + writer.println(maxSize); + writer.print(" Cache Size: "); + writer.println(size); + writer.print(" Cache % Full: "); + writer.println((int) Math.ceil((float) size / maxSize * 100)); + writer.print(" Cache Hits: "); + writer.println(cacheHits); + writer.print(" Cache Misses: "); + writer.println(cacheMisses); + writer.println("Bitmap Stats"); + writer.print(" Total Bitmaps Decoded: "); + writer.println(originalBitmapCount); + writer.print(" Total Bitmap Size: "); + writer.println(totalOriginalBitmapSize); + writer.print(" Total Transformed Bitmaps: "); + writer.println(transformedBitmapCount); + writer.print(" Total Transformed Bitmap Size: "); + writer.println(totalTransformedBitmapSize); + writer.print(" Average Bitmap Size: "); + writer.println(averageOriginalBitmapSize); + writer.print(" Average Transformed Bitmap Size: "); + writer.println(averageTransformedBitmapSize); + writer.println("===============END PICASSO STATS ==============="); + writer.flush(); + } + + @Override public String toString() { + return "StatsSnapshot{" + + "maxSize=" + + maxSize + + ", size=" + + size + + ", cacheHits=" + + cacheHits + + ", cacheMisses=" + + cacheMisses + + ", totalOriginalBitmapSize=" + + totalOriginalBitmapSize + + ", totalTransformedBitmapSize=" + + totalTransformedBitmapSize + + ", averageOriginalBitmapSize=" + + averageOriginalBitmapSize + + ", averageTransformedBitmapSize=" + + averageTransformedBitmapSize + + ", originalBitmapCount=" + + originalBitmapCount + + ", transformedBitmapCount=" + + transformedBitmapCount + + ", timeStamp=" + + timeStamp + + '}'; + } +} \ No newline at end of file diff --git a/mobile/android/thirdparty/com/squareup/picasso/Target.java b/mobile/android/thirdparty/com/squareup/picasso/Target.java new file mode 100644 index 000000000..ad3ce6fcf --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Target.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; + +import static com.squareup.picasso.Picasso.LoadedFrom; + +/** + * Represents an arbitrary listener for image loading. + *

    + * Objects implementing this class must have a working implementation of + * {@link #equals(Object)} and {@link #hashCode()} for proper storage internally. Instances of this + * interface will also be compared to determine if view recycling is occurring. It is recommended + * that you add this interface directly on to a custom view type when using in an adapter to ensure + * correct recycling behavior. + */ +public interface Target { + /** + * Callback when an image has been successfully loaded. + *

    + * Note: You must not recycle the bitmap. + */ + void onBitmapLoaded(Bitmap bitmap, LoadedFrom from); + + /** Callback indicating the image could not be successfully loaded. */ + void onBitmapFailed(Drawable errorDrawable); + + /** Callback invoked right before your request is submitted. */ + void onPrepareLoad(Drawable placeHolderDrawable); +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/TargetAction.java b/mobile/android/thirdparty/com/squareup/picasso/TargetAction.java new file mode 100644 index 000000000..77a40f51d --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/TargetAction.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; + +final class TargetAction extends Action { + + TargetAction(Picasso picasso, Target target, Request data, boolean skipCache, String key) { + super(picasso, target, data, skipCache, false, 0, null, key); + } + + @Override void complete(Bitmap result, Picasso.LoadedFrom from) { + if (result == null) { + throw new AssertionError( + String.format("Attempted to complete action with no result!\n%s", this)); + } + Target target = getTarget(); + if (target != null) { + target.onBitmapLoaded(result, from); + if (result.isRecycled()) { + throw new IllegalStateException("Target callback must not recycle bitmap!"); + } + } + } + + @Override void error() { + Target target = getTarget(); + if (target != null) { + target.onBitmapFailed(errorDrawable); + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Transformation.java b/mobile/android/thirdparty/com/squareup/picasso/Transformation.java new file mode 100644 index 000000000..2c59f160c --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Transformation.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.graphics.Bitmap; + +/** Image transformation. */ +public interface Transformation { + /** + * Transform the source bitmap into a new bitmap. If you create a new bitmap instance, you must + * call {@link android.graphics.Bitmap#recycle()} on {@code source}. You may return the original + * if no transformation is required. + */ + Bitmap transform(Bitmap source); + + /** + * Returns a unique key for the transformation, used for caching purposes. If the transformation + * has parameters (e.g. size, scale factor, etc) then these should be part of the key. + */ + String key(); +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/UrlConnectionDownloader.java b/mobile/android/thirdparty/com/squareup/picasso/UrlConnectionDownloader.java new file mode 100644 index 000000000..50f9b2b98 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/UrlConnectionDownloader.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.content.Context; +import android.net.Uri; +import android.net.http.HttpResponseCache; +import android.os.Build; +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import static com.squareup.picasso.Utils.parseResponseSourceHeader; + +/** + * A {@link Downloader} which uses {@link HttpURLConnection} to download images. A disk cache of 2% + * of the total available space will be used (capped at 50MB) will automatically be installed in the + * application's cache directory, when available. + */ +public class UrlConnectionDownloader implements Downloader { + static final String RESPONSE_SOURCE = "X-Android-Response-Source"; + + private static final Object lock = new Object(); + static volatile Object cache; + + private final Context context; + + public UrlConnectionDownloader(Context context) { + this.context = context.getApplicationContext(); + } + + protected HttpURLConnection openConnection(Uri path) throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL(path.toString()).openConnection(); + connection.setConnectTimeout(Utils.DEFAULT_CONNECT_TIMEOUT); + connection.setReadTimeout(Utils.DEFAULT_READ_TIMEOUT); + return connection; + } + + @Override public Response load(Uri uri, boolean localCacheOnly) throws IOException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + installCacheIfNeeded(context); + } + + HttpURLConnection connection = openConnection(uri); + connection.setUseCaches(true); + if (localCacheOnly) { + connection.setRequestProperty("Cache-Control", "only-if-cached,max-age=" + Integer.MAX_VALUE); + } + + int responseCode = connection.getResponseCode(); + if (responseCode >= 300) { + connection.disconnect(); + throw new ResponseException(responseCode + " " + connection.getResponseMessage()); + } + + boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE)); + + return new Response(connection.getInputStream(), fromCache); + } + + private static void installCacheIfNeeded(Context context) { + // DCL + volatile should be safe after Java 5. + if (cache == null) { + try { + synchronized (lock) { + if (cache == null) { + cache = ResponseCacheIcs.install(context); + } + } + } catch (IOException ignored) { + } + } + } + + private static class ResponseCacheIcs { + static Object install(Context context) throws IOException { + File cacheDir = Utils.createDefaultCacheDir(context); + HttpResponseCache cache = HttpResponseCache.getInstalled(); + if (cache == null) { + long maxSize = Utils.calculateDiskCacheSize(cacheDir); + cache = HttpResponseCache.install(cacheDir, maxSize); + } + return cache; + } + } +} diff --git a/mobile/android/thirdparty/com/squareup/picasso/Utils.java b/mobile/android/thirdparty/com/squareup/picasso/Utils.java new file mode 100644 index 000000000..bafe93f98 --- /dev/null +++ b/mobile/android/thirdparty/com/squareup/picasso/Utils.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.picasso; + +import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.os.Looper; +import android.os.Process; +import android.os.StatFs; +import android.provider.Settings; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.concurrent.ThreadFactory; + +import static android.content.Context.ACTIVITY_SERVICE; +import static android.content.pm.ApplicationInfo.FLAG_LARGE_HEAP; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.HONEYCOMB; +import static android.os.Build.VERSION_CODES.HONEYCOMB_MR1; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; +import static android.provider.Settings.System.AIRPLANE_MODE_ON; + +final class Utils { + static final String THREAD_PREFIX = "Picasso-"; + static final String THREAD_IDLE_NAME = THREAD_PREFIX + "Idle"; + static final int DEFAULT_READ_TIMEOUT = 20 * 1000; // 20s + static final int DEFAULT_CONNECT_TIMEOUT = 15 * 1000; // 15s + private static final String PICASSO_CACHE = "picasso-cache"; + private static final int KEY_PADDING = 50; // Determined by exact science. + private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB + private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB + + /* WebP file header + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 'R' | 'I' | 'F' | 'F' | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | File Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 'W' | 'E' | 'B' | 'P' | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + private static final int WEBP_FILE_HEADER_SIZE = 12; + private static final String WEBP_FILE_HEADER_RIFF = "RIFF"; + private static final String WEBP_FILE_HEADER_WEBP = "WEBP"; + + private Utils() { + // No instances. + } + + static int getBitmapBytes(Bitmap bitmap) { + int result; + if (SDK_INT >= HONEYCOMB_MR1) { + result = BitmapHoneycombMR1.getByteCount(bitmap); + } else { + result = bitmap.getRowBytes() * bitmap.getHeight(); + } + if (result < 0) { + throw new IllegalStateException("Negative size: " + bitmap); + } + return result; + } + + static void checkNotMain() { + if (Looper.getMainLooper().getThread() == Thread.currentThread()) { + throw new IllegalStateException("Method call should not happen from the main thread."); + } + } + + static String createKey(Request data) { + StringBuilder builder; + + if (data.uri != null) { + String path = data.uri.toString(); + builder = new StringBuilder(path.length() + KEY_PADDING); + builder.append(path); + } else { + builder = new StringBuilder(KEY_PADDING); + builder.append(data.resourceId); + } + builder.append('\n'); + + if (data.rotationDegrees != 0) { + builder.append("rotation:").append(data.rotationDegrees); + if (data.hasRotationPivot) { + builder.append('@').append(data.rotationPivotX).append('x').append(data.rotationPivotY); + } + builder.append('\n'); + } + if (data.targetWidth != 0) { + builder.append("resize:").append(data.targetWidth).append('x').append(data.targetHeight); + builder.append('\n'); + } + if (data.centerCrop) { + builder.append("centerCrop\n"); + } else if (data.centerInside) { + builder.append("centerInside\n"); + } + + if (data.transformations != null) { + //noinspection ForLoopReplaceableByForEach + for (int i = 0, count = data.transformations.size(); i < count; i++) { + builder.append(data.transformations.get(i).key()); + builder.append('\n'); + } + } + + return builder.toString(); + } + + static void closeQuietly(InputStream is) { + if (is == null) return; + try { + is.close(); + } catch (IOException ignored) { + } + } + + /** Returns {@code true} if header indicates the response body was loaded from the disk cache. */ + static boolean parseResponseSourceHeader(String header) { + if (header == null) { + return false; + } + String[] parts = header.split(" ", 2); + if ("CACHE".equals(parts[0])) { + return true; + } + if (parts.length == 1) { + return false; + } + try { + return "CONDITIONAL_CACHE".equals(parts[0]) && Integer.parseInt(parts[1]) == 304; + } catch (NumberFormatException e) { + return false; + } + } + + static Downloader createDefaultDownloader(Context context) { + return new UrlConnectionDownloader(context); + } + + static File createDefaultCacheDir(Context context) { + File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE); + if (!cache.exists()) { + cache.mkdirs(); + } + return cache; + } + + static long calculateDiskCacheSize(File dir) { + long size = MIN_DISK_CACHE_SIZE; + + try { + StatFs statFs = new StatFs(dir.getAbsolutePath()); + long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize(); + // Target 2% of the total space. + size = available / 50; + } catch (IllegalArgumentException ignored) { + } + + // Bound inside min/max size for disk cache. + return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE); + } + + static int calculateMemoryCacheSize(Context context) { + ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE); + boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0; + int memoryClass = am.getMemoryClass(); + if (largeHeap && SDK_INT >= HONEYCOMB) { + memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am); + } + // Target ~15% of the available heap. + return 1024 * 1024 * memoryClass / 7; + } + + static boolean isAirplaneModeOn(Context context) { + ContentResolver contentResolver = context.getContentResolver(); + return Settings.System.getInt(contentResolver, AIRPLANE_MODE_ON, 0) != 0; + } + + static boolean hasPermission(Context context, String permission) { + return context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; + } + + static byte[] toByteArray(InputStream input) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024 * 4]; + int n = 0; + while (-1 != (n = input.read(buffer))) { + byteArrayOutputStream.write(buffer, 0, n); + } + return byteArrayOutputStream.toByteArray(); + } + + static boolean isWebPFile(InputStream stream) throws IOException { + byte[] fileHeaderBytes = new byte[WEBP_FILE_HEADER_SIZE]; + boolean isWebPFile = false; + if (stream.read(fileHeaderBytes, 0, WEBP_FILE_HEADER_SIZE) == WEBP_FILE_HEADER_SIZE) { + // If a file's header starts with RIFF and end with WEBP, the file is a WebP file + isWebPFile = WEBP_FILE_HEADER_RIFF.equals(new String(fileHeaderBytes, 0, 4, "US-ASCII")) + && WEBP_FILE_HEADER_WEBP.equals(new String(fileHeaderBytes, 8, 4, "US-ASCII")); + } + return isWebPFile; + } + + static int getResourceId(Resources resources, Request data) throws FileNotFoundException { + if (data.resourceId != 0 || data.uri == null) { + return data.resourceId; + } + + String pkg = data.uri.getAuthority(); + if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri); + + int id; + List segments = data.uri.getPathSegments(); + if (segments == null || segments.isEmpty()) { + throw new FileNotFoundException("No path segments: " + data.uri); + } else if (segments.size() == 1) { + try { + id = Integer.parseInt(segments.get(0)); + } catch (NumberFormatException e) { + throw new FileNotFoundException("Last path segment is not a resource ID: " + data.uri); + } + } else if (segments.size() == 2) { + String type = segments.get(0); + String name = segments.get(1); + + id = resources.getIdentifier(name, type, pkg); + } else { + throw new FileNotFoundException("More than two path segments: " + data.uri); + } + return id; + } + + static Resources getResources(Context context, Request data) throws FileNotFoundException { + if (data.resourceId != 0 || data.uri == null) { + return context.getResources(); + } + + String pkg = data.uri.getAuthority(); + if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri); + try { + PackageManager pm = context.getPackageManager(); + return pm.getResourcesForApplication(pkg); + } catch (PackageManager.NameNotFoundException e) { + throw new FileNotFoundException("Unable to obtain resources for package: " + data.uri); + } + } + + @TargetApi(HONEYCOMB) + private static class ActivityManagerHoneycomb { + static int getLargeMemoryClass(ActivityManager activityManager) { + return activityManager.getLargeMemoryClass(); + } + } + + static class PicassoThreadFactory implements ThreadFactory { + @SuppressWarnings("NullableProblems") + public Thread newThread(Runnable r) { + return new PicassoThread(r); + } + } + + private static class PicassoThread extends Thread { + public PicassoThread(Runnable r) { + super(r); + } + + @Override public void run() { + Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND); + super.run(); + } + } + + @TargetApi(HONEYCOMB_MR1) + private static class BitmapHoneycombMR1 { + static int getByteCount(Bitmap bitmap) { + return bitmap.getByteCount(); + } + } +} diff --git a/mobile/android/thirdparty/org/json/simple/ItemList.java b/mobile/android/thirdparty/org/json/simple/ItemList.java new file mode 100644 index 000000000..07231e673 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/ItemList.java @@ -0,0 +1,147 @@ +/* + * $Id: ItemList.java,v 1.1 2006/04/15 14:10:48 platform Exp $ + * Created on 2006-3-24 + */ +package org.json.simple; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * |a:b:c| => |a|,|b|,|c| + * |:| => ||,|| + * |a:| => |a|,|| + * @author FangYidong + */ +public class ItemList { + private String sp=","; + List items=new ArrayList(); + + + public ItemList(){} + + + public ItemList(String s){ + this.split(s,sp,items); + } + + public ItemList(String s,String sp){ + this.sp=s; + this.split(s,sp,items); + } + + public ItemList(String s,String sp,boolean isMultiToken){ + split(s,sp,items,isMultiToken); + } + + public List getItems(){ + return this.items; + } + + public String[] getArray(){ + return (String[])this.items.toArray(); + } + + public void split(String s,String sp,List append,boolean isMultiToken){ + if(s==null || sp==null) + return; + if(isMultiToken){ + StringTokenizer tokens=new StringTokenizer(s,sp); + while(tokens.hasMoreTokens()){ + append.add(tokens.nextToken().trim()); + } + } + else{ + this.split(s,sp,append); + } + } + + public void split(String s,String sp,List append){ + if(s==null || sp==null) + return; + int pos=0; + int prevPos=0; + do{ + prevPos=pos; + pos=s.indexOf(sp,pos); + if(pos==-1) + break; + append.add(s.substring(prevPos,pos).trim()); + pos+=sp.length(); + }while(pos!=-1); + append.add(s.substring(prevPos).trim()); + } + + public void setSP(String sp){ + this.sp=sp; + } + + public void add(int i,String item){ + if(item==null) + return; + items.add(i,item.trim()); + } + + public void add(String item){ + if(item==null) + return; + items.add(item.trim()); + } + + public void addAll(ItemList list){ + items.addAll(list.items); + } + + public void addAll(String s){ + this.split(s,sp,items); + } + + public void addAll(String s,String sp){ + this.split(s,sp,items); + } + + public void addAll(String s,String sp,boolean isMultiToken){ + this.split(s,sp,items,isMultiToken); + } + + /** + * @param i 0-based + * @return + */ + public String get(int i){ + return (String)items.get(i); + } + + public int size(){ + return items.size(); + } + + public String toString(){ + return toString(sp); + } + + public String toString(String sp){ + StringBuffer sb=new StringBuffer(); + + for(int i=0;i + */ +public class JSONArray extends ArrayList implements List, JSONAware, JSONStreamAware { + private static final long serialVersionUID = 3957988303675231981L; + + /** + * Encode a list into JSON text and write it to out. + * If this list is also a JSONStreamAware or a JSONAware, JSONStreamAware and JSONAware specific behaviours will be ignored at this top level. + * + * @see org.json.simple.JSONValue#writeJSONString(Object, Writer) + * + * @param list + * @param out + */ + public static void writeJSONString(List list, Writer out) throws IOException{ + if(list == null){ + out.write("null"); + return; + } + + boolean first = true; + Iterator iter=list.iterator(); + + out.write('['); + while(iter.hasNext()){ + if(first) + first = false; + else + out.write(','); + + Object value=iter.next(); + if(value == null){ + out.write("null"); + continue; + } + + JSONValue.writeJSONString(value, out); + } + out.write(']'); + } + + public void writeJSONString(Writer out) throws IOException{ + writeJSONString(this, out); + } + + /** + * Convert a list to JSON text. The result is a JSON array. + * If this list is also a JSONAware, JSONAware specific behaviours will be omitted at this top level. + * + * @see org.json.simple.JSONValue#toJSONString(Object) + * + * @param list + * @return JSON text, or "null" if list is null. + */ + public static String toJSONString(List list){ + if(list == null) + return "null"; + + boolean first = true; + StringBuffer sb = new StringBuffer(); + Iterator iter=list.iterator(); + + sb.append('['); + while(iter.hasNext()){ + if(first) + first = false; + else + sb.append(','); + + Object value=iter.next(); + if(value == null){ + sb.append("null"); + continue; + } + sb.append(JSONValue.toJSONString(value)); + } + sb.append(']'); + return sb.toString(); + } + + public String toJSONString(){ + return toJSONString(this); + } + + public String toString() { + return toJSONString(); + } + + + +} diff --git a/mobile/android/thirdparty/org/json/simple/JSONAware.java b/mobile/android/thirdparty/org/json/simple/JSONAware.java new file mode 100644 index 000000000..89f152510 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/JSONAware.java @@ -0,0 +1,12 @@ +package org.json.simple; + +/** + * Beans that support customized output of JSON text shall implement this interface. + * @author FangYidong + */ +public interface JSONAware { + /** + * @return JSON text + */ + String toJSONString(); +} diff --git a/mobile/android/thirdparty/org/json/simple/JSONObject.java b/mobile/android/thirdparty/org/json/simple/JSONObject.java new file mode 100644 index 000000000..d4401e114 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/JSONObject.java @@ -0,0 +1,129 @@ +/* + * $Id: JSONObject.java,v 1.1 2006/04/15 14:10:48 platform Exp $ + * Created on 2006-4-10 + */ +package org.json.simple; + +import java.io.IOException; +import java.io.Writer; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A JSON object. Key value pairs are unordered. JSONObject supports java.util.Map interface. + * + * @author FangYidong + */ +public class JSONObject extends HashMap implements Map, JSONAware, JSONStreamAware{ + private static final long serialVersionUID = -503443796854799292L; + + /** + * Encode a map into JSON text and write it to out. + * If this map is also a JSONAware or JSONStreamAware, JSONAware or JSONStreamAware specific behaviours will be ignored at this top level. + * + * @see org.json.simple.JSONValue#writeJSONString(Object, Writer) + * + * @param map + * @param out + */ + public static void writeJSONString(Map map, Writer out) throws IOException { + if(map == null){ + out.write("null"); + return; + } + + boolean first = true; + Iterator iter=map.entrySet().iterator(); + + out.write('{'); + while(iter.hasNext()){ + if(first) + first = false; + else + out.write(','); + Map.Entry entry=(Map.Entry)iter.next(); + out.write('\"'); + out.write(escape(String.valueOf(entry.getKey()))); + out.write('\"'); + out.write(':'); + JSONValue.writeJSONString(entry.getValue(), out); + } + out.write('}'); + } + + public void writeJSONString(Writer out) throws IOException{ + writeJSONString(this, out); + } + + /** + * Convert a map to JSON text. The result is a JSON object. + * If this map is also a JSONAware, JSONAware specific behaviours will be omitted at this top level. + * + * @see org.json.simple.JSONValue#toJSONString(Object) + * + * @param map + * @return JSON text, or "null" if map is null. + */ + public static String toJSONString(Map map){ + if(map == null) + return "null"; + + StringBuffer sb = new StringBuffer(); + boolean first = true; + Iterator iter=map.entrySet().iterator(); + + sb.append('{'); + while(iter.hasNext()){ + if(first) + first = false; + else + sb.append(','); + + Map.Entry entry=(Map.Entry)iter.next(); + toJSONString(String.valueOf(entry.getKey()),entry.getValue(), sb); + } + sb.append('}'); + return sb.toString(); + } + + public String toJSONString(){ + return toJSONString(this); + } + + private static String toJSONString(String key,Object value, StringBuffer sb){ + sb.append('\"'); + if(key == null) + sb.append("null"); + else + JSONValue.escape(key, sb); + sb.append('\"').append(':'); + + sb.append(JSONValue.toJSONString(value)); + + return sb.toString(); + } + + public String toString(){ + return toJSONString(); + } + + public static String toString(String key,Object value){ + StringBuffer sb = new StringBuffer(); + toJSONString(key, value, sb); + return sb.toString(); + } + + /** + * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). + * It's the same as JSONValue.escape() only for compatibility here. + * + * @see org.json.simple.JSONValue#escape(String) + * + * @param s + * @return + */ + public static String escape(String s){ + return JSONValue.escape(s); + } +} diff --git a/mobile/android/thirdparty/org/json/simple/JSONStreamAware.java b/mobile/android/thirdparty/org/json/simple/JSONStreamAware.java new file mode 100644 index 000000000..c2287c459 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/JSONStreamAware.java @@ -0,0 +1,15 @@ +package org.json.simple; + +import java.io.IOException; +import java.io.Writer; + +/** + * Beans that support customized output of JSON text to a writer shall implement this interface. + * @author FangYidong + */ +public interface JSONStreamAware { + /** + * write JSON string to out. + */ + void writeJSONString(Writer out) throws IOException; +} diff --git a/mobile/android/thirdparty/org/json/simple/JSONValue.java b/mobile/android/thirdparty/org/json/simple/JSONValue.java new file mode 100644 index 000000000..aba3c40c2 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/JSONValue.java @@ -0,0 +1,272 @@ +/* + * $Id: JSONValue.java,v 1.1 2006/04/15 14:37:04 platform Exp $ + * Created on 2006-4-15 + */ +package org.json.simple; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.util.List; +import java.util.Map; + +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + + +/** + * @author FangYidong + */ +public class JSONValue { + /** + * Parse JSON text into java object from the input source. + * Please use parseWithException() if you don't want to ignore the exception. + * + * @see org.json.simple.parser.JSONParser#parse(Reader) + * @see #parseWithException(Reader) + * + * @param in + * @return Instance of the following: + * org.json.simple.JSONObject, + * org.json.simple.JSONArray, + * java.lang.String, + * java.lang.Number, + * java.lang.Boolean, + * null + * + */ + public static Object parse(Reader in){ + try{ + JSONParser parser=new JSONParser(); + return parser.parse(in); + } + catch(Exception e){ + return null; + } + } + + public static Object parse(String s){ + StringReader in=new StringReader(s); + return parse(in); + } + + /** + * Parse JSON text into java object from the input source. + * + * @see org.json.simple.parser.JSONParser + * + * @param in + * @return Instance of the following: + * org.json.simple.JSONObject, + * org.json.simple.JSONArray, + * java.lang.String, + * java.lang.Number, + * java.lang.Boolean, + * null + * + * @throws IOException + * @throws ParseException + */ + public static Object parseWithException(Reader in) throws IOException, ParseException{ + JSONParser parser=new JSONParser(); + return parser.parse(in); + } + + public static Object parseWithException(String s) throws ParseException{ + JSONParser parser=new JSONParser(); + return parser.parse(s); + } + + /** + * Encode an object into JSON text and write it to out. + *

    + * If this object is a Map or a List, and it's also a JSONStreamAware or a JSONAware, JSONStreamAware or JSONAware will be considered firstly. + *

    + * DO NOT call this method from writeJSONString(Writer) of a class that implements both JSONStreamAware and (Map or List) with + * "this" as the first parameter, use JSONObject.writeJSONString(Map, Writer) or JSONArray.writeJSONString(List, Writer) instead. + * + * @see org.json.simple.JSONObject#writeJSONString(Map, Writer) + * @see org.json.simple.JSONArray#writeJSONString(List, Writer) + * + * @param value + * @param writer + */ + public static void writeJSONString(Object value, Writer out) throws IOException { + if(value == null){ + out.write("null"); + return; + } + + if(value instanceof String){ + out.write('\"'); + out.write(escape((String)value)); + out.write('\"'); + return; + } + + if(value instanceof Double){ + if(((Double)value).isInfinite() || ((Double)value).isNaN()) + out.write("null"); + else + out.write(value.toString()); + return; + } + + if(value instanceof Float){ + if(((Float)value).isInfinite() || ((Float)value).isNaN()) + out.write("null"); + else + out.write(value.toString()); + return; + } + + if(value instanceof Number){ + out.write(value.toString()); + return; + } + + if(value instanceof Boolean){ + out.write(value.toString()); + return; + } + + if((value instanceof JSONStreamAware)){ + ((JSONStreamAware)value).writeJSONString(out); + return; + } + + if((value instanceof JSONAware)){ + out.write(((JSONAware)value).toJSONString()); + return; + } + + if(value instanceof Map){ + JSONObject.writeJSONString((Map)value, out); + return; + } + + if(value instanceof List){ + JSONArray.writeJSONString((List)value, out); + return; + } + + out.write(value.toString()); + } + + /** + * Convert an object to JSON text. + *

    + * If this object is a Map or a List, and it's also a JSONAware, JSONAware will be considered firstly. + *

    + * DO NOT call this method from toJSONString() of a class that implements both JSONAware and Map or List with + * "this" as the parameter, use JSONObject.toJSONString(Map) or JSONArray.toJSONString(List) instead. + * + * @see org.json.simple.JSONObject#toJSONString(Map) + * @see org.json.simple.JSONArray#toJSONString(List) + * + * @param value + * @return JSON text, or "null" if value is null or it's an NaN or an INF number. + */ + public static String toJSONString(Object value){ + if(value == null) + return "null"; + + if(value instanceof String) + return "\""+escape((String)value)+"\""; + + if(value instanceof Double){ + if(((Double)value).isInfinite() || ((Double)value).isNaN()) + return "null"; + else + return value.toString(); + } + + if(value instanceof Float){ + if(((Float)value).isInfinite() || ((Float)value).isNaN()) + return "null"; + else + return value.toString(); + } + + if(value instanceof Number) + return value.toString(); + + if(value instanceof Boolean) + return value.toString(); + + if((value instanceof JSONAware)) + return ((JSONAware)value).toJSONString(); + + if(value instanceof Map) + return JSONObject.toJSONString((Map)value); + + if(value instanceof List) + return JSONArray.toJSONString((List)value); + + return value.toString(); + } + + /** + * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). + * @param s + * @return + */ + public static String escape(String s){ + if(s==null) + return null; + StringBuffer sb = new StringBuffer(); + escape(s, sb); + return sb.toString(); + } + + /** + * @param s - Must not be null. + * @param sb + */ + static void escape(String s, StringBuffer sb) { + for(int i=0;i='\u0000' && ch<='\u001F') || (ch>='\u007F' && ch<='\u009F') || (ch>='\u2000' && ch<='\u20FF')){ + String ss=Integer.toHexString(ch); + sb.append("\\u"); + for(int k=0;k<4-ss.length();k++){ + sb.append('0'); + } + sb.append(ss.toUpperCase()); + } + else{ + sb.append(ch); + } + } + }//for + } + +} diff --git a/mobile/android/thirdparty/org/json/simple/LICENSE.txt b/mobile/android/thirdparty/org/json/simple/LICENSE.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/mobile/android/thirdparty/org/json/simple/parser/ContainerFactory.java b/mobile/android/thirdparty/org/json/simple/parser/ContainerFactory.java new file mode 100644 index 000000000..366ac4dea --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/parser/ContainerFactory.java @@ -0,0 +1,23 @@ +package org.json.simple.parser; + +import java.util.List; +import java.util.Map; + +/** + * Container factory for creating containers for JSON object and JSON array. + * + * @see org.json.simple.parser.JSONParser#parse(java.io.Reader, ContainerFactory) + * + * @author FangYidong + */ +public interface ContainerFactory { + /** + * @return A Map instance to store JSON object, or null if you want to use org.json.simple.JSONObject. + */ + Map createObjectContainer(); + + /** + * @return A List instance to store JSON array, or null if you want to use org.json.simple.JSONArray. + */ + List creatArrayContainer(); +} diff --git a/mobile/android/thirdparty/org/json/simple/parser/ContentHandler.java b/mobile/android/thirdparty/org/json/simple/parser/ContentHandler.java new file mode 100644 index 000000000..ae8d06557 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/parser/ContentHandler.java @@ -0,0 +1,110 @@ +package org.json.simple.parser; + +import java.io.IOException; + +/** + * A simplified and stoppable SAX-like content handler for stream processing of JSON text. + * + * @see org.xml.sax.ContentHandler + * @see org.json.simple.parser.JSONParser#parse(java.io.Reader, ContentHandler, boolean) + * + * @author FangYidong + */ +public interface ContentHandler { + /** + * Receive notification of the beginning of JSON processing. + * The parser will invoke this method only once. + * + * @throws ParseException + * - JSONParser will stop and throw the same exception to the caller when receiving this exception. + */ + void startJSON() throws ParseException, IOException; + + /** + * Receive notification of the end of JSON processing. + * + * @throws ParseException + */ + void endJSON() throws ParseException, IOException; + + /** + * Receive notification of the beginning of a JSON object. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * - JSONParser will stop and throw the same exception to the caller when receiving this exception. + * @see #endJSON + */ + boolean startObject() throws ParseException, IOException; + + /** + * Receive notification of the end of a JSON object. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * + * @see #startObject + */ + boolean endObject() throws ParseException, IOException; + + /** + * Receive notification of the beginning of a JSON object entry. + * + * @param key - Key of a JSON object entry. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * + * @see #endObjectEntry + */ + boolean startObjectEntry(String key) throws ParseException, IOException; + + /** + * Receive notification of the end of the value of previous object entry. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * + * @see #startObjectEntry + */ + boolean endObjectEntry() throws ParseException, IOException; + + /** + * Receive notification of the beginning of a JSON array. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * + * @see #endArray + */ + boolean startArray() throws ParseException, IOException; + + /** + * Receive notification of the end of a JSON array. + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + * + * @see #startArray + */ + boolean endArray() throws ParseException, IOException; + + /** + * Receive notification of the JSON primitive values: + * java.lang.String, + * java.lang.Number, + * java.lang.Boolean + * null + * + * @param value - Instance of the following: + * java.lang.String, + * java.lang.Number, + * java.lang.Boolean + * null + * + * @return false if the handler wants to stop parsing after return. + * @throws ParseException + */ + boolean primitive(Object value) throws ParseException, IOException; + +} diff --git a/mobile/android/thirdparty/org/json/simple/parser/JSONParser.java b/mobile/android/thirdparty/org/json/simple/parser/JSONParser.java new file mode 100644 index 000000000..9acaa377f --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/parser/JSONParser.java @@ -0,0 +1,533 @@ +/* + * $Id: JSONParser.java,v 1.1 2006/04/15 14:10:48 platform Exp $ + * Created on 2006-4-15 + */ +package org.json.simple.parser; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + + +/** + * Parser for JSON text. Please note that JSONParser is NOT thread-safe. + * + * @author FangYidong + */ +public class JSONParser { + public static final int S_INIT=0; + public static final int S_IN_FINISHED_VALUE=1;//string,number,boolean,null,object,array + public static final int S_IN_OBJECT=2; + public static final int S_IN_ARRAY=3; + public static final int S_PASSED_PAIR_KEY=4; + public static final int S_IN_PAIR_VALUE=5; + public static final int S_END=6; + public static final int S_IN_ERROR=-1; + + private LinkedList handlerStatusStack; + private Yylex lexer = new Yylex((Reader)null); + private Yytoken token = null; + private int status = S_INIT; + + private int peekStatus(LinkedList statusStack){ + if(statusStack.size()==0) + return -1; + Integer status=(Integer)statusStack.getFirst(); + return status.intValue(); + } + + /** + * Reset the parser to the initial state without resetting the underlying reader. + * + */ + public void reset(){ + token = null; + status = S_INIT; + handlerStatusStack = null; + } + + /** + * Reset the parser to the initial state with a new character reader. + * + * @param in - The new character reader. + * @throws IOException + * @throws ParseException + */ + public void reset(Reader in){ + lexer.yyreset(in); + reset(); + } + + /** + * @return The position of the beginning of the current token. + */ + public int getPosition(){ + return lexer.getPosition(); + } + + public Object parse(String s) throws ParseException{ + return parse(s, (ContainerFactory)null); + } + + public Object parse(String s, ContainerFactory containerFactory) throws ParseException{ + StringReader in=new StringReader(s); + try{ + return parse(in, containerFactory); + } + catch(IOException ie){ + /* + * Actually it will never happen. + */ + throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); + } + } + + public Object parse(Reader in) throws IOException, ParseException{ + return parse(in, (ContainerFactory)null); + } + + /** + * Parse JSON text into java object from the input source. + * + * @param in + * @param containerFactory - Use this factory to createyour own JSON object and JSON array containers. + * @return Instance of the following: + * org.json.simple.JSONObject, + * org.json.simple.JSONArray, + * java.lang.String, + * java.lang.Number, + * java.lang.Boolean, + * null + * + * @throws IOException + * @throws ParseException + */ + public Object parse(Reader in, ContainerFactory containerFactory) throws IOException, ParseException{ + reset(in); + LinkedList statusStack = new LinkedList(); + LinkedList valueStack = new LinkedList(); + + try{ + do{ + nextToken(); + switch(status){ + case S_INIT: + switch(token.type){ + case Yytoken.TYPE_VALUE: + status=S_IN_FINISHED_VALUE; + statusStack.addFirst(new Integer(status)); + valueStack.addFirst(token.value); + break; + case Yytoken.TYPE_LEFT_BRACE: + status=S_IN_OBJECT; + statusStack.addFirst(new Integer(status)); + valueStack.addFirst(createObjectContainer(containerFactory)); + break; + case Yytoken.TYPE_LEFT_SQUARE: + status=S_IN_ARRAY; + statusStack.addFirst(new Integer(status)); + valueStack.addFirst(createArrayContainer(containerFactory)); + break; + default: + status=S_IN_ERROR; + }//inner switch + break; + + case S_IN_FINISHED_VALUE: + if(token.type==Yytoken.TYPE_EOF) + return valueStack.removeFirst(); + else + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + + case S_IN_OBJECT: + switch(token.type){ + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + if(token.value instanceof String){ + String key=(String)token.value; + valueStack.addFirst(key); + status=S_PASSED_PAIR_KEY; + statusStack.addFirst(new Integer(status)); + } + else{ + status=S_IN_ERROR; + } + break; + case Yytoken.TYPE_RIGHT_BRACE: + if(valueStack.size()>1){ + statusStack.removeFirst(); + valueStack.removeFirst(); + status=peekStatus(statusStack); + } + else{ + status=S_IN_FINISHED_VALUE; + } + break; + default: + status=S_IN_ERROR; + break; + }//inner switch + break; + + case S_PASSED_PAIR_KEY: + switch(token.type){ + case Yytoken.TYPE_COLON: + break; + case Yytoken.TYPE_VALUE: + statusStack.removeFirst(); + String key=(String)valueStack.removeFirst(); + Map parent=(Map)valueStack.getFirst(); + parent.put(key,token.value); + status=peekStatus(statusStack); + break; + case Yytoken.TYPE_LEFT_SQUARE: + statusStack.removeFirst(); + key=(String)valueStack.removeFirst(); + parent=(Map)valueStack.getFirst(); + List newArray=createArrayContainer(containerFactory); + parent.put(key,newArray); + status=S_IN_ARRAY; + statusStack.addFirst(new Integer(status)); + valueStack.addFirst(newArray); + break; + case Yytoken.TYPE_LEFT_BRACE: + statusStack.removeFirst(); + key=(String)valueStack.removeFirst(); + parent=(Map)valueStack.getFirst(); + Map newObject=createObjectContainer(containerFactory); + parent.put(key,newObject); + status=S_IN_OBJECT; + statusStack.addFirst(new Integer(status)); + valueStack.addFirst(newObject); + break; + default: + status=S_IN_ERROR; + } + break; + + case S_IN_ARRAY: + switch(token.type){ + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + List val=(List)valueStack.getFirst(); + val.add(token.value); + break; + case Yytoken.TYPE_RIGHT_SQUARE: + if(valueStack.size()>1){ + statusStack.removeFirst(); + valueStack.removeFirst(); + status=peekStatus(statusStack); + } + else{ + status=S_IN_FINISHED_VALUE; + } + break; + case Yytoken.TYPE_LEFT_BRACE: + val=(List)valueStack.getFirst(); + Map newObject=createObjectContainer(containerFactory); + val.add(newObject); + status=S_IN_OBJECT; + statusStack.addFirst(new Integer(status)); + valueStack.addFirst(newObject); + break; + case Yytoken.TYPE_LEFT_SQUARE: + val=(List)valueStack.getFirst(); + List newArray=createArrayContainer(containerFactory); + val.add(newArray); + status=S_IN_ARRAY; + statusStack.addFirst(new Integer(status)); + valueStack.addFirst(newArray); + break; + default: + status=S_IN_ERROR; + }//inner switch + break; + case S_IN_ERROR: + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + }//switch + if(status==S_IN_ERROR){ + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + }while(token.type!=Yytoken.TYPE_EOF); + } + catch(IOException ie){ + throw ie; + } + + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + + private void nextToken() throws ParseException, IOException{ + token = lexer.yylex(); + if(token == null) + token = new Yytoken(Yytoken.TYPE_EOF, null); + } + + private Map createObjectContainer(ContainerFactory containerFactory){ + if(containerFactory == null) + return new JSONObject(); + Map m = containerFactory.createObjectContainer(); + + if(m == null) + return new JSONObject(); + return m; + } + + private List createArrayContainer(ContainerFactory containerFactory){ + if(containerFactory == null) + return new JSONArray(); + List l = containerFactory.creatArrayContainer(); + + if(l == null) + return new JSONArray(); + return l; + } + + public void parse(String s, ContentHandler contentHandler) throws ParseException{ + parse(s, contentHandler, false); + } + + public void parse(String s, ContentHandler contentHandler, boolean isResume) throws ParseException{ + StringReader in=new StringReader(s); + try{ + parse(in, contentHandler, isResume); + } + catch(IOException ie){ + /* + * Actually it will never happen. + */ + throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); + } + } + + public void parse(Reader in, ContentHandler contentHandler) throws IOException, ParseException{ + parse(in, contentHandler, false); + } + + /** + * Stream processing of JSON text. + * + * @see ContentHandler + * + * @param in + * @param contentHandler + * @param isResume - Indicates if it continues previous parsing operation. + * If set to true, resume parsing the old stream, and parameter 'in' will be ignored. + * If this method is called for the first time in this instance, isResume will be ignored. + * + * @throws IOException + * @throws ParseException + */ + public void parse(Reader in, ContentHandler contentHandler, boolean isResume) throws IOException, ParseException{ + if(!isResume){ + reset(in); + handlerStatusStack = new LinkedList(); + } + else{ + if(handlerStatusStack == null){ + isResume = false; + reset(in); + handlerStatusStack = new LinkedList(); + } + } + + LinkedList statusStack = handlerStatusStack; + + try{ + do{ + switch(status){ + case S_INIT: + contentHandler.startJSON(); + nextToken(); + switch(token.type){ + case Yytoken.TYPE_VALUE: + status=S_IN_FINISHED_VALUE; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.primitive(token.value)) + return; + break; + case Yytoken.TYPE_LEFT_BRACE: + status=S_IN_OBJECT; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.startObject()) + return; + break; + case Yytoken.TYPE_LEFT_SQUARE: + status=S_IN_ARRAY; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.startArray()) + return; + break; + default: + status=S_IN_ERROR; + }//inner switch + break; + + case S_IN_FINISHED_VALUE: + nextToken(); + if(token.type==Yytoken.TYPE_EOF){ + contentHandler.endJSON(); + status = S_END; + return; + } + else{ + status = S_IN_ERROR; + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + + case S_IN_OBJECT: + nextToken(); + switch(token.type){ + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + if(token.value instanceof String){ + String key=(String)token.value; + status=S_PASSED_PAIR_KEY; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.startObjectEntry(key)) + return; + } + else{ + status=S_IN_ERROR; + } + break; + case Yytoken.TYPE_RIGHT_BRACE: + if(statusStack.size()>1){ + statusStack.removeFirst(); + status=peekStatus(statusStack); + } + else{ + status=S_IN_FINISHED_VALUE; + } + if(!contentHandler.endObject()) + return; + break; + default: + status=S_IN_ERROR; + break; + }//inner switch + break; + + case S_PASSED_PAIR_KEY: + nextToken(); + switch(token.type){ + case Yytoken.TYPE_COLON: + break; + case Yytoken.TYPE_VALUE: + statusStack.removeFirst(); + status=peekStatus(statusStack); + if(!contentHandler.primitive(token.value)) + return; + if(!contentHandler.endObjectEntry()) + return; + break; + case Yytoken.TYPE_LEFT_SQUARE: + statusStack.removeFirst(); + statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); + status=S_IN_ARRAY; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.startArray()) + return; + break; + case Yytoken.TYPE_LEFT_BRACE: + statusStack.removeFirst(); + statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); + status=S_IN_OBJECT; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.startObject()) + return; + break; + default: + status=S_IN_ERROR; + } + break; + + case S_IN_PAIR_VALUE: + /* + * S_IN_PAIR_VALUE is just a marker to indicate the end of an object entry, it doesn't proccess any token, + * therefore delay consuming token until next round. + */ + statusStack.removeFirst(); + status = peekStatus(statusStack); + if(!contentHandler.endObjectEntry()) + return; + break; + + case S_IN_ARRAY: + nextToken(); + switch(token.type){ + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + if(!contentHandler.primitive(token.value)) + return; + break; + case Yytoken.TYPE_RIGHT_SQUARE: + if(statusStack.size()>1){ + statusStack.removeFirst(); + status=peekStatus(statusStack); + } + else{ + status=S_IN_FINISHED_VALUE; + } + if(!contentHandler.endArray()) + return; + break; + case Yytoken.TYPE_LEFT_BRACE: + status=S_IN_OBJECT; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.startObject()) + return; + break; + case Yytoken.TYPE_LEFT_SQUARE: + status=S_IN_ARRAY; + statusStack.addFirst(new Integer(status)); + if(!contentHandler.startArray()) + return; + break; + default: + status=S_IN_ERROR; + }//inner switch + break; + + case S_END: + return; + + case S_IN_ERROR: + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + }//switch + if(status==S_IN_ERROR){ + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } + }while(token.type!=Yytoken.TYPE_EOF); + } + catch(IOException ie){ + status = S_IN_ERROR; + throw ie; + } + catch(ParseException pe){ + status = S_IN_ERROR; + throw pe; + } + catch(RuntimeException re){ + status = S_IN_ERROR; + throw re; + } + catch(Error e){ + status = S_IN_ERROR; + throw e; + } + + status = S_IN_ERROR; + throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); + } +} diff --git a/mobile/android/thirdparty/org/json/simple/parser/ParseException.java b/mobile/android/thirdparty/org/json/simple/parser/ParseException.java new file mode 100644 index 000000000..a5a5407f9 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/parser/ParseException.java @@ -0,0 +1,96 @@ +package org.json.simple.parser; + +/** + * ParseException explains why and where the error occurs in source JSON text. + * + * @author FangYidong + * + */ +public class ParseException extends Exception { + private static final long serialVersionUID = -7880698968187728548L; + + public static final int ERROR_UNEXPECTED_CHAR = 0; + public static final int ERROR_UNEXPECTED_TOKEN = 1; + public static final int ERROR_UNEXPECTED_EXCEPTION = 2; + + private int errorType; + private Object unexpectedObject; + private int position; + + public ParseException(int errorType, Throwable throwable) { + this(-1, errorType, null, throwable); + } + + public ParseException(int errorType, Object unexpectedObject) { + this(-1, errorType, unexpectedObject); + } + + public ParseException(int position, int errorType, Object unexpectedObject) { + this(-1, errorType, unexpectedObject, null); + } + + public ParseException(int position, int errorType, Object unexpectedObject, Throwable throwable) { + super(throwable); + this.position = position; + this.errorType = errorType; + this.unexpectedObject = unexpectedObject; + } + + public int getErrorType() { + return errorType; + } + + public void setErrorType(int errorType) { + this.errorType = errorType; + } + + /** + * @see org.json.simple.parser.JSONParser#getPosition() + * + * @return The character position (starting with 0) of the input where the error occurs. + */ + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } + + /** + * @see org.json.simple.parser.Yytoken + * + * @return One of the following base on the value of errorType: + * ERROR_UNEXPECTED_CHAR java.lang.Character + * ERROR_UNEXPECTED_TOKEN org.json.simple.parser.Yytoken + * ERROR_UNEXPECTED_EXCEPTION java.lang.Exception + */ + public Object getUnexpectedObject() { + return unexpectedObject; + } + + public void setUnexpectedObject(Object unexpectedObject) { + this.unexpectedObject = unexpectedObject; + } + + @Override + public String toString(){ + StringBuffer sb = new StringBuffer(); + + switch(errorType){ + case ERROR_UNEXPECTED_CHAR: + sb.append("Unexpected character (").append(unexpectedObject).append(") at position ").append(position).append("."); + break; + case ERROR_UNEXPECTED_TOKEN: + sb.append("Unexpected token ").append(unexpectedObject).append(" at position ").append(position).append("."); + break; + case ERROR_UNEXPECTED_EXCEPTION: + sb.append("Unexpected exception at position ").append(position).append(": ").append(unexpectedObject); + break; + default: + sb.append("Unkown error at position ").append(position).append("."); + break; + } + return sb.toString(); + } +} diff --git a/mobile/android/thirdparty/org/json/simple/parser/Yylex.java b/mobile/android/thirdparty/org/json/simple/parser/Yylex.java new file mode 100644 index 000000000..42ce508eb --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/parser/Yylex.java @@ -0,0 +1,688 @@ +/* The following code was generated by JFlex 1.4.2 */ + +package org.json.simple.parser; + +class Yylex { + + /** This character denotes the end of file */ + public static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int YYINITIAL = 0; + public static final int STRING_BEGIN = 2; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\11\0\1\7\1\7\2\0\1\7\22\0\1\7\1\0\1\11\10\0"+ + "\1\6\1\31\1\2\1\4\1\12\12\3\1\32\6\0\4\1\1\5"+ + "\1\1\24\0\1\27\1\10\1\30\3\0\1\22\1\13\2\1\1\21"+ + "\1\14\5\0\1\23\1\0\1\15\3\0\1\16\1\24\1\17\1\20"+ + "\5\0\1\25\1\0\1\26\uff82\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\2\0\2\1\1\2\1\3\1\4\3\1\1\5\1\6"+ + "\1\7\1\10\1\11\1\12\1\13\1\14\1\15\5\0"+ + "\1\14\1\16\1\17\1\20\1\21\1\22\1\23\1\24"+ + "\1\0\1\25\1\0\1\25\4\0\1\26\1\27\2\0"+ + "\1\30"; + + private static int [] zzUnpackAction() { + int [] result = new int[45]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\33\0\66\0\121\0\154\0\207\0\66\0\242"+ + "\0\275\0\330\0\66\0\66\0\66\0\66\0\66\0\66"+ + "\0\363\0\u010e\0\66\0\u0129\0\u0144\0\u015f\0\u017a\0\u0195"+ + "\0\66\0\66\0\66\0\66\0\66\0\66\0\66\0\66"+ + "\0\u01b0\0\u01cb\0\u01e6\0\u01e6\0\u0201\0\u021c\0\u0237\0\u0252"+ + "\0\66\0\66\0\u026d\0\u0288\0\66"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[45]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int ZZ_TRANS [] = { + 2, 2, 3, 4, 2, 2, 2, 5, 2, 6, + 2, 2, 7, 8, 2, 9, 2, 2, 2, 2, + 2, 10, 11, 12, 13, 14, 15, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, 19, 20, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 20, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 16, 16, 16, 16, 16, 16, 16, + 16, -1, -1, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + -1, -1, -1, -1, -1, -1, -1, -1, 24, 25, + 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 34, 35, -1, -1, + 34, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 37, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 38, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 39, -1, 39, -1, 39, -1, -1, + -1, -1, -1, 39, 39, -1, -1, -1, -1, 39, + 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 33, -1, 20, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 20, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 38, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 40, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 41, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 42, -1, 42, -1, 42, + -1, -1, -1, -1, -1, 42, 42, -1, -1, -1, + -1, 42, 42, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 43, -1, 43, -1, 43, -1, -1, -1, + -1, -1, 43, 43, -1, -1, -1, -1, 43, 43, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, + -1, 44, -1, 44, -1, -1, -1, -1, -1, 44, + 44, -1, -1, -1, -1, 44, 44, -1, -1, -1, + -1, -1, -1, -1, -1, + }; + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\2\0\1\11\3\1\1\11\3\1\6\11\2\1\1\11"+ + "\5\0\10\11\1\0\1\1\1\0\1\1\4\0\2\11"+ + "\2\0\1\11"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[45]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /* user code: */ +private StringBuffer sb=new StringBuffer(); + +int getPosition(){ + return yychar; +} + + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + Yylex(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + Yylex(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 90) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead-zzStartRead); + + /* translate stored positions */ + zzEndRead-= zzStartRead; + zzCurrentPos-= zzStartRead; + zzMarkedPos-= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[zzCurrentPos*2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = zzReader.read(zzBuffer, zzEndRead, + zzBuffer.length-zzEndRead); + + if (numRead > 0) { + zzEndRead+= numRead; + return false; + } + // unlikely but not impossible: read 0 characters, but not at end of stream + if (numRead == 0) { + int c = zzReader.read(); + if (c == -1) { + return true; + } else { + zzBuffer[zzEndRead++] = (char) c; + return false; + } + } + + // numRead < 0 + return true; + } + + + /** + * Closes the input stream. + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + * + * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). + * Lexical state is set to ZZ_INITIAL. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead+pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public Yytoken yylex() throws java.io.IOException, ParseException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char [] zzBufferL = zzBuffer; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar+= zzMarkedPosL-zzStartRead; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL[zzCurrentPosL++]; + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 11: + { sb.append(yytext()); + } + case 25: break; + case 4: + { sb.delete(0, sb.length());yybegin(STRING_BEGIN); + } + case 26: break; + case 16: + { sb.append('\b'); + } + case 27: break; + case 6: + { return new Yytoken(Yytoken.TYPE_RIGHT_BRACE,null); + } + case 28: break; + case 23: + { Boolean val=Boolean.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE, val); + } + case 29: break; + case 22: + { return new Yytoken(Yytoken.TYPE_VALUE, null); + } + case 30: break; + case 13: + { yybegin(YYINITIAL);return new Yytoken(Yytoken.TYPE_VALUE, sb.toString()); + } + case 31: break; + case 12: + { sb.append('\\'); + } + case 32: break; + case 21: + { Double val=Double.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE, val); + } + case 33: break; + case 1: + { throw new ParseException(yychar, ParseException.ERROR_UNEXPECTED_CHAR, new Character(yycharat(0))); + } + case 34: break; + case 8: + { return new Yytoken(Yytoken.TYPE_RIGHT_SQUARE,null); + } + case 35: break; + case 19: + { sb.append('\r'); + } + case 36: break; + case 15: + { sb.append('/'); + } + case 37: break; + case 10: + { return new Yytoken(Yytoken.TYPE_COLON,null); + } + case 38: break; + case 14: + { sb.append('"'); + } + case 39: break; + case 5: + { return new Yytoken(Yytoken.TYPE_LEFT_BRACE,null); + } + case 40: break; + case 17: + { sb.append('\f'); + } + case 41: break; + case 24: + { try{ + int ch=Integer.parseInt(yytext().substring(2),16); + sb.append((char)ch); + } + catch(Exception e){ + throw new ParseException(yychar, ParseException.ERROR_UNEXPECTED_EXCEPTION, e); + } + } + case 42: break; + case 20: + { sb.append('\t'); + } + case 43: break; + case 7: + { return new Yytoken(Yytoken.TYPE_LEFT_SQUARE,null); + } + case 44: break; + case 2: + { Long val=Long.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE, val); + } + case 45: break; + case 18: + { sb.append('\n'); + } + case 46: break; + case 9: + { return new Yytoken(Yytoken.TYPE_COMMA,null); + } + case 47: break; + case 3: + { + } + case 48: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + return null; + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/mobile/android/thirdparty/org/json/simple/parser/Yytoken.java b/mobile/android/thirdparty/org/json/simple/parser/Yytoken.java new file mode 100644 index 000000000..ff14e27c1 --- /dev/null +++ b/mobile/android/thirdparty/org/json/simple/parser/Yytoken.java @@ -0,0 +1,58 @@ +/* + * $Id: Yytoken.java,v 1.1 2006/04/15 14:10:48 platform Exp $ + * Created on 2006-4-15 + */ +package org.json.simple.parser; + +/** + * @author FangYidong + */ +public class Yytoken { + public static final int TYPE_VALUE=0;//JSON primitive value: string,number,boolean,null + public static final int TYPE_LEFT_BRACE=1; + public static final int TYPE_RIGHT_BRACE=2; + public static final int TYPE_LEFT_SQUARE=3; + public static final int TYPE_RIGHT_SQUARE=4; + public static final int TYPE_COMMA=5; + public static final int TYPE_COLON=6; + public static final int TYPE_EOF=-1;//end of file + + public int type=0; + public Object value=null; + + public Yytoken(int type,Object value){ + this.type=type; + this.value=value; + } + + public String toString(){ + StringBuffer sb = new StringBuffer(); + switch(type){ + case TYPE_VALUE: + sb.append("VALUE(").append(value).append(")"); + break; + case TYPE_LEFT_BRACE: + sb.append("LEFT BRACE({)"); + break; + case TYPE_RIGHT_BRACE: + sb.append("RIGHT BRACE(})"); + break; + case TYPE_LEFT_SQUARE: + sb.append("LEFT SQUARE([)"); + break; + case TYPE_RIGHT_SQUARE: + sb.append("RIGHT SQUARE(])"); + break; + case TYPE_COMMA: + sb.append("COMMA(,)"); + break; + case TYPE_COLON: + sb.append("COLON(:)"); + break; + case TYPE_EOF: + sb.append("END OF FILE"); + break; + } + return sb.toString(); + } +} diff --git a/mobile/android/thirdparty/org/lucasr/dspec/DesignSpec.java b/mobile/android/thirdparty/org/lucasr/dspec/DesignSpec.java new file mode 100644 index 000000000..43505be4e --- /dev/null +++ b/mobile/android/thirdparty/org/lucasr/dspec/DesignSpec.java @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2014 Lucas Rocha + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lucasr.dspec; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Draw a baseline grid, keylines, and spacing markers on top of a {@link View}. + * + * A {@link DesignSpec} can be configure programmatically as follows: + *

      + *
    1. Toggle baseline grid visibility with {@link #setBaselineGridVisible(boolean)}.
    2. + *
    3. Change baseline grid cell width with {@link #setBaselineGridCellSize(float)}. + *
    4. Change baseline grid color with {@link #setBaselineGridColor(int)}. + *
    5. Toggle keylines visibility with {@link #setKeylinesVisible(boolean)}. + *
    6. Change keylines color with {@link #setKeylinesColor(int)}. + *
    7. Add keylines with {@link #addKeyline(float, From)}. + *
    8. Toggle spacings visibility with {@link #setSpacingsVisible(boolean)}. + *
    9. Change spacings color with {@link #setSpacingsColor(int)}. + *
    10. Add spacing with {@link #addSpacing(float, float, From)}. + *
    + * + * You can also define a {@link DesignSpec} via a raw JSON resource as follows: + *
    + * {
    + *     "baselineGridVisible": true,
    + *     "baselineGridCellSize": 8,
    + *     "keylines": [
    + *         { "offset": 16,
    + *           "from": "LEFT" },
    + *         { "offset": 72,
    + *           "from": "LEFT" },
    + *         { "offset": 16,
    + *           "from": "RIGHT" }
    + *     ],
    + *     "spacings": [
    + *         { "offset": 0,
    + *           "size": 16,
    + *           "from": "LEFT" },
    + *         { "offset": 56,
    + *           "size": 16,
    + *           "from": "LEFT" },
    + *         { "offset": 0,
    + *           "size": 16,
    + *           "from": "RIGHT" }
    + *     ]
    + * }
    + * 
    + * + * The {@link From} arguments implicitly define the orientation of the given + * keyline or spacing i.e. {@link From#LEFT}, {@link From#RIGHT}, {@link From#HORIZONTAL_CENTER} + * are implicitly vertical; and {@link From#TOP}, {@link From#BOTTOM}, {@link From#VERTICAL_CENTER} + * are implicitly horizontal. + * + * The {@link From} arguments also define the 'direction' of the offsets and sizes in keylines and + * spacings. For example, a keyline using {@link From#RIGHT} will have its offset measured from + * right to left of the target {@link View}. + * + * The easiest way to use a {@link DesignSpec} is by enclosing your target {@link View} with + * a {@link DesignSpecFrameLayout} using the {@code designSpec} attribute as follows: + *
    + * 
    + *
    + *     ...
    + *
    + * 
    + * 
    + * + * Where {@code @raw/my_spec} is a raw JSON resource. Because the {@link DesignSpec} is + * defined in an Android resource, you can vary it according to the target form factor using + * well-known resource qualifiers making it easy to define different specs for phones and tablets. + * + * Because {@link DesignSpec} is a {@link Drawable}, you can simply add it to any + * {@link android.view.ViewOverlay} if you're running your app on API level >= 18: + * + *
    + * DesignSpec designSpec = DesignSpec.fromResource(someView, R.raw.some_spec);
    + * someView.getOverlay().add(designSpec);
    + * 
    + * + * @see DesignSpecFrameLayout + * @see #fromResource(View, int) + */ +public class DesignSpec extends Drawable { + private static final boolean DEFAULT_BASELINE_GRID_VISIBLE = false; + private static final boolean DEFAULT_KEYLINES_VISIBLE = true; + private static final boolean DEFAULT_SPACINGS_VISIBLE = true; + + private static final int DEFAULT_BASELINE_GRID_CELL_SIZE_DIP = 8; + + private static final String DEFAULT_BASELINE_GRID_COLOR = "#44C2185B"; + private static final String DEFAULT_KEYLINE_COLOR = "#CCC2185B"; + private static final String DEFAULT_SPACING_COLOR = "#CC89FDFD"; + + private static final float KEYLINE_STROKE_WIDTH_DIP = 1.1f; + + private static final String JSON_KEY_BASELINE_GRID_VISIBLE = "baselineGridVisible"; + private static final String JSON_KEY_BASELINE_GRID_CELL_SIZE = "baselineGridCellSize"; + private static final String JSON_KEY_BASELINE_GRID_COLOR = "baselineGridColor"; + + private static final String JSON_KEY_KEYLINES_VISIBLE = "keylinesVisible"; + private static final String JSON_KEY_KEYLINES_COLOR = "keylinesColor"; + private static final String JSON_KEY_KEYLINES = "keylines"; + + private static final String JSON_KEY_OFFSET = "offset"; + private static final String JSON_KEY_SIZE = "size"; + private static final String JSON_KEY_FROM = "from"; + + private static final String JSON_KEY_SPACINGS_VISIBLE = "spacingsVisible"; + private static final String JSON_KEY_SPACINGS_COLOR = "spacingsColor"; + private static final String JSON_KEY_SPACINGS = "spacings"; + + /** + * Defined the reference point from which keyline/spacing offsets and sizes + * will be calculated. + */ + public enum From { + LEFT, + RIGHT, + TOP, + BOTTOM, + VERTICAL_CENTER, + HORIZONTAL_CENTER + } + + private static class Keyline { + public final float position; + public final From from; + + public Keyline(float position, From from) { + this.position = position; + this.from = from; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Keyline)) { + return false; + } + + if (o == this) { + return true; + } + + final Keyline other = (Keyline) o; + return (this.position == other.position && this.from == other.from); + } + } + + private static class Spacing { + public final float offset; + public final float size; + public final From from; + + public Spacing(float offset, float size, From from) { + this.offset = offset; + this.size = size; + this.from = from; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Keyline)) { + return false; + } + + if (o == this) { + return true; + } + + final Spacing other = (Spacing) o; + return (this.offset == other.offset && + this.size == other.size && + this.from == other.from); + } + } + + private final View mHostView; + + private final float mDensity; + + private boolean mBaselineGridVisible = DEFAULT_BASELINE_GRID_VISIBLE; + private float mBaselineGridCellSize; + private final Paint mBaselineGridPaint; + + private boolean mKeylinesVisible = DEFAULT_KEYLINES_VISIBLE; + private final Paint mKeylinesPaint; + private final List mKeylines; + + private boolean mSpacingsVisible = DEFAULT_SPACINGS_VISIBLE; + private final Paint mSpacingsPaint; + private final List mSpacings; + + public DesignSpec(Resources resources, View hostView) { + mHostView = hostView; + mDensity = resources.getDisplayMetrics().density; + + mKeylines = new ArrayList(); + mSpacings = new ArrayList(); + + mBaselineGridPaint = new Paint(); + mBaselineGridPaint.setColor(Color.parseColor(DEFAULT_BASELINE_GRID_COLOR)); + + mKeylinesPaint = new Paint(); + mKeylinesPaint.setStrokeWidth(KEYLINE_STROKE_WIDTH_DIP * mDensity); + mKeylinesPaint.setColor(Color.parseColor(DEFAULT_KEYLINE_COLOR)); + + mSpacingsPaint = new Paint(); + mSpacingsPaint.setColor(Color.parseColor(DEFAULT_SPACING_COLOR)); + + mBaselineGridCellSize = mDensity * DEFAULT_BASELINE_GRID_CELL_SIZE_DIP; + } + + /** + * Whether or not the baseline grid should be drawn. + */ + public boolean isBaselineGridVisible() { + return mBaselineGridVisible; + } + + /** + * Sets the baseline grid visibility. + */ + public DesignSpec setBaselineGridVisible(boolean visible) { + if (mBaselineGridVisible == visible) { + return this; + } + + mBaselineGridVisible = visible; + invalidateSelf(); + + return this; + } + + /** + * Sets the size of the baseline grid cells. By default, it uses the + * material design 8dp cell size. + */ + public DesignSpec setBaselineGridCellSize(float cellSize) { + if (mBaselineGridCellSize == cellSize) { + return this; + } + + mBaselineGridCellSize = cellSize; + invalidateSelf(); + + return this; + } + + /** + * Sets the baseline grid color. + */ + public DesignSpec setBaselineGridColor(int color) { + if (mBaselineGridPaint.getColor() == color) { + return this; + } + + mBaselineGridPaint.setColor(color); + invalidateSelf(); + + return this; + } + + /** + * Whether or not the keylines should be drawn. + */ + public boolean areKeylinesVisible() { + return mKeylinesVisible; + } + + /** + * Sets the visibility of keylines. + */ + public DesignSpec setKeylinesVisible(boolean visible) { + if (mKeylinesVisible == visible) { + return this; + } + + mKeylinesVisible = visible; + invalidateSelf(); + + return this; + } + + /** + * Sets the keyline color. + */ + public DesignSpec setKeylinesColor(int color) { + if (mKeylinesPaint.getColor() == color) { + return this; + } + + mKeylinesPaint.setColor(color); + invalidateSelf(); + + return this; + } + + /** + * Adds a keyline to the {@link DesignSpec}. + */ + public DesignSpec addKeyline(float position, From from) { + final Keyline keyline = new Keyline(position * mDensity, from); + if (mKeylines.contains(keyline)) { + return this; + } + + mKeylines.add(keyline); + return this; + } + + /** + * Whether or not the spacing markers should be drawn. + */ + public boolean areSpacingsVisible() { + return mSpacingsVisible; + } + + /** + * Sets the visibility of spacing markers. + */ + public DesignSpec setSpacingsVisible(boolean visible) { + if (mSpacingsVisible == visible) { + return this; + } + + mSpacingsVisible = visible; + invalidateSelf(); + + return this; + } + + /** + * Sets the spacing mark color. + */ + public DesignSpec setSpacingsColor(int color) { + if (mSpacingsPaint.getColor() == color) { + return this; + } + + mSpacingsPaint.setColor(color); + invalidateSelf(); + + return this; + } + + /** + * Adds a spacing mark to the {@link DesignSpec}. + */ + public DesignSpec addSpacing(float position, float size, From from) { + final Spacing spacing = new Spacing(position * mDensity, size * mDensity, from); + if (mSpacings.contains(spacing)) { + return this; + } + + mSpacings.add(spacing); + return this; + } + + private void drawBaselineGrid(Canvas canvas) { + if (!mBaselineGridVisible) { + return; + } + + final int width = getIntrinsicWidth(); + final int height = getIntrinsicHeight(); + + float x = mBaselineGridCellSize; + while (x < width) { + canvas.drawLine(x, 0, x, height, mBaselineGridPaint); + x += mBaselineGridCellSize; + } + + float y = mBaselineGridCellSize; + while (y < height) { + canvas.drawLine(0, y, width, y, mBaselineGridPaint); + y += mBaselineGridCellSize; + } + } + + private void drawKeylines(Canvas canvas) { + if (!mKeylinesVisible) { + return; + } + + final int width = getIntrinsicWidth(); + final int height = getIntrinsicHeight(); + + final int count = mKeylines.size(); + for (int i = 0; i < count; i++) { + final Keyline keyline = mKeylines.get(i); + + final float position; + switch (keyline.from) { + case LEFT: + case TOP: + position = keyline.position; + break; + + case RIGHT: + position = width - keyline.position; + break; + + case BOTTOM: + position = height - keyline.position; + break; + + case VERTICAL_CENTER: + position = (height / 2) + keyline.position; + break; + + case HORIZONTAL_CENTER: + position = (width / 2) + keyline.position; + break; + + default: + throw new IllegalStateException("Invalid keyline offset"); + } + + switch (keyline.from) { + case LEFT: + case RIGHT: + case HORIZONTAL_CENTER: + canvas.drawLine(position, 0, position, height, mKeylinesPaint); + break; + + case TOP: + case BOTTOM: + case VERTICAL_CENTER: + canvas.drawLine(0, position, width, position, mKeylinesPaint); + break; + } + } + } + + private void drawSpacings(Canvas canvas) { + if (!mSpacingsVisible) { + return; + } + + final int width = getIntrinsicWidth(); + final int height = getIntrinsicHeight(); + + final int count = mSpacings.size(); + for (int i = 0; i < count; i++) { + final Spacing spacing = mSpacings.get(i); + + final float position1; + final float position2; + switch (spacing.from) { + case LEFT: + case TOP: + position1 = spacing.offset; + position2 = position1 + spacing.size; + break; + + case RIGHT: + position1 = width - spacing.offset + spacing.size; + position2 = width - spacing.offset; + break; + + case BOTTOM: + position1 = height - spacing.offset + spacing.size; + position2 = height - spacing.offset; + break; + + case VERTICAL_CENTER: + position1 = (height / 2) + spacing.offset; + position2 = position1 + spacing.size; + break; + + case HORIZONTAL_CENTER: + position1 = (width / 2) + spacing.offset; + position2 = position1 + spacing.size; + break; + + default: + throw new IllegalStateException("Invalid spacing offset"); + } + + switch (spacing.from) { + case LEFT: + case RIGHT: + case HORIZONTAL_CENTER: + canvas.drawRect(position1, 0, position2, height, mSpacingsPaint); + break; + + case TOP: + case BOTTOM: + case VERTICAL_CENTER: + canvas.drawRect(0, position1, width, position2, mSpacingsPaint); + break; + } + } + } + + /** + * Draws the {@link DesignSpec}. You should call this in your {@link View}'s + * {@link View#onDraw(Canvas)} method if you're not simply enclosing it with a + * {@link DesignSpecFrameLayout}. + */ + @Override + public void draw(Canvas canvas) { + drawSpacings(canvas); + drawBaselineGrid(canvas); + drawKeylines(canvas); + } + + @Override + public int getIntrinsicWidth() { + return mHostView.getWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mHostView.getHeight(); + } + + @Override + public void setAlpha(int alpha) { + mBaselineGridPaint.setAlpha(alpha); + mKeylinesPaint.setAlpha(alpha); + mSpacingsPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mBaselineGridPaint.setColorFilter(cf); + mKeylinesPaint.setColorFilter(cf); + mSpacingsPaint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + /** + * Creates a new {@link DesignSpec} instance from a resource ID using a {@link View} + * that will provide the {@link DesignSpec}'s intrinsic dimensions. + * + * @param view The {@link View} who will own the new {@link DesignSpec} instance. + * @param resId The resource ID pointing to a raw JSON resource. + * @return The newly created {@link DesignSpec} instance. + */ + public static DesignSpec fromResource(View view, int resId) { + final Resources resources = view.getResources(); + final DesignSpec spec = new DesignSpec(resources, view); + if (resId == 0) { + return spec; + } + + final JSONObject json; + try { + json = RawResource.getAsJSON(resources, resId); + } catch (IOException e) { + throw new IllegalStateException("Could not read design spec resource", e); + } + + final float density = resources.getDisplayMetrics().density; + + spec.setBaselineGridCellSize(density * json.optInt(JSON_KEY_BASELINE_GRID_CELL_SIZE, + DEFAULT_BASELINE_GRID_CELL_SIZE_DIP)); + + spec.setBaselineGridVisible(json.optBoolean(JSON_KEY_BASELINE_GRID_VISIBLE, + DEFAULT_BASELINE_GRID_VISIBLE)); + spec.setKeylinesVisible(json.optBoolean(JSON_KEY_KEYLINES_VISIBLE, + DEFAULT_KEYLINES_VISIBLE)); + spec.setSpacingsVisible(json.optBoolean(JSON_KEY_SPACINGS_VISIBLE, + DEFAULT_SPACINGS_VISIBLE)); + + spec.setBaselineGridColor(Color.parseColor(json.optString(JSON_KEY_BASELINE_GRID_COLOR, + DEFAULT_BASELINE_GRID_COLOR))); + spec.setKeylinesColor(Color.parseColor(json.optString(JSON_KEY_KEYLINES_COLOR, + DEFAULT_KEYLINE_COLOR))); + spec.setSpacingsColor(Color.parseColor(json.optString(JSON_KEY_SPACINGS_COLOR, + DEFAULT_SPACING_COLOR))); + + final JSONArray keylines = json.optJSONArray(JSON_KEY_KEYLINES); + if (keylines != null) { + final int keylineCount = keylines.length(); + for (int i = 0; i < keylineCount; i++) { + try { + final JSONObject keyline = keylines.getJSONObject(i); + spec.addKeyline(keyline.getInt(JSON_KEY_OFFSET), + From.valueOf(keyline.getString(JSON_KEY_FROM).toUpperCase())); + } catch (JSONException e) { + continue; + } + } + } + + final JSONArray spacings = json.optJSONArray(JSON_KEY_SPACINGS); + if (spacings != null) { + final int spacingCount = spacings.length(); + for (int i = 0; i < spacingCount; i++) { + try { + final JSONObject spacing = spacings.getJSONObject(i); + spec.addSpacing(spacing.getInt(JSON_KEY_OFFSET), spacing.getInt(JSON_KEY_SIZE), + From.valueOf(spacing.getString(JSON_KEY_FROM).toUpperCase())); + } catch (JSONException e) { + continue; + } + } + } + + return spec; + } +} diff --git a/mobile/android/thirdparty/org/lucasr/dspec/RawResource.java b/mobile/android/thirdparty/org/lucasr/dspec/RawResource.java new file mode 100644 index 000000000..f2e743b11 --- /dev/null +++ b/mobile/android/thirdparty/org/lucasr/dspec/RawResource.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 Lucas Rocha + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lucasr.dspec; + +import android.content.Context; +import android.content.res.Resources; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; + +class RawResource { + public static JSONObject getAsJSON(Resources resources, int id) throws IOException { + InputStreamReader reader = null; + + try { + final InputStream is = resources.openRawResource(id); + if (is == null) { + return null; + } + + reader = new InputStreamReader(is, "UTF-8"); + + final char[] buffer = new char[1024]; + final StringWriter s = new StringWriter(); + + int n; + while ((n = reader.read(buffer, 0, buffer.length)) != -1) { + s.write(buffer, 0, n); + } + + return new JSONObject(s.toString()); + } catch (JSONException e) { + throw new IllegalStateException("Invalid design spec JSON resource", e); + } finally { + if (reader != null) { + reader.close(); + } + } + } +} \ No newline at end of file diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryDecoder.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryDecoder.java new file mode 100644 index 000000000..82e77e009 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryDecoder.java @@ -0,0 +1,43 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + * Defines common decoding methods for byte array decoders. + * + * @author Apache Software Foundation + * @version $Id: BinaryDecoder.java 1075406 2011-02-28 16:18:26Z ggregory $ + */ +public interface BinaryDecoder extends Decoder { + + /** + * Decodes a byte array and returns the results as a byte array. + * + * @param source A byte array which has been encoded with the + * appropriate encoder + * + * @return a byte array that contains decoded content + * + * @throws DecoderException A decoder exception is thrown + * if a Decoder encounters a failure condition during + * the decode process. + */ + byte[] decode(byte[] source) throws DecoderException; +} + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryEncoder.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryEncoder.java new file mode 100644 index 000000000..5b1be202c --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/BinaryEncoder.java @@ -0,0 +1,43 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + * Defines common encoding methods for byte array encoders. + * + * @author Apache Software Foundation + * @version $Id: BinaryEncoder.java 1075406 2011-02-28 16:18:26Z ggregory $ + */ +public interface BinaryEncoder extends Encoder { + + /** + * Encodes a byte array and return the encoded data + * as a byte array. + * + * @param source Data to be encoded + * + * @return A byte array containing the encoded data + * + * @throws EncoderException thrown if the Encoder + * encounters a failure condition during the + * encoding process. + */ + byte[] encode(byte[] source) throws EncoderException; +} + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/CharEncoding.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/CharEncoding.java new file mode 100644 index 000000000..c4227fb0e --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/CharEncoding.java @@ -0,0 +1,127 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + * Character encoding names required of every implementation of the Java platform. + * + * From the Java documentation Standard + * charsets: + *

    + * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. Consult the release + * documentation for your implementation to see if any other encodings are supported. + *

    + * + *
      + *
    • US-ASCII
      + * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
    • + *
    • ISO-8859-1
      + * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
    • + *
    • UTF-8
      + * Eight-bit Unicode Transformation Format.
    • + *
    • UTF-16BE
      + * Sixteen-bit Unicode Transformation Format, big-endian byte order.
    • + *
    • UTF-16LE
      + * Sixteen-bit Unicode Transformation Format, little-endian byte order.
    • + *
    • UTF-16
      + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order + * accepted on input, big-endian used on output.)
    • + *
    + * + * This perhaps would best belong in the [lang] project. Even if a similar interface is defined in [lang], it is not + * forseen that [codec] would be made to depend on [lang]. + * + * @see Standard charsets + * @author Apache Software Foundation + * @since 1.4 + * @version $Id: CharEncoding.java 797857 2009-07-25 23:43:33Z ggregory $ + */ +public class CharEncoding { + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.

    + *

    + * Every implementation of the Java platform is required to support this character encoding. + *

    + * + * @see Standard charsets + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + *

    + * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

    + *

    + * Every implementation of the Java platform is required to support this character encoding. + *

    + * + * @see Standard charsets + */ + public static final String US_ASCII = "US-ASCII"; + + /** + *

    + * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark + * (either order accepted on input, big-endian used on output) + *

    + *

    + * Every implementation of the Java platform is required to support this character encoding. + *

    + * + * @see Standard charsets + */ + public static final String UTF_16 = "UTF-16"; + + /** + *

    + * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

    + *

    + * Every implementation of the Java platform is required to support this character encoding. + *

    + * + * @see Standard charsets + */ + public static final String UTF_16BE = "UTF-16BE"; + + /** + *

    + * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

    + *

    + * Every implementation of the Java platform is required to support this character encoding. + *

    + * + * @see Standard charsets + */ + public static final String UTF_16LE = "UTF-16LE"; + + /** + *

    + * Eight-bit Unicode Transformation Format. + *

    + *

    + * Every implementation of the Java platform is required to support this character encoding. + *

    + * + * @see Standard charsets + */ + public static final String UTF_8 = "UTF-8"; +} \ No newline at end of file diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/Decoder.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/Decoder.java new file mode 100644 index 000000000..5194feae2 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/Decoder.java @@ -0,0 +1,56 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + *

    Provides the highest level of abstraction for Decoders. + * This is the sister interface of {@link Encoder}. All + * Decoders implement this common generic interface.

    + * + *

    Allows a user to pass a generic Object to any Decoder + * implementation in the codec package.

    + * + *

    One of the two interfaces at the center of the codec package.

    + * + * @author Apache Software Foundation + * @version $Id: Decoder.java 1075404 2011-02-28 16:17:29Z ggregory $ + */ +public interface Decoder { + + /** + * Decodes an "encoded" Object and returns a "decoded" + * Object. Note that the implementation of this + * interface will try to cast the Object parameter + * to the specific type expected by a particular Decoder + * implementation. If a {@link ClassCastException} occurs + * this decode method will throw a DecoderException. + * + * @param source the object to decode + * + * @return a 'decoded" object + * + * @throws DecoderException a decoder exception can + * be thrown for any number of reasons. Some good + * candidates are that the parameter passed to this + * method is null, a param cannot be cast to the + * appropriate type for a specific encoder. + */ + Object decode(Object source) throws DecoderException; +} + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/DecoderException.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/DecoderException.java new file mode 100644 index 000000000..88108a548 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/DecoderException.java @@ -0,0 +1,90 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + * Thrown when there is a failure condition during the decoding process. This exception is thrown when a {@link Decoder} + * encounters a decoding specific exception such as invalid data, or characters outside of the expected range. + * + * @author Apache Software Foundation + * @version $Id: DecoderException.java 1080701 2011-03-11 17:52:27Z ggregory $ + */ +public class DecoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public DecoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + */ + public DecoderException(String message) { + super(message); + } + + /** + * Constructsa new exception with the specified detail message and cause. + * + *

    + * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

    + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(Throwable cause) { + super(cause); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/Encoder.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/Encoder.java new file mode 100644 index 000000000..1b81af9b2 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/Encoder.java @@ -0,0 +1,47 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + *

    Provides the highest level of abstraction for Encoders. + * This is the sister interface of {@link Decoder}. Every implementation of + * Encoder provides this common generic interface whic allows a user to pass a + * generic Object to any Encoder implementation in the codec package.

    + * + * @author Apache Software Foundation + * @version $Id: Encoder.java 1075406 2011-02-28 16:18:26Z ggregory $ + */ +public interface Encoder { + + /** + * Encodes an "Object" and returns the encoded content + * as an Object. The Objects here may just be byte[] + * or Strings depending on the implementation used. + * + * @param source An object ot encode + * + * @return An "encoded" Object + * + * @throws EncoderException an encoder exception is + * thrown if the encoder experiences a failure + * condition during the encoding process. + */ + Object encode(Object source) throws EncoderException; +} + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/EncoderException.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/EncoderException.java new file mode 100644 index 000000000..a858abccd --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/EncoderException.java @@ -0,0 +1,91 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + * Thrown when there is a failure condition during the encoding process. This exception is thrown when an + * {@link Encoder} encounters a encoding specific exception such as invalid data, inability to calculate a checksum, + * characters outside of the expected range. + * + * @author Apache Software Foundation + * @version $Id: EncoderException.java 1080701 2011-03-11 17:52:27Z ggregory $ + */ +public class EncoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public EncoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * a useful message relating to the encoder specific error. + */ + public EncoderException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

    + * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

    + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(Throwable cause) { + super(cause); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringDecoder.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringDecoder.java new file mode 100644 index 000000000..a112485b3 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringDecoder.java @@ -0,0 +1,41 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + * Defines common decoding methods for String decoders. + * + * @author Apache Software Foundation + * @version $Id: StringDecoder.java 1080701 2011-03-11 17:52:27Z ggregory $ + */ +public interface StringDecoder extends Decoder { + + /** + * Decodes a String and returns a String. + * + * @param source the String to decode + * + * @return the encoded String + * + * @throws DecoderException thrown if there is + * an error condition during the Encoding process. + */ + String decode(String source) throws DecoderException; +} + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoder.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoder.java new file mode 100644 index 000000000..2bcc5165d --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoder.java @@ -0,0 +1,41 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +/** + * Defines common encoding methods for String encoders. + * + * @author Apache Software Foundation + * @version $Id: StringEncoder.java 1080701 2011-03-11 17:52:27Z ggregory $ + */ +public interface StringEncoder extends Encoder { + + /** + * Encodes a String and returns a String. + * + * @param source the String to encode + * + * @return the encoded String + * + * @throws EncoderException thrown if there is + * an error conidition during the Encoding process. + */ + String encode(String source) throws EncoderException; +} + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoderComparator.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoderComparator.java new file mode 100644 index 000000000..8923df571 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/StringEncoderComparator.java @@ -0,0 +1,87 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec; + +import java.util.Comparator; + +/** + * Compares Strings using a {@link StringEncoder}. This comparator is used to sort Strings by an encoding scheme such as + * Soundex, Metaphone, etc. This class can come in handy if one need to sort Strings by an encoded form of a name such + * as Soundex. + * + * @author Apache Software Foundation + * @version $Id: StringEncoderComparator.java 1080701 2011-03-11 17:52:27Z ggregory $ + */ +@SuppressWarnings("rawtypes") +public class StringEncoderComparator implements Comparator { + + /** + * Internal encoder instance. + */ + private final StringEncoder stringEncoder; + + /** + * Constructs a new instance. + * + * @deprecated Creating an instance without a {@link StringEncoder} leads to a {@link NullPointerException}. Will be + * removed in 2.0. + */ + public StringEncoderComparator() { + this.stringEncoder = null; // Trying to use this will cause things to break + } + + /** + * Constructs a new instance with the given algorithm. + * + * @param stringEncoder + * the StringEncoder used for comparisons. + */ + public StringEncoderComparator(StringEncoder stringEncoder) { + this.stringEncoder = stringEncoder; + } + + /** + * Compares two strings based not on the strings themselves, but on an encoding of the two strings using the + * StringEncoder this Comparator was created with. + * + * If an {@link EncoderException} is encountered, return 0. + * + * @param o1 + * the object to compare + * @param o2 + * the object to compare to + * @return the Comparable.compareTo() return code or 0 if an encoding error was caught. + * @see Comparable + */ + @SuppressWarnings("unchecked") + public int compare(Object o1, Object o2) { + + int compareCode = 0; + + try { + Comparable s1 = (Comparable) this.stringEncoder.encode(o1); + Comparable s2 = (Comparable) this.stringEncoder.encode(o2); + compareCode = s1.compareTo(s2); + } catch (EncoderException ee) { + compareCode = 0; + } + return compareCode; + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32.java new file mode 100644 index 000000000..d0d923e62 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32.java @@ -0,0 +1,471 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +/** + * Provides Base32 encoding and decoding as defined by RFC 4648. + * + *

    + * The class can be parameterized in the following manner with various constructors: + *

      + *
    • Whether to use the "base32hex" variant instead of the default "base32"
    • + *
    • Line length: Default 76. Line length that aren't multiples of 8 will still essentially end up being multiples of + * 8 in the encoded data. + *
    • Line separator: Default is CRLF ("\r\n")
    • + *
    + *

    + *

    + * This class operates directly on byte streams, and not character streams. + *

    + *

    + * This class is not thread-safe. Each thread should use its own instance. + *

    + * + * @see RFC 4648 + * + * @since 1.5 + * @version $Revision: 1080712 $ + */ +public class Base32 extends BaseNCodec { + + /** + * BASE32 characters are 5 bits in length. + * They are formed by taking a block of five octets to form a 40-bit string, + * which is converted into eight BASE32 characters. + */ + private static final int BITS_PER_ENCODED_BYTE = 5; + private static final int BYTES_PER_ENCODED_BLOCK = 8; + private static final int BYTES_PER_UNENCODED_BLOCK = 5; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + * @see RFC 2045 section 2.1 + */ + private static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base32 Alphabet" (as specified in + * Table 3 of RFC 2045) into their 5-bit positive integer equivalents. Characters that are not in the Base32 + * alphabet but fall within the bounds of the array are translated to -1. + * + */ + private static final byte[] DECODE_TABLE = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63, // 20-2f + -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, // 30-3f 2-7 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-N + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 50-5a O-Z + }; + + /** + * This array is a lookup table that translates 5-bit positive integer index values into their "Base32 Alphabet" + * equivalents as specified in Table 3 of RFC 2045. + */ + private static final byte[] ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '2', '3', '4', '5', '6', '7', + }; + + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base32 |Hex Alphabet" (as specified in + * Table 3 of RFC 2045) into their 5-bit positive integer equivalents. Characters that are not in the Base32 Hex + * alphabet but fall within the bounds of the array are translated to -1. + * + */ + private static final byte[] HEX_DECODE_TABLE = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63, // 20-2f + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 30-3f 2-7 + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 40-4f A-N + 25, 26, 27, 28, 29, 30, 31, 32, // 50-57 O-V + }; + + /** + * This array is a lookup table that translates 5-bit positive integer index values into their "Base32 Hex Alphabet" + * equivalents as specified in Table 3 of RFC 2045. + */ + private static final byte[] HEX_ENCODE_TABLE = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + }; + + /** Mask used to extract 5 bits, used when encoding Base32 bytes */ + private static final int MASK_5BITS = 0x1f; + + // The static final fields above are used for the original static byte[] methods on Base32. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Place holder for the bytes we're dealing with for our based logic. + * Bitwise operations store and extract the encoding or decoding from this variable. + */ + private long bitWorkArea; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * decodeSize = {@link BYTES_PER_ENCODED_BLOCK} - 1 + lineSeparator.length; + */ + private final int decodeSize; + + /** + * Decode table to use. + */ + private final byte[] decodeTable; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * encodeSize = {@link BYTES_PER_ENCODED_BLOCK} + lineSeparator.length; + */ + private final int encodeSize; + + /** + * Encode table to use. + */ + private final byte[] encodeTable; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; + + /** + * Creates a Base32 codec used for decoding and encoding. + *

    + * When encoding the line length is 0 (no chunking). + *

    + * + */ + public Base32() { + this(false); + } + + /** + * Creates a Base32 codec used for decoding and encoding. + *

    + * When encoding the line length is 0 (no chunking). + *

    + * @param useHex if true then use Base32 Hex alphabet + */ + public Base32(boolean useHex) { + this(0, null, useHex); + } + + /** + * Creates a Base32 codec used for decoding and encoding. + *

    + * When encoding the line length is given in the constructor, the line separator is CRLF. + *

    + * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 8). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + */ + public Base32(int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base32 codec used for decoding and encoding. + *

    + * When encoding the line length and line separator are given in the constructor. + *

    + *

    + * Line lengths that aren't multiples of 8 will still essentially end up being multiples of 8 in the encoded data. + *

    + * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 8). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @throws IllegalArgumentException + * The provided lineSeparator included some Base32 characters. That's not going to work! + */ + public Base32(int lineLength, byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base32 / Base32 Hex codec used for decoding and encoding. + *

    + * When encoding the line length and line separator are given in the constructor. + *

    + *

    + * Line lengths that aren't multiples of 8 will still essentially end up being multiples of 8 in the encoded data. + *

    + * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 8). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @param useHex if true, then use Base32 Hex alphabet, otherwise use Base32 alphabet + * @throws IllegalArgumentException + * The provided lineSeparator included some Base32 characters. That's not going to work! + * Or the lineLength > 0 and lineSeparator is null. + */ + public Base32(int lineLength, byte[] lineSeparator, boolean useHex) { + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, + lineLength, + lineSeparator == null ? 0 : lineSeparator.length); + if (useHex){ + this.encodeTable = HEX_ENCODE_TABLE; + this.decodeTable = HEX_DECODE_TABLE; + } else { + this.encodeTable = ENCODE_TABLE; + this.decodeTable = DECODE_TABLE; + } + if (lineLength > 0) { + if (lineSeparator == null) { + throw new IllegalArgumentException("lineLength "+lineLength+" > 0, but lineSeparator is null"); + } + // Must be done after initializing the tables + if (containsAlphabetOrPad(lineSeparator)) { + String sep = StringUtils.newStringUtf8(lineSeparator); + throw new IllegalArgumentException("lineSeparator must not contain Base32 characters: [" + sep + "]"); + } + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + this.decodeSize = this.encodeSize - 1; + } + + /** + *

    + * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once + * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" + * call is not necessary when decoding, but it doesn't hurt, either. + *

    + *

    + * Ignores all non-Base32 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are + * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, + * garbage-out philosophy: it will not check the provided data for validity. + *

    + * + * @param in + * byte[] array of ascii data to Base32 decode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + * + * Output is written to {@link #buffer} as 8-bit octets, using {@link pos} as the buffer position + */ + void decode(byte[] in, int inPos, int inAvail) { // package protected for access from I/O streams + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + } + for (int i = 0; i < inAvail; i++) { + byte b = in[inPos++]; + if (b == PAD) { + // We're done. + eof = true; + break; + } else { + ensureBufferSize(decodeSize); + if (b >= 0 && b < this.decodeTable.length) { + int result = this.decodeTable[b]; + if (result >= 0) { + modulus = (modulus+1) % BYTES_PER_ENCODED_BLOCK; + bitWorkArea = (bitWorkArea << BITS_PER_ENCODED_BYTE) + result; // collect decoded bytes + if (modulus == 0) { // we can output the 5 bytes + buffer[pos++] = (byte) ((bitWorkArea >> 32) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 24) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) (bitWorkArea & MASK_8BITS); + } + } + } + } + } + + // Two forms of EOF as far as Base32 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (eof && modulus >= 2) { // if modulus < 2, nothing to do + ensureBufferSize(decodeSize); + + // we ignore partial bytes, i.e. only multiples of 8 count + switch (modulus) { + case 2 : // 10 bits, drop 2 and output one byte + buffer[pos++] = (byte) ((bitWorkArea >> 2) & MASK_8BITS); + break; + case 3 : // 15 bits, drop 7 and output 1 byte + buffer[pos++] = (byte) ((bitWorkArea >> 7) & MASK_8BITS); + break; + case 4 : // 20 bits = 2*8 + 4 + bitWorkArea = bitWorkArea >> 4; // drop 4 bits + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + case 5 : // 25bits = 3*8 + 1 + bitWorkArea = bitWorkArea >> 1; + buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + case 6 : // 30bits = 3*8 + 6 + bitWorkArea = bitWorkArea >> 6; + buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + case 7 : // 35 = 4*8 +3 + bitWorkArea = bitWorkArea >> 3; + buffer[pos++] = (byte) ((bitWorkArea >> 24) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + } + } + } + + /** + *

    + * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with + * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last + * remaining bytes (if not multiple of 5). + *

    + * + * @param in + * byte[] array of binary data to Base32 encode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + */ + void encode(byte[] in, int inPos, int inAvail) { // package protected for access from I/O streams + if (eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + eof = true; + if (0 == modulus && lineLength == 0) { + return; // no leftovers to process and not using chunking + } + ensureBufferSize(encodeSize); + int savedPos = pos; + switch (modulus) { // % 5 + case 1 : // Only 1 octet; take top 5 bits then remainder + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 3) & MASK_5BITS]; // 8-1*5 = 3 + buffer[pos++] = encodeTable[(int)(bitWorkArea << 2) & MASK_5BITS]; // 5-3=2 + buffer[pos++] = PAD; + buffer[pos++] = PAD; + buffer[pos++] = PAD; + buffer[pos++] = PAD; + buffer[pos++] = PAD; + buffer[pos++] = PAD; + break; + + case 2 : // 2 octets = 16 bits to use + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 11) & MASK_5BITS]; // 16-1*5 = 11 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 6) & MASK_5BITS]; // 16-2*5 = 6 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 1) & MASK_5BITS]; // 16-3*5 = 1 + buffer[pos++] = encodeTable[(int)(bitWorkArea << 4) & MASK_5BITS]; // 5-1 = 4 + buffer[pos++] = PAD; + buffer[pos++] = PAD; + buffer[pos++] = PAD; + buffer[pos++] = PAD; + break; + case 3 : // 3 octets = 24 bits to use + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 19) & MASK_5BITS]; // 24-1*5 = 19 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 14) & MASK_5BITS]; // 24-2*5 = 14 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 9) & MASK_5BITS]; // 24-3*5 = 9 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 4) & MASK_5BITS]; // 24-4*5 = 4 + buffer[pos++] = encodeTable[(int)(bitWorkArea << 1) & MASK_5BITS]; // 5-4 = 1 + buffer[pos++] = PAD; + buffer[pos++] = PAD; + buffer[pos++] = PAD; + break; + case 4 : // 4 octets = 32 bits to use + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 27) & MASK_5BITS]; // 32-1*5 = 27 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 22) & MASK_5BITS]; // 32-2*5 = 22 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 17) & MASK_5BITS]; // 32-3*5 = 17 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 12) & MASK_5BITS]; // 32-4*5 = 12 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 7) & MASK_5BITS]; // 32-5*5 = 7 + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 2) & MASK_5BITS]; // 32-6*5 = 2 + buffer[pos++] = encodeTable[(int)(bitWorkArea << 3) & MASK_5BITS]; // 5-2 = 3 + buffer[pos++] = PAD; + break; + } + currentLinePos += pos - savedPos; // keep track of current line position + // if currentPos == 0 we are at the start of a line, so don't add CRLF + if (lineLength > 0 && currentLinePos > 0){ // add chunk separator if required + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + ensureBufferSize(encodeSize); + modulus = (modulus+1) % BYTES_PER_UNENCODED_BLOCK; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + bitWorkArea = (bitWorkArea << 8) + b; // BITS_PER_BYTE + if (0 == modulus) { // we have enough bytes to create our output + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 35) & MASK_5BITS]; + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 30) & MASK_5BITS]; + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 25) & MASK_5BITS]; + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 20) & MASK_5BITS]; + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 15) & MASK_5BITS]; + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 10) & MASK_5BITS]; + buffer[pos++] = encodeTable[(int)(bitWorkArea >> 5) & MASK_5BITS]; + buffer[pos++] = encodeTable[(int)bitWorkArea & MASK_5BITS]; + currentLinePos += BYTES_PER_ENCODED_BLOCK; + if (lineLength > 0 && lineLength <= currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + currentLinePos = 0; + } + } + } + } + } + + /** + * Returns whether or not the octet is in the Base32 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the Base32 alphabet false otherwise. + */ + public boolean isInAlphabet(byte octet) { + return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32InputStream.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32InputStream.java new file mode 100644 index 000000000..acb284f9b --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32InputStream.java @@ -0,0 +1,85 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.InputStream; + +/** + * Provides Base32 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength + * is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate + * constructor. + *

    + * The default behaviour of the Base32InputStream is to DECODE, whereas the default behaviour of the Base32OutputStream + * is to ENCODE, but this behaviour can be overridden by using a different constructor. + *

    + *

    + * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). + *

    + * + * @version $Revision: 1063784 $ + * @see RFC 4648 + * @since 1.5 + */ +public class Base32InputStream extends BaseNCodecInputStream { + + /** + * Creates a Base32InputStream such that all data read is Base32-decoded from the original provided InputStream. + * + * @param in + * InputStream to wrap. + */ + public Base32InputStream(InputStream in) { + this(in, false); + } + + /** + * Creates a Base32InputStream such that all data read is either Base32-encoded or Base32-decoded from the original + * provided InputStream. + * + * @param in + * InputStream to wrap. + * @param doEncode + * true if we should encode all data read from us, false if we should decode. + */ + public Base32InputStream(InputStream in, boolean doEncode) { + super(in, new Base32(false), doEncode); + } + + /** + * Creates a Base32InputStream such that all data read is either Base32-encoded or Base32-decoded from the original + * provided InputStream. + * + * @param in + * InputStream to wrap. + * @param doEncode + * true if we should encode all data read from us, false if we should decode. + * @param lineLength + * If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to + * nearest multiple of 4). If lineLength <=0, the encoded data is not divided into lines. If doEncode is + * false, lineLength is ignored. + * @param lineSeparator + * If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \r\n). + * If lineLength <= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored. + */ + public Base32InputStream(InputStream in, boolean doEncode, int lineLength, byte[] lineSeparator) { + super(in, new Base32(lineLength, lineSeparator), doEncode); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32OutputStream.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32OutputStream.java new file mode 100644 index 000000000..af2f4d718 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base32OutputStream.java @@ -0,0 +1,85 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.OutputStream; + +/** + * Provides Base32 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength + * is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate + * constructor. + *

    + * The default behaviour of the Base32OutputStream is to ENCODE, whereas the default behaviour of the Base32InputStream + * is to DECODE. But this behaviour can be overridden by using a different constructor. + *

    + *

    + * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). + *

    + * + * @version $Revision: 1064132 $ + * @see RFC 4648 + * @since 1.5 + */ +public class Base32OutputStream extends BaseNCodecOutputStream { + + /** + * Creates a Base32OutputStream such that all data written is Base32-encoded to the original provided OutputStream. + * + * @param out + * OutputStream to wrap. + */ + public Base32OutputStream(OutputStream out) { + this(out, true); + } + + /** + * Creates a Base32OutputStream such that all data written is either Base32-encoded or Base32-decoded to the + * original provided OutputStream. + * + * @param out + * OutputStream to wrap. + * @param doEncode + * true if we should encode all data written to us, false if we should decode. + */ + public Base32OutputStream(OutputStream out, boolean doEncode) { + super(out, new Base32(false), doEncode); + } + + /** + * Creates a Base32OutputStream such that all data written is either Base32-encoded or Base32-decoded to the + * original provided OutputStream. + * + * @param out + * OutputStream to wrap. + * @param doEncode + * true if we should encode all data written to us, false if we should decode. + * @param lineLength + * If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to + * nearest multiple of 4). If lineLength <=0, the encoded data is not divided into lines. If doEncode is + * false, lineLength is ignored. + * @param lineSeparator + * If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \r\n). + * If lineLength <= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored. + */ + public Base32OutputStream(OutputStream out, boolean doEncode, int lineLength, byte[] lineSeparator) { + super(out, new Base32(lineLength, lineSeparator), doEncode); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64.java new file mode 100644 index 000000000..07ed1a4c4 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64.java @@ -0,0 +1,756 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.math.BigInteger; + +/** + * Provides Base64 encoding and decoding as defined by RFC 2045. + * + *

    + * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. + *

    + *

    + * The class can be parameterized in the following manner with various constructors: + *

      + *
    • URL-safe mode: Default off.
    • + *
    • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of + * 4 in the encoded data. + *
    • Line separator: Default is CRLF ("\r\n")
    • + *
    + *

    + *

    + * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). + *

    + *

    + * This class is not thread-safe. Each thread should use its own instance. + *

    + * + * @see RFC 2045 + * @author Apache Software Foundation + * @since 1.0 + * @version $Revision: 1080712 $ + */ +public class Base64 extends BaseNCodec { + + /** + * BASE32 characters are 6 bits in length. + * They are formed by taking a block of 3 octets to form a 24-bit string, + * which is converted into 4 BASE64 characters. + */ + private static final int BITS_PER_ENCODED_BYTE = 6; + private static final int BYTES_PER_UNENCODED_BLOCK = 3; + private static final int BYTES_PER_ENCODED_BLOCK = 4; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + *

    + * N.B. The next major release may break compatibility and make this field private. + *

    + * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + + /** + * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" + * equivalents as specified in Table 1 of RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / + * changed to - and _ to make the encoded Base64 results more URL-SAFE. + * This table is only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in + * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 + * alphabet but fall within the bounds of the array are translated to -1. + * + * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both + * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + /** + * Base64 uses 6-bit fields. + */ + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + + // The static final fields above are used for the original static byte[] methods on Base64. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able + * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch + * between the two modes. + */ + private final byte[] encodeTable; + + // Only one decode table currently; keep for consistency with Base32 code + private final byte[] decodeTable = DECODE_TABLE; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * decodeSize = 3 + lineSeparator.length; + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * encodeSize = 4 + lineSeparator.length; + */ + private final int encodeSize; + + /** + * Place holder for the bytes we're dealing with for our based logic. + * Bitwise operations store and extract the encoding or decoding from this variable. + */ + private int bitWorkArea; + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

    + * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE. + *

    + * + *

    + * When decoding all variants are supported. + *

    + */ + public Base64() { + this(0); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. + *

    + * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. + *

    + * + *

    + * When decoding all variants are supported. + *

    + * + * @param urlSafe + * if true, URL-safe encoding is used. In most cases this should be set to + * false. + * @since 1.4 + */ + public Base64(boolean urlSafe) { + this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

    + * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

    + *

    + * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

    + *

    + * When decoding all variants are supported. + *

    + * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @since 1.4 + */ + public Base64(int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

    + * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

    + *

    + * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

    + *

    + * When decoding all variants are supported. + *

    + * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @throws IllegalArgumentException + * Thrown when the provided lineSeparator included some base64 characters. + * @since 1.4 + */ + public Base64(int lineLength, byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

    + * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

    + *

    + * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

    + *

    + * When decoding all variants are supported. + *

    + * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @param urlSafe + * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * @throws IllegalArgumentException + * The provided lineSeparator included some base64 characters. That's not going to work! + * @since 1.4 + */ + public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) { + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, + lineLength, + lineSeparator == null ? 0 : lineSeparator.length); + // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0 + // @see test case Base64Test.testConstructors() + if (lineSeparator != null) { + if (containsAlphabetOrPad(lineSeparator)) { + String sep = StringUtils.newStringUtf8(lineSeparator); + throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); + } + if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + this.decodeSize = this.encodeSize - 1; + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } + + /** + * Returns our current encode mode. True if we're URL-SAFE, false otherwise. + * + * @return true if we're in URL-SAFE mode, false otherwise. + * @since 1.4 + */ + public boolean isUrlSafe() { + return this.encodeTable == URL_SAFE_ENCODE_TABLE; + } + + /** + *

    + * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with + * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last + * remaining bytes (if not multiple of 3). + *

    + *

    + * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

    + * + * @param in + * byte[] array of binary data to base64 encode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + */ + void encode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + eof = true; + if (0 == modulus && lineLength == 0) { + return; // no leftovers to process and not using chunking + } + ensureBufferSize(encodeSize); + int savedPos = pos; + switch (modulus) { // 0-2 + case 1 : // 8 bits = 6 + 2 + buffer[pos++] = encodeTable[(bitWorkArea >> 2) & MASK_6BITS]; // top 6 bits + buffer[pos++] = encodeTable[(bitWorkArea << 4) & MASK_6BITS]; // remaining 2 + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + buffer[pos++] = PAD; + } + break; + + case 2 : // 16 bits = 6 + 6 + 4 + buffer[pos++] = encodeTable[(bitWorkArea >> 10) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 4) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea << 2) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + } + break; + } + currentLinePos += pos - savedPos; // keep track of current line position + // if currentPos == 0 we are at the start of a line, so don't add CRLF + if (lineLength > 0 && currentLinePos > 0) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + ensureBufferSize(encodeSize); + modulus = (modulus+1) % BYTES_PER_UNENCODED_BLOCK; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + bitWorkArea = (bitWorkArea << 8) + b; // BITS_PER_BYTE + if (0 == modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract + buffer[pos++] = encodeTable[(bitWorkArea >> 18) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 12) & MASK_6BITS]; + buffer[pos++] = encodeTable[(bitWorkArea >> 6) & MASK_6BITS]; + buffer[pos++] = encodeTable[bitWorkArea & MASK_6BITS]; + currentLinePos += BYTES_PER_ENCODED_BLOCK; + if (lineLength > 0 && lineLength <= currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + currentLinePos = 0; + } + } + } + } + } + + /** + *

    + * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once + * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" + * call is not necessary when decoding, but it doesn't hurt, either. + *

    + *

    + * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are + * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, + * garbage-out philosophy: it will not check the provided data for validity. + *

    + *

    + * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

    + * + * @param in + * byte[] array of ascii data to base64 decode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + */ + void decode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + } + for (int i = 0; i < inAvail; i++) { + ensureBufferSize(decodeSize); + byte b = in[inPos++]; + if (b == PAD) { + // We're done. + eof = true; + break; + } else { + if (b >= 0 && b < DECODE_TABLE.length) { + int result = DECODE_TABLE[b]; + if (result >= 0) { + modulus = (modulus+1) % BYTES_PER_ENCODED_BLOCK; + bitWorkArea = (bitWorkArea << BITS_PER_ENCODED_BYTE) + result; + if (modulus == 0) { + buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) (bitWorkArea & MASK_8BITS); + } + } + } + } + } + + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (eof && modulus != 0) { + ensureBufferSize(decodeSize); + + // We have some spare bits remaining + // Output all whole multiples of 8 bits and ignore the rest + switch (modulus) { + // case 1: // 6 bits - ignore entirely + // break; + case 2 : // 12 bits = 8 + 4 + bitWorkArea = bitWorkArea >> 4; // dump the extra 4 bits + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + case 3 : // 18 bits = 8 + 8 + 2 + bitWorkArea = bitWorkArea >> 2; // dump 2 bits + buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); + buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); + break; + } + } + } + + /** + * Returns whether or not the octet is in the base 64 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the base 64 alphabet, false otherwise. + * @since 1.4 + */ + public static boolean isBase64(byte octet) { + return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); + } + + /** + * Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param base64 + * String to test + * @return true if all characters in the String are valid characters in the Base64 alphabet or if + * the String is empty; false, otherwise + * @since 1.5 + */ + public static boolean isBase64(String base64) { + return isBase64(StringUtils.getBytesUtf8(base64)); + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; + * false, otherwise + * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0. + */ + public static boolean isArrayByteBase64(byte[] arrayOctet) { + return isBase64(arrayOctet); + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; + * false, otherwise + * @since 1.5 + */ + public static boolean isBase64(byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * NOTE: We changed the behaviour of this method from multi-line chunking (commons-codec-1.4) to + * single-line non-chunking (commons-codec-1.5). + * + * @param binaryData + * binary data to encode + * @return String containing Base64 characters. + * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). + */ + public static String encodeBase64String(byte[] binaryData) { + return StringUtils.newStringUtf8(encodeBase64(binaryData, false)); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + * @since 1.4 + */ + public static byte[] encodeBase64URLSafe(byte[] binaryData) { + return encodeBase64(binaryData, false, true); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData + * binary data to encode + * @return String containing Base64 characters + * @since 1.4 + */ + public static String encodeBase64URLSafeString(byte[] binaryData) { + return StringUtils.newStringUtf8(encodeBase64(binaryData, false, true)); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks + * + * @param binaryData + * binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { + return encodeBase64(binaryData, isChunked, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe) { + return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * @param maxResultSize + * The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + + // Create this so can use the super-class method + // Also ensures that the same roundings are performed by the ctor and the code + Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); + long len = b64.getEncodedLength(binaryData); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + + len + + ") than the specified maximum size of " + + maxResultSize); + } + + return b64.encode(binaryData); + } + + /** + * Decodes a Base64 String into octets + * + * @param base64String + * String containing Base64 data + * @return Array containing decoded data. + * @since 1.4 + */ + public static byte[] decodeBase64(String base64String) { + return new Base64().decode(base64String); + } + + /** + * Decodes Base64 data into octets + * + * @param base64Data + * Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + return new Base64().decode(base64Data); + } + + // Implementation of the Encoder Interface + + // Implementation of integer encoding used for crypto + /** + * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param pArray + * a byte array containing base64 character data + * @return A BigInteger + * @since 1.4 + */ + public static BigInteger decodeInteger(byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param bigInt + * a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException + * if null is passed in + * @since 1.4 + */ + public static byte[] encodeInteger(BigInteger bigInt) { + if (bigInt == null) { + throw new NullPointerException("encodeInteger called with null parameter"); + } + return encodeBase64(toIntegerBytes(bigInt), false); + } + + /** + * Returns a byte-array representation of a BigInteger without sign bit. + * + * @param bigInt + * BigInteger to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + byte[] bigBytes = bigInt.toByteArray(); + + if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if ((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + byte[] resizedBytes = new byte[bitlen / 8]; + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + return resizedBytes; + } + + /** + * Returns whether or not the octet is in the Base32 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the Base32 alphabet false otherwise. + */ + protected boolean isInAlphabet(byte octet) { + return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64InputStream.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64InputStream.java new file mode 100644 index 000000000..cf99ceb91 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64InputStream.java @@ -0,0 +1,89 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.InputStream; + +/** + * Provides Base64 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength + * is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate + * constructor. + *

    + * The default behaviour of the Base64InputStream is to DECODE, whereas the default behaviour of the Base64OutputStream + * is to ENCODE, but this behaviour can be overridden by using a different constructor. + *

    + *

    + * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. + *

    + *

    + * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). + *

    + * + * @author Apache Software Foundation + * @version $Id: Base64InputStream.java 1064424 2011-01-28 02:02:46Z sebb $ + * @see RFC 2045 + * @since 1.4 + */ +public class Base64InputStream extends BaseNCodecInputStream { + + /** + * Creates a Base64InputStream such that all data read is Base64-decoded from the original provided InputStream. + * + * @param in + * InputStream to wrap. + */ + public Base64InputStream(InputStream in) { + this(in, false); + } + + /** + * Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original + * provided InputStream. + * + * @param in + * InputStream to wrap. + * @param doEncode + * true if we should encode all data read from us, false if we should decode. + */ + public Base64InputStream(InputStream in, boolean doEncode) { + super(in, new Base64(false), doEncode); + } + + /** + * Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original + * provided InputStream. + * + * @param in + * InputStream to wrap. + * @param doEncode + * true if we should encode all data read from us, false if we should decode. + * @param lineLength + * If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to + * nearest multiple of 4). If lineLength <=0, the encoded data is not divided into lines. If doEncode is + * false, lineLength is ignored. + * @param lineSeparator + * If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \r\n). + * If lineLength <= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored. + */ + public Base64InputStream(InputStream in, boolean doEncode, int lineLength, byte[] lineSeparator) { + super(in, new Base64(lineLength, lineSeparator), doEncode); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64OutputStream.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64OutputStream.java new file mode 100644 index 000000000..df880c2b9 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Base64OutputStream.java @@ -0,0 +1,89 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.OutputStream; + +/** + * Provides Base64 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength + * is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate + * constructor. + *

    + * The default behaviour of the Base64OutputStream is to ENCODE, whereas the default behaviour of the Base64InputStream + * is to DECODE. But this behaviour can be overridden by using a different constructor. + *

    + *

    + * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. + *

    + *

    + * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). + *

    + * + * @author Apache Software Foundation + * @version $Id: Base64OutputStream.java 1064424 2011-01-28 02:02:46Z sebb $ + * @see RFC 2045 + * @since 1.4 + */ +public class Base64OutputStream extends BaseNCodecOutputStream { + + /** + * Creates a Base64OutputStream such that all data written is Base64-encoded to the original provided OutputStream. + * + * @param out + * OutputStream to wrap. + */ + public Base64OutputStream(OutputStream out) { + this(out, true); + } + + /** + * Creates a Base64OutputStream such that all data written is either Base64-encoded or Base64-decoded to the + * original provided OutputStream. + * + * @param out + * OutputStream to wrap. + * @param doEncode + * true if we should encode all data written to us, false if we should decode. + */ + public Base64OutputStream(OutputStream out, boolean doEncode) { + super(out,new Base64(false), doEncode); + } + + /** + * Creates a Base64OutputStream such that all data written is either Base64-encoded or Base64-decoded to the + * original provided OutputStream. + * + * @param out + * OutputStream to wrap. + * @param doEncode + * true if we should encode all data written to us, false if we should decode. + * @param lineLength + * If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to + * nearest multiple of 4). If lineLength <=0, the encoded data is not divided into lines. If doEncode is + * false, lineLength is ignored. + * @param lineSeparator + * If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \r\n). + * If lineLength <= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored. + */ + public Base64OutputStream(OutputStream out, boolean doEncode, int lineLength, byte[] lineSeparator) { + super(out, new Base64(lineLength, lineSeparator), doEncode); + } +} \ No newline at end of file diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodec.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodec.java new file mode 100644 index 000000000..c2ed4efef --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodec.java @@ -0,0 +1,445 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import org.mozilla.apache.commons.codec.BinaryDecoder; +import org.mozilla.apache.commons.codec.BinaryEncoder; +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; + +/** + * Abstract superclass for Base-N encoders and decoders. + * + *

    + * This class is not thread-safe. + * Each thread should use its own instance. + *

    + */ +public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder { + + /** + * MIME chunk size per RFC 2045 section 6.8. + * + *

    + * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

    + * + * @see RFC 2045 section 6.8 + */ + public static final int MIME_CHUNK_SIZE = 76; + + /** + * PEM chunk size per RFC 1421 section 4.3.2.4. + * + *

    + * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

    + * + * @see RFC 1421 section 4.3.2.4 + */ + public static final int PEM_CHUNK_SIZE = 64; + + private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; + + /** + * Defines the default buffer size - currently {@value} + * - must be large enough for at least one encoded block+separator + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** Mask used to extract 8 bits, used in decoding bytes */ + protected static final int MASK_8BITS = 0xff; + + /** + * Byte used to pad output. + */ + protected static final byte PAD_DEFAULT = '='; // Allow static access to default + + protected final byte PAD = PAD_DEFAULT; // instance variable just in case it needs to vary later + + /** Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */ + private final int unencodedBlockSize; + + /** Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */ + private final int encodedBlockSize; + + /** + * Chunksize for encoding. Not used when decoding. + * A value of zero or less implies no chunking of the encoded data. + * Rounded down to nearest multiple of encodedBlockSize. + */ + protected final int lineLength; + + /** + * Size of chunk separator. Not used unless {@link #lineLength} > 0. + */ + private final int chunkSeparatorLength; + + /** + * Buffer for streaming. + */ + protected byte[] buffer; + + /** + * Position where next character should be written in the buffer. + */ + protected int pos; + + /** + * Position where next character should be read from the buffer. + */ + private int readPos; + + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless, + * and must be thrown away. + */ + protected boolean eof; + + /** + * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to + * make sure each encoded line never goes beyond lineLength (if lineLength > 0). + */ + protected int currentLinePos; + + /** + * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. + * This variable helps track that. + */ + protected int modulus; + + /** + * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize} + * If chunkSeparatorLength is zero, then chunking is disabled. + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length lineLength + * @param chunkSeparatorLength the chunk separator length, if relevant + */ + protected BaseNCodec(int unencodedBlockSize, int encodedBlockSize, int lineLength, int chunkSeparatorLength){ + this.unencodedBlockSize = unencodedBlockSize; + this.encodedBlockSize = encodedBlockSize; + this.lineLength = (lineLength > 0 && chunkSeparatorLength > 0) ? (lineLength / encodedBlockSize) * encodedBlockSize : 0; + this.chunkSeparatorLength = chunkSeparatorLength; + } + + /** + * Returns true if this object has buffered data for reading. + * + * @return true if there is data still available for reading. + */ + boolean hasData() { // package protected for access from I/O streams + return this.buffer != null; + } + + /** + * Returns the amount of buffered data available for reading. + * + * @return The amount of buffered data available for reading. + */ + int available() { // package protected for access from I/O streams + return buffer != null ? pos - readPos : 0; + } + + /** + * Get the default buffer size. Can be overridden. + * + * @return {@link #DEFAULT_BUFFER_SIZE} + */ + protected int getDefaultBufferSize() { + return DEFAULT_BUFFER_SIZE; + } + + /** Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. */ + private void resizeBuffer() { + if (buffer == null) { + buffer = new byte[getDefaultBufferSize()]; + pos = 0; + readPos = 0; + } else { + byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; + System.arraycopy(buffer, 0, b, 0, buffer.length); + buffer = b; + } + } + + /** + * Ensure that the buffer has room for size bytes + * + * @param size minimum spare space required + */ + protected void ensureBufferSize(int size){ + if ((buffer == null) || (buffer.length < pos + size)){ + resizeBuffer(); + } + } + + /** + * Extracts buffered data into the provided byte[] array, starting at position bPos, + * up to a maximum of bAvail bytes. Returns how many bytes were actually extracted. + * + * @param b + * byte[] array to extract the buffered data into. + * @param bPos + * position in byte[] array to start extraction at. + * @param bAvail + * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). + * @return The number of bytes successfully extracted into the provided byte[] array. + */ + int readResults(byte[] b, int bPos, int bAvail) { // package protected for access from I/O streams + if (buffer != null) { + int len = Math.min(available(), bAvail); + System.arraycopy(buffer, readPos, b, bPos, len); + readPos += len; + if (readPos >= pos) { + buffer = null; // so hasData() will return false, and this method can return -1 + } + return len; + } + return eof ? -1 : 0; + } + + /** + * Checks if a byte value is whitespace or not. + * Whitespace is taken to mean: space, tab, CR, LF + * @param byteToCheck + * the byte to check + * @return true if byte is whitespace, false otherwise + */ + protected static boolean isWhiteSpace(byte byteToCheck) { + switch (byteToCheck) { + case ' ' : + case '\n' : + case '\r' : + case '\t' : + return true; + default : + return false; + } + } + + /** + * Resets this object to its initial newly constructed state. + */ + private void reset() { + buffer = null; + pos = 0; + readPos = 0; + currentLinePos = 0; + modulus = 0; + eof = false; + } + + /** + * Encodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of the + * Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[]. + * + * @param pObject + * Object to encode + * @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied. + * @throws EncoderException + * if the parameter supplied is not of type byte[] + */ + public Object encode(Object pObject) throws EncoderException { + if (!(pObject instanceof byte[])) { + throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]"); + } + return encode((byte[]) pObject); + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A String containing only Base-N character data + */ + public String encodeToString(byte[] pArray) { + return StringUtils.newStringUtf8(encode(pArray)); + } + + /** + * Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of the + * Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String. + * + * @param pObject + * Object to decode + * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String supplied. + * @throws DecoderException + * if the parameter supplied is not of type byte[] + */ + public Object decode(Object pObject) throws DecoderException { + if (pObject instanceof byte[]) { + return decode((byte[]) pObject); + } else if (pObject instanceof String) { + return decode((String) pObject); + } else { + throw new DecoderException("Parameter supplied to Base-N decode is not a byte[] or a String"); + } + } + + /** + * Decodes a String containing characters in the Base-N alphabet. + * + * @param pArray + * A String containing Base-N character data + * @return a byte array containing binary data + */ + public byte[] decode(String pArray) { + return decode(StringUtils.getBytesUtf8(pArray)); + } + + /** + * Decodes a byte[] containing characters in the Base-N alphabet. + * + * @param pArray + * A byte array containing Base-N character data + * @return a byte array containing binary data + */ + public byte[] decode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + decode(pArray, 0, pArray.length); + decode(pArray, 0, -1); // Notify decoder of EOF. + byte[] result = new byte[pos]; + readResults(result, 0, result.length); + return result; + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A byte array containing only the basen alphabetic character data + */ + public byte[] encode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + encode(pArray, 0, pArray.length); + encode(pArray, 0, -1); // Notify encoder of EOF. + byte[] buf = new byte[pos - readPos]; + readResults(buf, 0, buf.length); + return buf; + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet. + * Uses UTF8 encoding. + * + * @param pArray a byte array containing binary data + * @return String containing only character data in the appropriate alphabet. + */ + public String encodeAsString(byte[] pArray){ + return StringUtils.newStringUtf8(encode(pArray)); + } + + abstract void encode(byte[] pArray, int i, int length); // package protected for access from I/O streams + + abstract void decode(byte[] pArray, int i, int length); // package protected for access from I/O streams + + /** + * Returns whether or not the octet is in the current alphabet. + * Does not allow whitespace or pad. + * + * @param value The value to test + * + * @return true if the value is defined in the current alphabet, false otherwise. + */ + protected abstract boolean isInAlphabet(byte value); + + /** + * Tests a given byte array to see if it contains only valid characters within the alphabet. + * The method optionally treats whitespace and pad as valid. + * + * @param arrayOctet byte array to test + * @param allowWSPad if true, then whitespace and PAD are also allowed + * + * @return true if all bytes are valid characters in the alphabet or if the byte array is empty; + * false, otherwise + */ + public boolean isInAlphabet(byte[] arrayOctet, boolean allowWSPad) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isInAlphabet(arrayOctet[i]) && + (!allowWSPad || (arrayOctet[i] != PAD) && !isWhiteSpace(arrayOctet[i]))) { + return false; + } + } + return true; + } + + /** + * Tests a given String to see if it contains only valid characters within the alphabet. + * The method treats whitespace and PAD as valid. + * + * @param basen String to test + * @return true if all characters in the String are valid characters in the alphabet or if + * the String is empty; false, otherwise + * @see #isInAlphabet(byte[], boolean) + */ + public boolean isInAlphabet(String basen) { + return isInAlphabet(StringUtils.getBytesUtf8(basen), true); + } + + /** + * Tests a given byte array to see if it contains any characters within the alphabet or PAD. + * + * Intended for use in checking line-ending arrays + * + * @param arrayOctet + * byte array to test + * @return true if any byte is a valid character in the alphabet or PAD; false otherwise + */ + protected boolean containsAlphabetOrPad(byte[] arrayOctet) { + if (arrayOctet == null) { + return false; + } + for (int i = 0; i < arrayOctet.length; i++) { + if (PAD == arrayOctet[i] || isInAlphabet(arrayOctet[i])) { + return true; + } + } + return false; + } + + /** + * Calculates the amount of space needed to encode the supplied array. + * + * @param pArray byte[] array which will later be encoded + * + * @return amount of space needed to encoded the supplied array. + * Returns a long since a max-len array will require > Integer.MAX_VALUE + */ + public long getEncodedLength(byte[] pArray) { + // Calculate non-chunked size - rounded up to allow for padding + // cast to long is needed to avoid possibility of overflow + long len = ((pArray.length + unencodedBlockSize-1) / unencodedBlockSize) * (long) encodedBlockSize; + if (lineLength > 0) { // We're using chunking + // Round up to nearest multiple + len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength; + } + return len; + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecInputStream.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecInputStream.java new file mode 100644 index 000000000..0aa879b15 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecInputStream.java @@ -0,0 +1,132 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Abstract superclass for Base-N input streams. + * + * @since 1.5 + */ +public class BaseNCodecInputStream extends FilterInputStream { + + private final boolean doEncode; + + private final BaseNCodec baseNCodec; + + private final byte[] singleByte = new byte[1]; + + protected BaseNCodecInputStream(InputStream in, BaseNCodec baseNCodec, boolean doEncode) { + super(in); + this.doEncode = doEncode; + this.baseNCodec = baseNCodec; + } + + /** + * Reads one byte from this input stream. + * + * @return the byte as an integer in the range 0 to 255. Returns -1 if EOF has been reached. + * @throws IOException + * if an I/O error occurs. + */ + public int read() throws IOException { + int r = read(singleByte, 0, 1); + while (r == 0) { + r = read(singleByte, 0, 1); + } + if (r > 0) { + return singleByte[0] < 0 ? 256 + singleByte[0] : singleByte[0]; + } + return -1; + } + + /** + * Attempts to read len bytes into the specified b array starting at offset + * from this InputStream. + * + * @param b + * destination byte array + * @param offset + * where to start writing the bytes + * @param len + * maximum number of bytes to read + * + * @return number of bytes read + * @throws IOException + * if an I/O error occurs. + * @throws NullPointerException + * if the byte array parameter is null + * @throws IndexOutOfBoundsException + * if offset, len or buffer size are invalid + */ + public int read(byte b[], int offset, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (offset < 0 || len < 0) { + throw new IndexOutOfBoundsException(); + } else if (offset > b.length || offset + len > b.length) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } else { + int readLen = 0; + /* + Rationale for while-loop on (readLen == 0): + ----- + Base32.readResults() usually returns > 0 or EOF (-1). In the + rare case where it returns 0, we just keep trying. + + This is essentially an undocumented contract for InputStream + implementors that want their code to work properly with + java.io.InputStreamReader, since the latter hates it when + InputStream.read(byte[]) returns a zero. Unfortunately our + readResults() call must return 0 if a large amount of the data + being decoded was non-base32, so this while-loop enables proper + interop with InputStreamReader for that scenario. + ----- + This is a fix for CODEC-101 + */ + while (readLen == 0) { + if (!baseNCodec.hasData()) { + byte[] buf = new byte[doEncode ? 4096 : 8192]; + int c = in.read(buf); + if (doEncode) { + baseNCodec.encode(buf, 0, c); + } else { + baseNCodec.decode(buf, 0, c); + } + } + readLen = baseNCodec.readResults(b, offset, len); + } + return readLen; + } + } + /** + * {@inheritDoc} + * + * @return false + */ + public boolean markSupported() { + return false; // not an easy job to support marks + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecOutputStream.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecOutputStream.java new file mode 100644 index 000000000..bdcbd4d34 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BaseNCodecOutputStream.java @@ -0,0 +1,142 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Abstract superclass for Base-N output streams. + * + * @since 1.5 + */ +public class BaseNCodecOutputStream extends FilterOutputStream { + + private final boolean doEncode; + + private final BaseNCodec baseNCodec; + + private final byte[] singleByte = new byte[1]; + + public BaseNCodecOutputStream(OutputStream out, BaseNCodec basedCodec, boolean doEncode) { + super(out); + this.baseNCodec = basedCodec; + this.doEncode = doEncode; + } + + /** + * Writes the specified byte to this output stream. + * + * @param i + * source byte + * @throws IOException + * if an I/O error occurs. + */ + public void write(int i) throws IOException { + singleByte[0] = (byte) i; + write(singleByte, 0, 1); + } + + /** + * Writes len bytes from the specified b array starting at offset to this + * output stream. + * + * @param b + * source byte array + * @param offset + * where to start reading the bytes + * @param len + * maximum number of bytes to write + * + * @throws IOException + * if an I/O error occurs. + * @throws NullPointerException + * if the byte array parameter is null + * @throws IndexOutOfBoundsException + * if offset, len or buffer size are invalid + */ + public void write(byte b[], int offset, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (offset < 0 || len < 0) { + throw new IndexOutOfBoundsException(); + } else if (offset > b.length || offset + len > b.length) { + throw new IndexOutOfBoundsException(); + } else if (len > 0) { + if (doEncode) { + baseNCodec.encode(b, offset, len); + } else { + baseNCodec.decode(b, offset, len); + } + flush(false); + } + } + + /** + * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propogate is + * true, the wrapped stream will also be flushed. + * + * @param propogate + * boolean flag to indicate whether the wrapped OutputStream should also be flushed. + * @throws IOException + * if an I/O error occurs. + */ + private void flush(boolean propogate) throws IOException { + int avail = baseNCodec.available(); + if (avail > 0) { + byte[] buf = new byte[avail]; + int c = baseNCodec.readResults(buf, 0, avail); + if (c > 0) { + out.write(buf, 0, c); + } + } + if (propogate) { + out.flush(); + } + } + + /** + * Flushes this output stream and forces any buffered output bytes to be written out to the stream. + * + * @throws IOException + * if an I/O error occurs. + */ + public void flush() throws IOException { + flush(true); + } + + /** + * Closes this output stream and releases any system resources associated with the stream. + * + * @throws IOException + * if an I/O error occurs. + */ + public void close() throws IOException { + // Notify encoder of EOF (-1). + if (doEncode) { + baseNCodec.encode(singleByte, 0, -1); + } else { + baseNCodec.decode(singleByte, 0, -1); + } + flush(); + out.close(); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BinaryCodec.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BinaryCodec.java new file mode 100644 index 000000000..141474151 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/BinaryCodec.java @@ -0,0 +1,297 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import org.mozilla.apache.commons.codec.BinaryDecoder; +import org.mozilla.apache.commons.codec.BinaryEncoder; +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; + +/** + * Converts between byte arrays and strings of "0"s and "1"s. + * + * TODO: may want to add more bit vector functions like and/or/xor/nand + * TODO: also might be good to generate boolean[] from byte[] et cetera. + * + * @author Apache Software Foundation + * @since 1.3 + * @version $Id: BinaryCodec.java 1080701 2011-03-11 17:52:27Z ggregory $ + */ +public class BinaryCodec implements BinaryDecoder, BinaryEncoder { + /* + * tried to avoid using ArrayUtils to minimize dependencies while using these empty arrays - dep is just not worth + * it. + */ + /** Empty char array. */ + private static final char[] EMPTY_CHAR_ARRAY = new char[0]; + + /** Empty byte array. */ + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + /** Mask for bit 0 of a byte. */ + private static final int BIT_0 = 1; + + /** Mask for bit 1 of a byte. */ + private static final int BIT_1 = 0x02; + + /** Mask for bit 2 of a byte. */ + private static final int BIT_2 = 0x04; + + /** Mask for bit 3 of a byte. */ + private static final int BIT_3 = 0x08; + + /** Mask for bit 4 of a byte. */ + private static final int BIT_4 = 0x10; + + /** Mask for bit 5 of a byte. */ + private static final int BIT_5 = 0x20; + + /** Mask for bit 6 of a byte. */ + private static final int BIT_6 = 0x40; + + /** Mask for bit 7 of a byte. */ + private static final int BIT_7 = 0x80; + + private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7}; + + /** + * Converts an array of raw binary data into an array of ASCII 0 and 1 characters. + * + * @param raw + * the raw binary data to convert + * @return 0 and 1 ASCII character bytes one for each bit of the argument + * @see org.mozilla.apache.commons.codec.BinaryEncoder#encode(byte[]) + */ + public byte[] encode(byte[] raw) { + return toAsciiBytes(raw); + } + + /** + * Converts an array of raw binary data into an array of ASCII 0 and 1 chars. + * + * @param raw + * the raw binary data to convert + * @return 0 and 1 ASCII character chars one for each bit of the argument + * @throws EncoderException + * if the argument is not a byte[] + * @see org.mozilla.apache.commons.codec.Encoder#encode(Object) + */ + public Object encode(Object raw) throws EncoderException { + if (!(raw instanceof byte[])) { + throw new EncoderException("argument not a byte array"); + } + return toAsciiChars((byte[]) raw); + } + + /** + * Decodes a byte array where each byte represents an ASCII '0' or '1'. + * + * @param ascii + * each byte represents an ASCII '0' or '1' + * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument + * @throws DecoderException + * if argument is not a byte[], char[] or String + * @see org.mozilla.apache.commons.codec.Decoder#decode(Object) + */ + public Object decode(Object ascii) throws DecoderException { + if (ascii == null) { + return EMPTY_BYTE_ARRAY; + } + if (ascii instanceof byte[]) { + return fromAscii((byte[]) ascii); + } + if (ascii instanceof char[]) { + return fromAscii((char[]) ascii); + } + if (ascii instanceof String) { + return fromAscii(((String) ascii).toCharArray()); + } + throw new DecoderException("argument not a byte array"); + } + + /** + * Decodes a byte array where each byte represents an ASCII '0' or '1'. + * + * @param ascii + * each byte represents an ASCII '0' or '1' + * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument + * @see org.mozilla.apache.commons.codec.Decoder#decode(Object) + */ + public byte[] decode(byte[] ascii) { + return fromAscii(ascii); + } + + /** + * Decodes a String where each char of the String represents an ASCII '0' or '1'. + * + * @param ascii + * String of '0' and '1' characters + * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument + * @see org.mozilla.apache.commons.codec.Decoder#decode(Object) + */ + public byte[] toByteArray(String ascii) { + if (ascii == null) { + return EMPTY_BYTE_ARRAY; + } + return fromAscii(ascii.toCharArray()); + } + + // ------------------------------------------------------------------------ + // + // static codec operations + // + // ------------------------------------------------------------------------ + /** + * Decodes a char array where each char represents an ASCII '0' or '1'. + * + * @param ascii + * each char represents an ASCII '0' or '1' + * @return the raw encoded binary where each bit corresponds to a char in the char array argument + */ + public static byte[] fromAscii(char[] ascii) { + if (ascii == null || ascii.length == 0) { + return EMPTY_BYTE_ARRAY; + } + // get length/8 times bytes with 3 bit shifts to the right of the length + byte[] l_raw = new byte[ascii.length >> 3]; + /* + * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the + * loop. + */ + for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) { + for (int bits = 0; bits < BITS.length; ++bits) { + if (ascii[jj - bits] == '1') { + l_raw[ii] |= BITS[bits]; + } + } + } + return l_raw; + } + + /** + * Decodes a byte array where each byte represents an ASCII '0' or '1'. + * + * @param ascii + * each byte represents an ASCII '0' or '1' + * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument + */ + public static byte[] fromAscii(byte[] ascii) { + if (isEmpty(ascii)) { + return EMPTY_BYTE_ARRAY; + } + // get length/8 times bytes with 3 bit shifts to the right of the length + byte[] l_raw = new byte[ascii.length >> 3]; + /* + * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the + * loop. + */ + for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) { + for (int bits = 0; bits < BITS.length; ++bits) { + if (ascii[jj - bits] == '1') { + l_raw[ii] |= BITS[bits]; + } + } + } + return l_raw; + } + + /** + * Returns true if the given array is null or empty (size 0.) + * + * @param array + * the source array + * @return true if the given array is null or empty (size 0.) + */ + private static boolean isEmpty(byte[] array) { + return array == null || array.length == 0; + } + + /** + * Converts an array of raw binary data into an array of ASCII 0 and 1 character bytes - each byte is a truncated + * char. + * + * @param raw + * the raw binary data to convert + * @return an array of 0 and 1 character bytes for each bit of the argument + * @see org.mozilla.apache.commons.codec.BinaryEncoder#encode(byte[]) + */ + public static byte[] toAsciiBytes(byte[] raw) { + if (isEmpty(raw)) { + return EMPTY_BYTE_ARRAY; + } + // get 8 times the bytes with 3 bit shifts to the left of the length + byte[] l_ascii = new byte[raw.length << 3]; + /* + * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the + * loop. + */ + for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) { + for (int bits = 0; bits < BITS.length; ++bits) { + if ((raw[ii] & BITS[bits]) == 0) { + l_ascii[jj - bits] = '0'; + } else { + l_ascii[jj - bits] = '1'; + } + } + } + return l_ascii; + } + + /** + * Converts an array of raw binary data into an array of ASCII 0 and 1 characters. + * + * @param raw + * the raw binary data to convert + * @return an array of 0 and 1 characters for each bit of the argument + * @see org.mozilla.apache.commons.codec.BinaryEncoder#encode(byte[]) + */ + public static char[] toAsciiChars(byte[] raw) { + if (isEmpty(raw)) { + return EMPTY_CHAR_ARRAY; + } + // get 8 times the bytes with 3 bit shifts to the left of the length + char[] l_ascii = new char[raw.length << 3]; + /* + * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the + * loop. + */ + for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) { + for (int bits = 0; bits < BITS.length; ++bits) { + if ((raw[ii] & BITS[bits]) == 0) { + l_ascii[jj - bits] = '0'; + } else { + l_ascii[jj - bits] = '1'; + } + } + } + return l_ascii; + } + + /** + * Converts an array of raw binary data into a String of ASCII 0 and 1 characters. + * + * @param raw + * the raw binary data to convert + * @return a String of 0 and 1 characters representing the binary data + * @see org.mozilla.apache.commons.codec.BinaryEncoder#encode(byte[]) + */ + public static String toAsciiString(byte[] raw) { + return new String(toAsciiChars(raw)); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Hex.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Hex.java new file mode 100644 index 000000000..a2e34fe34 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Hex.java @@ -0,0 +1,302 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.UnsupportedEncodingException; + +import org.mozilla.apache.commons.codec.BinaryDecoder; +import org.mozilla.apache.commons.codec.BinaryEncoder; +import org.mozilla.apache.commons.codec.CharEncoding; +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; + +/** + * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in + * {@link #DEFAULT_CHARSET_NAME} + * + * @since 1.1 + * @author Apache Software Foundation + * @version $Id: Hex.java 1080701 2011-03-11 17:52:27Z ggregory $ + */ +public class Hex implements BinaryEncoder, BinaryDecoder { + + /** + * Default charset name is {@link CharEncoding#UTF_8} + * + * @since 1.4 + */ + public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; + + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The + * returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param data + * An array of characters containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied char array. + * @throws DecoderException + * Thrown if an odd number or illegal of characters is supplied + */ + public static byte[] decodeHex(char[] data) throws DecoderException { + + int len = data.length; + + if ((len & 0x01) != 0) { + throw new DecoderException("Odd number of characters."); + } + + byte[] out = new byte[len >> 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; j < len; i++) { + int f = toDigit(data[j], j) << 4; + j++; + f = f | toDigit(data[j], j); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @return A char[] containing hexadecimal characters + */ + public static char[] encodeHex(byte[] data) { + return encodeHex(data, true); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @param toLowerCase + * true converts to lowercase, false to uppercase + * @return A char[] containing hexadecimal characters + * @since 1.4 + */ + public static char[] encodeHex(byte[] data, boolean toLowerCase) { + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @param toDigits + * the output alphabet + * @return A char[] containing hexadecimal characters + * @since 1.4 + */ + protected static char[] encodeHex(byte[] data, char[] toDigits) { + int l = data.length; + char[] out = new char[l << 1]; + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + return out; + } + + /** + * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned + * String will be double the length of the passed array, as it takes two characters to represent any given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @return A String containing hexadecimal characters + * @since 1.4 + */ + public static String encodeHexString(byte[] data) { + return new String(encodeHex(data)); + } + + /** + * Converts a hexadecimal character to an integer. + * + * @param ch + * A character to convert to an integer digit + * @param index + * The index of the character in the source + * @return An integer + * @throws DecoderException + * Thrown if ch is an illegal hex character + */ + protected static int toDigit(char ch, int index) throws DecoderException { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); + } + return digit; + } + + private final String charsetName; + + /** + * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET_NAME} + */ + public Hex() { + // use default encoding + this.charsetName = DEFAULT_CHARSET_NAME; + } + + /** + * Creates a new codec with the given charset name. + * + * @param csName + * the charset name. + * @since 1.4 + */ + public Hex(String csName) { + this.charsetName = csName; + } + + /** + * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. + * The returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param array + * An array of character bytes containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied byte array (representing characters). + * @throws DecoderException + * Thrown if an odd number of characters is supplied to this function + * @see #decodeHex(char[]) + */ + public byte[] decode(byte[] array) throws DecoderException { + try { + return decodeHex(new String(array, getCharsetName()).toCharArray()); + } catch (UnsupportedEncodingException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those + * same values. The returned array will be half the length of the passed String or array, as it takes two characters + * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param object + * A String or, an array of character bytes containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied byte array (representing characters). + * @throws DecoderException + * Thrown if an odd number of characters is supplied to this function or the object is not a String or + * char[] + * @see #decodeHex(char[]) + */ + public Object decode(Object object) throws DecoderException { + try { + char[] charArray = object instanceof String ? ((String) object).toCharArray() : (char[]) object; + return decodeHex(charArray); + } catch (ClassCastException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each + * byte in order. The returned array will be double the length of the passed array, as it takes two characters to + * represent any given byte. + *

    + * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by + * {@link #getCharsetName()}. + *

    + * + * @param array + * a byte[] to convert to Hex characters + * @return A byte[] containing the bytes of the hexadecimal characters + * @throws IllegalStateException + * if the charsetName is invalid. This API throws {@link IllegalStateException} instead of + * {@link UnsupportedEncodingException} for backward compatibility. + * @see #encodeHex(byte[]) + */ + public byte[] encode(byte[] array) { + return StringUtils.getBytesUnchecked(encodeHexString(array), getCharsetName()); + } + + /** + * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each + * byte in order. The returned array will be double the length of the passed String or array, as it takes two + * characters to represent any given byte. + *

    + * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by + * {@link #getCharsetName()}. + *

    + * + * @param object + * a String, or byte[] to convert to Hex characters + * @return A char[] containing hexadecimal characters + * @throws EncoderException + * Thrown if the given object is not a String or byte[] + * @see #encodeHex(byte[]) + */ + public Object encode(Object object) throws EncoderException { + try { + byte[] byteArray = object instanceof String ? ((String) object).getBytes(getCharsetName()) : (byte[]) object; + return encodeHex(byteArray); + } catch (ClassCastException e) { + throw new EncoderException(e.getMessage(), e); + } catch (UnsupportedEncodingException e) { + throw new EncoderException(e.getMessage(), e); + } + } + + /** + * Gets the charset name. + * + * @return the charset name. + * @since 1.4 + */ + public String getCharsetName() { + return this.charsetName; + } + + /** + * Returns a string representation of the object, which includes the charset name. + * + * @return a string representation of the object. + */ + public String toString() { + return super.toString() + "[charsetName=" + this.charsetName + "]"; + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/StringUtils.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/StringUtils.java new file mode 100644 index 000000000..7bf960124 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/StringUtils.java @@ -0,0 +1,287 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.binary; + +import java.io.UnsupportedEncodingException; + +import org.mozilla.apache.commons.codec.CharEncoding; + +/** + * Converts String to and from bytes using the encodings required by the Java specification. These encodings are specified in Standard charsets + * + * @see CharEncoding + * @see Standard charsets + * @author Gary Gregory + * @version $Id: StringUtils.java 950460 2010-06-02 09:43:02Z sebb $ + * @since 1.4 + */ +public class StringUtils { + + /** + * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new + * byte array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesIso8859_1(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.ISO_8859_1); + } + + /** + * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUsAscii(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.US_ASCII); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Be(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16BE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Le(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16LE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf8(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_8); + } + + /** + * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte + * array. + *

    + * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

    + * + * @param string + * the String to encode, may be null + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#getBytes(String) + */ + public static byte[] getBytesUnchecked(String string, String charsetName) { + if (string == null) { + return null; + } + try { + return string.getBytes(charsetName); + } catch (UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + private static IllegalStateException newIllegalStateException(String charsetName, UnsupportedEncodingException e) { + return new IllegalStateException(charsetName + ": " + e); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the given charset. + *

    + * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

    + * + * @param bytes + * The bytes to be decoded into characters, may be null + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return A new String decoded from the specified array of bytes using the given charset, + * or null if the input byte arrray was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#String(byte[], String) + */ + public static String newString(byte[] bytes, String charsetName) { + if (bytes == null) { + return null; + } + try { + return new String(bytes, charsetName); + } catch (UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Constructs a new String by decoding the specified array of bytes using the ISO-8859-1 charset. + * + * @param bytes + * The bytes to be decoded into characters, may be null + * @return A new String decoded from the specified array of bytes using the ISO-8859-1 charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringIso8859_1(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.ISO_8859_1); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the US-ASCII charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the US-ASCII charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUsAscii(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.US_ASCII); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16 charset + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16BE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16BE charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16Be(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16BE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16LE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16LE charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16Le(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16LE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-8 charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf8(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_8); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/package.html b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/package.html new file mode 100644 index 000000000..13345ece4 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/package.html @@ -0,0 +1,21 @@ + + + + Base64, Base32, Binary, and Hexadecimal String encoding and decoding. + + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/DigestUtils.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/DigestUtils.java new file mode 100644 index 000000000..2421bb0fe --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/DigestUtils.java @@ -0,0 +1,583 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.digest; + +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.mozilla.apache.commons.codec.binary.Hex; +import org.mozilla.apache.commons.codec.binary.StringUtils; + +/** + * Operations to simplify common {@link java.security.MessageDigest} tasks. This class is thread safe. + * + * @author Apache Software Foundation + * @version $Id: DigestUtils.java 1064793 2011-01-28 17:42:55Z ggregory $ + */ +public class DigestUtils { + + private static final int STREAM_BUFFER_LENGTH = 1024; + + /** + * Read through an InputStream and returns the digest for the data + * + * @param digest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return MD5 digest + * @throws IOException + * On error reading from the stream + */ + private static byte[] digest(MessageDigest digest, InputStream data) throws IOException { + byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; + int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); + + while (read > -1) { + digest.update(buffer, 0, read); + read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); + } + + return digest.digest(); + } + + /** + * Calls {@link StringUtils#getBytesUtf8(String)} + * + * @param data + * the String to encode + * @return encoded bytes + */ + private static byte[] getBytesUtf8(String data) { + return StringUtils.getBytesUtf8(data); + } + + /** + * Returns a MessageDigest for the given algorithm. + * + * @param algorithm + * the name of the algorithm requested. See Appendix A in the Java + * Cryptography Architecture API Specification & Reference for information about standard algorithm + * names. + * @return An MD5 digest instance. + * @see MessageDigest#getInstance(String) + * @throws RuntimeException + * when a {@link java.security.NoSuchAlgorithmException} is caught. + */ + static MessageDigest getDigest(String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e.getMessage()); + } + } + + /** + * Returns an MD5 MessageDigest. + * + * @return An MD5 digest instance. + * @throws RuntimeException + * when a {@link java.security.NoSuchAlgorithmException} is caught. + */ + private static MessageDigest getMd5Digest() { + return getDigest("MD5"); + } + + /** + * Returns an SHA-256 digest. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @return An SHA-256 digest instance. + * @throws RuntimeException + * when a {@link java.security.NoSuchAlgorithmException} is caught. + */ + private static MessageDigest getSha256Digest() { + return getDigest("SHA-256"); + } + + /** + * Returns an SHA-384 digest. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @return An SHA-384 digest instance. + * @throws RuntimeException + * when a {@link java.security.NoSuchAlgorithmException} is caught. + */ + private static MessageDigest getSha384Digest() { + return getDigest("SHA-384"); + } + + /** + * Returns an SHA-512 digest. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @return An SHA-512 digest instance. + * @throws RuntimeException + * when a {@link java.security.NoSuchAlgorithmException} is caught. + */ + private static MessageDigest getSha512Digest() { + return getDigest("SHA-512"); + } + + /** + * Returns an SHA-1 digest. + * + * @return An SHA-1 digest instance. + * @throws RuntimeException + * when a {@link java.security.NoSuchAlgorithmException} is caught. + */ + private static MessageDigest getShaDigest() { + return getDigest("SHA"); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest + * @return MD5 digest + */ + public static byte[] md5(byte[] data) { + return getMd5Digest().digest(data); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest + * @return MD5 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] md5(InputStream data) throws IOException { + return digest(getMd5Digest(), data); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest + * @return MD5 digest + */ + public static byte[] md5(String data) { + return md5(getBytesUtf8(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + */ + public static String md5Hex(byte[] data) { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String md5Hex(InputStream data) throws IOException { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + */ + public static String md5Hex(String data) { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + */ + public static byte[] sha(byte[] data) { + return getShaDigest().digest(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha(InputStream data) throws IOException { + return digest(getShaDigest(), data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + */ + public static byte[] sha(String data) { + return sha(getBytesUtf8(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-256 digest + * @since 1.4 + */ + public static byte[] sha256(byte[] data) { + return getSha256Digest().digest(data); + } + + /** + * Calculates the SHA-256 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-256 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha256(InputStream data) throws IOException { + return digest(getSha256Digest(), data); + } + + /** + * Calculates the SHA-256 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-256 digest + * @since 1.4 + */ + public static byte[] sha256(String data) { + return sha256(getBytesUtf8(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @since 1.4 + */ + public static String sha256Hex(byte[] data) { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha256Hex(InputStream data) throws IOException { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @since 1.4 + */ + public static String sha256Hex(String data) { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-384 digest + * @since 1.4 + */ + public static byte[] sha384(byte[] data) { + return getSha384Digest().digest(data); + } + + /** + * Calculates the SHA-384 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-384 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha384(InputStream data) throws IOException { + return digest(getSha384Digest(), data); + } + + /** + * Calculates the SHA-384 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-384 digest + * @since 1.4 + */ + public static byte[] sha384(String data) { + return sha384(getBytesUtf8(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @since 1.4 + */ + public static String sha384Hex(byte[] data) { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha384Hex(InputStream data) throws IOException { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @since 1.4 + */ + public static String sha384Hex(String data) { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-512 digest + * @since 1.4 + */ + public static byte[] sha512(byte[] data) { + return getSha512Digest().digest(data); + } + + /** + * Calculates the SHA-512 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-512 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha512(InputStream data) throws IOException { + return digest(getSha512Digest(), data); + } + + /** + * Calculates the SHA-512 digest and returns the value as a byte[]. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-512 digest + * @since 1.4 + */ + public static byte[] sha512(String data) { + return sha512(getBytesUtf8(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @since 1.4 + */ + public static String sha512Hex(byte[] data) { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha512Hex(InputStream data) throws IOException { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + *

    + * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

    + * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @since 1.4 + */ + public static String sha512Hex(String data) { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + */ + public static String shaHex(byte[] data) { + return Hex.encodeHexString(sha(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String shaHex(InputStream data) throws IOException { + return Hex.encodeHexString(sha(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + */ + public static String shaHex(String data) { + return Hex.encodeHexString(sha(data)); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/package.html b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/package.html new file mode 100644 index 000000000..1da976276 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/digest/package.html @@ -0,0 +1,21 @@ + + + + Simplifies common {@link java.security.MessageDigest} tasks. + + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/AbstractCaverphone.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/AbstractCaverphone.java new file mode 100644 index 000000000..fbcc75943 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/AbstractCaverphone.java @@ -0,0 +1,78 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + * Encodes a string into a Caverphone value. + * + * This is an algorithm created by the Caversham Project at the University of Otago. It implements the Caverphone 2.0 + * algorithm: + * + * @author Apache Software Foundation + * @version $Id: Caverphone.java 1075947 2011-03-01 17:56:14Z ggregory $ + * @see Wikipedia - Caverphone + * @since 1.5 + */ +public abstract class AbstractCaverphone implements StringEncoder { + + /** + * Creates an instance of the Caverphone encoder + */ + public AbstractCaverphone() { + super(); + } + + /** + * Encodes an Object using the caverphone algorithm. This method is provided in order to satisfy the requirements of + * the Encoder interface, and will throw an EncoderException if the supplied object is not of type java.lang.String. + * + * @param source + * Object to encode + * @return An object (or type java.lang.String) containing the caverphone code which corresponds to the String + * supplied. + * @throws EncoderException + * if the parameter supplied is not of type java.lang.String + */ + public Object encode(Object source) throws EncoderException { + if (!(source instanceof String)) { + throw new EncoderException("Parameter supplied to Caverphone encode is not of type java.lang.String"); + } + return this.encode((String) source); + } + + /** + * Tests if the encodings of two strings are equal. + * + * This method might be promoted to a new AbstractStringEncoder superclass. + * + * @param str1 + * First of two strings to compare + * @param str2 + * Second of two strings to compare + * @return true if the encodings of these strings are identical, false otherwise. + * @throws EncoderException + */ + public boolean isEncodeEqual(String str1, String str2) throws EncoderException { + return this.encode(str1).equals(this.encode(str2)); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone.java new file mode 100644 index 000000000..062fa4135 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone.java @@ -0,0 +1,104 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + * Encodes a string into a Caverphone 2.0 value. Delegate to a {@link Caverphone2} instance. + * + * This is an algorithm created by the Caversham Project at the University of Otago. It implements the Caverphone 2.0 + * algorithm: + * + * @author Apache Software Foundation + * @version $Id: Caverphone.java 1079535 2011-03-08 20:54:37Z ggregory $ + * @see Wikipedia - Caverphone + * @see Caverphone 2.0 specification + * @since 1.4 + * @deprecated 1.5 Replaced by {@link Caverphone2}, will be removed in 2.0. + */ +public class Caverphone implements StringEncoder { + + /** + * Delegate to a {@link Caverphone2} instance to avoid code duplication. + */ + final private Caverphone2 encoder = new Caverphone2(); + + /** + * Creates an instance of the Caverphone encoder + */ + public Caverphone() { + super(); + } + + /** + * Encodes the given String into a Caverphone value. + * + * @param source + * String the source string + * @return A caverphone code for the given String + */ + public String caverphone(String source) { + return this.encoder.encode(source); + } + + /** + * Encodes an Object using the caverphone algorithm. This method is provided in order to satisfy the requirements of + * the Encoder interface, and will throw an EncoderException if the supplied object is not of type java.lang.String. + * + * @param pObject + * Object to encode + * @return An object (or type java.lang.String) containing the caverphone code which corresponds to the String + * supplied. + * @throws EncoderException + * if the parameter supplied is not of type java.lang.String + */ + public Object encode(Object pObject) throws EncoderException { + if (!(pObject instanceof String)) { + throw new EncoderException("Parameter supplied to Caverphone encode is not of type java.lang.String"); + } + return this.caverphone((String) pObject); + } + + /** + * Encodes a String using the Caverphone algorithm. + * + * @param pString + * String object to encode + * @return The caverphone code corresponding to the String supplied + */ + public String encode(String pString) { + return this.caverphone(pString); + } + + /** + * Tests if the caverphones of two strings are identical. + * + * @param str1 + * First of two strings to compare + * @param str2 + * Second of two strings to compare + * @return true if the caverphones of these strings are identical, false otherwise. + */ + public boolean isCaverphoneEqual(String str1, String str2) { + return this.caverphone(str1).equals(this.caverphone(str2)); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone1.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone1.java new file mode 100644 index 000000000..e0d2e1de3 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone1.java @@ -0,0 +1,126 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +/** + * Encodes a string into a Caverphone 1.0 value. + * + * This is an algorithm created by the Caversham Project at the University of Otago. It implements the Caverphone 1.0 + * algorithm: + * + * @author Apache Software Foundation + * @version $Id: Caverphone.java 1075947 2011-03-01 17:56:14Z ggregory $ + * @see Wikipedia - Caverphone + * @see Caverphone 1.0 specification + * @since 1.5 + */ +public class Caverphone1 extends AbstractCaverphone { + + private static final String SIX_1 = "111111"; + + /** + * Encodes the given String into a Caverphone value. + * + * @param source + * String the source string + * @return A caverphone code for the given String + */ + public String encode(String source) { + String txt = source; + if (txt == null || txt.length() == 0) { + return SIX_1; + } + + // 1. Convert to lowercase + txt = txt.toLowerCase(java.util.Locale.ENGLISH); + + // 2. Remove anything not A-Z + txt = txt.replaceAll("[^a-z]", ""); + + // 3. Handle various start options + // 2 is a temporary placeholder to indicate a consonant which we are no longer interested in. + txt = txt.replaceAll("^cough", "cou2f"); + txt = txt.replaceAll("^rough", "rou2f"); + txt = txt.replaceAll("^tough", "tou2f"); + txt = txt.replaceAll("^enough", "enou2f"); + txt = txt.replaceAll("^gn", "2n"); + + // End + txt = txt.replaceAll("mb$", "m2"); + + // 4. Handle replacements + txt = txt.replaceAll("cq", "2q"); + txt = txt.replaceAll("ci", "si"); + txt = txt.replaceAll("ce", "se"); + txt = txt.replaceAll("cy", "sy"); + txt = txt.replaceAll("tch", "2ch"); + txt = txt.replaceAll("c", "k"); + txt = txt.replaceAll("q", "k"); + txt = txt.replaceAll("x", "k"); + txt = txt.replaceAll("v", "f"); + txt = txt.replaceAll("dg", "2g"); + txt = txt.replaceAll("tio", "sio"); + txt = txt.replaceAll("tia", "sia"); + txt = txt.replaceAll("d", "t"); + txt = txt.replaceAll("ph", "fh"); + txt = txt.replaceAll("b", "p"); + txt = txt.replaceAll("sh", "s2"); + txt = txt.replaceAll("z", "s"); + txt = txt.replaceAll("^[aeiou]", "A"); + // 3 is a temporary placeholder marking a vowel + txt = txt.replaceAll("[aeiou]", "3"); + txt = txt.replaceAll("3gh3", "3kh3"); + txt = txt.replaceAll("gh", "22"); + txt = txt.replaceAll("g", "k"); + txt = txt.replaceAll("s+", "S"); + txt = txt.replaceAll("t+", "T"); + txt = txt.replaceAll("p+", "P"); + txt = txt.replaceAll("k+", "K"); + txt = txt.replaceAll("f+", "F"); + txt = txt.replaceAll("m+", "M"); + txt = txt.replaceAll("n+", "N"); + txt = txt.replaceAll("w3", "W3"); + txt = txt.replaceAll("wy", "Wy"); // 1.0 only + txt = txt.replaceAll("wh3", "Wh3"); + txt = txt.replaceAll("why", "Why"); // 1.0 only + txt = txt.replaceAll("w", "2"); + txt = txt.replaceAll("^h", "A"); + txt = txt.replaceAll("h", "2"); + txt = txt.replaceAll("r3", "R3"); + txt = txt.replaceAll("ry", "Ry"); // 1.0 only + txt = txt.replaceAll("r", "2"); + txt = txt.replaceAll("l3", "L3"); + txt = txt.replaceAll("ly", "Ly"); // 1.0 only + txt = txt.replaceAll("l", "2"); + txt = txt.replaceAll("j", "y"); // 1.0 only + txt = txt.replaceAll("y3", "Y3"); // 1.0 only + txt = txt.replaceAll("y", "2"); // 1.0 only + + // 5. Handle removals + txt = txt.replaceAll("2", ""); + txt = txt.replaceAll("3", ""); + + // 6. put ten 1s on the end + txt = txt + SIX_1; + + // 7. take the first six characters as the code + return txt.substring(0, SIX_1.length()); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone2.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone2.java new file mode 100644 index 000000000..a05b560e7 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Caverphone2.java @@ -0,0 +1,129 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +/** + * Encodes a string into a Caverphone 2.0 value. + * + * This is an algorithm created by the Caversham Project at the University of Otago. It implements the Caverphone 2.0 + * algorithm: + * + * @author Apache Software Foundation + * @version $Id: Caverphone.java 1075947 2011-03-01 17:56:14Z ggregory $ + * @see Wikipedia - Caverphone + * @see Caverphone 2.0 specification + * @since 1.5 + */ +public class Caverphone2 extends AbstractCaverphone { + + private static final String TEN_1 = "1111111111"; + + /** + * Encodes the given String into a Caverphone 2.0 value. + * + * @param source + * String the source string + * @return A caverphone code for the given String + */ + public String encode(String source) { + String txt = source; + if (txt == null || txt.length() == 0) { + return TEN_1; + } + + // 1. Convert to lowercase + txt = txt.toLowerCase(java.util.Locale.ENGLISH); + + // 2. Remove anything not A-Z + txt = txt.replaceAll("[^a-z]", ""); + + // 2.5. Remove final e + txt = txt.replaceAll("e$", ""); // 2.0 only + + // 3. Handle various start options + txt = txt.replaceAll("^cough", "cou2f"); + txt = txt.replaceAll("^rough", "rou2f"); + txt = txt.replaceAll("^tough", "tou2f"); + txt = txt.replaceAll("^enough", "enou2f"); // 2.0 only + txt = txt.replaceAll("^trough", "trou2f"); // 2.0 only - note the spec says ^enough here again, c+p error I assume + txt = txt.replaceAll("^gn", "2n"); + + // End + txt = txt.replaceAll("mb$", "m2"); + + // 4. Handle replacements + txt = txt.replaceAll("cq", "2q"); + txt = txt.replaceAll("ci", "si"); + txt = txt.replaceAll("ce", "se"); + txt = txt.replaceAll("cy", "sy"); + txt = txt.replaceAll("tch", "2ch"); + txt = txt.replaceAll("c", "k"); + txt = txt.replaceAll("q", "k"); + txt = txt.replaceAll("x", "k"); + txt = txt.replaceAll("v", "f"); + txt = txt.replaceAll("dg", "2g"); + txt = txt.replaceAll("tio", "sio"); + txt = txt.replaceAll("tia", "sia"); + txt = txt.replaceAll("d", "t"); + txt = txt.replaceAll("ph", "fh"); + txt = txt.replaceAll("b", "p"); + txt = txt.replaceAll("sh", "s2"); + txt = txt.replaceAll("z", "s"); + txt = txt.replaceAll("^[aeiou]", "A"); + txt = txt.replaceAll("[aeiou]", "3"); + txt = txt.replaceAll("j", "y"); // 2.0 only + txt = txt.replaceAll("^y3", "Y3"); // 2.0 only + txt = txt.replaceAll("^y", "A"); // 2.0 only + txt = txt.replaceAll("y", "3"); // 2.0 only + txt = txt.replaceAll("3gh3", "3kh3"); + txt = txt.replaceAll("gh", "22"); + txt = txt.replaceAll("g", "k"); + txt = txt.replaceAll("s+", "S"); + txt = txt.replaceAll("t+", "T"); + txt = txt.replaceAll("p+", "P"); + txt = txt.replaceAll("k+", "K"); + txt = txt.replaceAll("f+", "F"); + txt = txt.replaceAll("m+", "M"); + txt = txt.replaceAll("n+", "N"); + txt = txt.replaceAll("w3", "W3"); + txt = txt.replaceAll("wh3", "Wh3"); + txt = txt.replaceAll("w$", "3"); // 2.0 only + txt = txt.replaceAll("w", "2"); + txt = txt.replaceAll("^h", "A"); + txt = txt.replaceAll("h", "2"); + txt = txt.replaceAll("r3", "R3"); + txt = txt.replaceAll("r$", "3"); // 2.0 only + txt = txt.replaceAll("r", "2"); + txt = txt.replaceAll("l3", "L3"); + txt = txt.replaceAll("l$", "3"); // 2.0 only + txt = txt.replaceAll("l", "2"); + + // 5. Handle removals + txt = txt.replaceAll("2", ""); + txt = txt.replaceAll("3$", "A"); // 2.0 only + txt = txt.replaceAll("3", ""); + + // 6. put ten 1s on the end + txt = txt + TEN_1; + + // 7. take the first ten characters as the code + return txt.substring(0, TEN_1.length()); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/ColognePhonetic.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/ColognePhonetic.java new file mode 100644 index 000000000..ae6285790 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/ColognePhonetic.java @@ -0,0 +1,417 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import java.util.Locale; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + *

    + * Encodes a string into a Cologne Phonetic value. + *

    + *

    + * Implements the “Kölner Phonetic” (Cologne Phonetic) + * algorithm issued by Hans Joachim Postel in 1969. + *

    + * + *

    + * The Kölner Phonetik is a phonetic algorithm which is optimized for the German language. It is related to the + * well-known soundex algorithm. + *

    + * + *

    Algorithm

    + * + *
      + * + *
    • + *

      Step 1:

      + * After preprocessing (convertion to upper case, transcription of germanic umlauts, removal of non alphabetical characters) the + * letters of the supplied text are replaced by their phonetic code according to the folowing table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      LetterContextCode
      A, E, I, J, O, U, Y0
      H-
      B1
      Pnot before H
      D, Tnot before C, S, Z2
      F, V, W3
      Pbefore H
      G, K, Q4
      Cat onset before A, H, K, L, O, Q, R, U, X
      before A, H, K, O, Q, U, X except after S, Z
      Xnot after C, K, Q48
      L5
      M, N6
      R7
      S, Z8
      Cafter S, Z
      at onset except before A, H, K, L, O, Q, R, U, X
      not before A, H, K, O, Q, U, X
      D, Tbefore C, S, Z
      Xafter C, K, Q
      + *

      + * (Source: Wikipedia (de): + * Kölner Phonetik – Buchstabencodes) + *

      + * + *

      Example:

      + * + * {@code "Müller-Lüdenscheidt" => "MULLERLUDENSCHEIDT" => "6005507500206880022"} + * + *
    • + * + *
    • + *

      Step 2:

      + * Collapse of all multiple consecutive code digits. + *

      Example:

      + * {@code "6005507500206880022" => "6050750206802"}
    • + * + *
    • + *

      Step 3:

      + * Removal of all codes “0” except at the beginning. This means that two or more identical consecutive digits can occur + * if they occur after removing the "0" digits. + * + *

      Example:

      + * {@code "6050750206802" => "65752682"}
    • + * + *
    + * + * @see Wikipedia (de): Kölner Phonetik (in German) + * @author Apache Software Foundation + * @since 1.5 + */ +public class ColognePhonetic implements StringEncoder { + + private abstract class CologneBuffer { + + protected final char[] data; + + protected int length = 0; + + public CologneBuffer(char[] data) { + this.data = data; + this.length = data.length; + } + + public CologneBuffer(int buffSize) { + this.data = new char[buffSize]; + this.length = 0; + } + + protected abstract char[] copyData(int start, final int length); + + public int length() { + return length; + } + + public String toString() { + return new String(copyData(0, length)); + } + } + + private class CologneOutputBuffer extends CologneBuffer { + + public CologneOutputBuffer(int buffSize) { + super(buffSize); + } + + public void addRight(char chr) { + data[length] = chr; + length++; + } + + protected char[] copyData(int start, final int length) { + char[] newData = new char[length]; + System.arraycopy(data, start, newData, 0, length); + return newData; + } + } + + private class CologneInputBuffer extends CologneBuffer { + + public CologneInputBuffer(char[] data) { + super(data); + } + + public void addLeft(char ch) { + length++; + data[getNextPos()] = ch; + } + + protected char[] copyData(int start, final int length) { + char[] newData = new char[length]; + System.arraycopy(data, data.length - this.length + start, newData, 0, length); + return newData; + } + + public char getNextChar() { + return data[getNextPos()]; + } + + protected int getNextPos() { + return data.length - length; + } + + public char removeNext() { + char ch = getNextChar(); + length--; + return ch; + } + } + + private static final char[][] PREPROCESS_MAP = new char[][]{{'\u00C4', 'A'}, // Ä + {'\u00DC', 'U'}, // Ü + {'\u00D6', 'O'}, // Ö + {'\u00DF', 'S'} // ß + }; + + /* + * Returns whether the array contains the key, or not. + */ + private static boolean arrayContains(char[] arr, char key) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] == key) { + return true; + } + } + return false; + } + + /** + *

    + * colognePhonetic() is the actual implementations of the Kölner Phonetik algorithm. + *

    + *

    + * In contrast to the initial description of the algorithm, this implementation does the encoding in one pass. + *

    + * + * @param text + * @return the corresponding encoding according to the Kölner Phonetik algorithm + */ + public String colognePhonetic(String text) { + if (text == null) { + return null; + } + + text = preprocess(text); + + CologneOutputBuffer output = new CologneOutputBuffer(text.length() * 2); + CologneInputBuffer input = new CologneInputBuffer(text.toCharArray()); + + char nextChar; + + char lastChar = '-'; + char lastCode = '/'; + char code; + char chr; + + int rightLength = input.length(); + + while (rightLength > 0) { + chr = input.removeNext(); + + if ((rightLength = input.length()) > 0) { + nextChar = input.getNextChar(); + } else { + nextChar = '-'; + } + + if (arrayContains(new char[]{'A', 'E', 'I', 'J', 'O', 'U', 'Y'}, chr)) { + code = '0'; + } else if (chr == 'H' || chr < 'A' || chr > 'Z') { + if (lastCode == '/') { + continue; + } + code = '-'; + } else if (chr == 'B' || (chr == 'P' && nextChar != 'H')) { + code = '1'; + } else if ((chr == 'D' || chr == 'T') && !arrayContains(new char[]{'S', 'C', 'Z'}, nextChar)) { + code = '2'; + } else if (arrayContains(new char[]{'W', 'F', 'P', 'V'}, chr)) { + code = '3'; + } else if (arrayContains(new char[]{'G', 'K', 'Q'}, chr)) { + code = '4'; + } else if (chr == 'X' && !arrayContains(new char[]{'C', 'K', 'Q'}, lastChar)) { + code = '4'; + input.addLeft('S'); + rightLength++; + } else if (chr == 'S' || chr == 'Z') { + code = '8'; + } else if (chr == 'C') { + if (lastCode == '/') { + if (arrayContains(new char[]{'A', 'H', 'K', 'L', 'O', 'Q', 'R', 'U', 'X'}, nextChar)) { + code = '4'; + } else { + code = '8'; + } + } else { + if (arrayContains(new char[]{'S', 'Z'}, lastChar) || + !arrayContains(new char[]{'A', 'H', 'O', 'U', 'K', 'Q', 'X'}, nextChar)) { + code = '8'; + } else { + code = '4'; + } + } + } else if (arrayContains(new char[]{'T', 'D', 'X'}, chr)) { + code = '8'; + } else if (chr == 'R') { + code = '7'; + } else if (chr == 'L') { + code = '5'; + } else if (chr == 'M' || chr == 'N') { + code = '6'; + } else { + code = chr; + } + + if (code != '-' && (lastCode != code && (code != '0' || lastCode == '/') || code < '0' || code > '8')) { + output.addRight(code); + } + + lastChar = chr; + lastCode = code; + } + return output.toString(); + } + + public Object encode(Object object) throws EncoderException { + if (!(object instanceof String)) { + throw new EncoderException("This method’s parameter was expected to be of the type " + + String.class.getName() + + ". But actually it was of the type " + + object.getClass().getName() + + "."); + } + return encode((String) object); + } + + public String encode(String text) { + return colognePhonetic(text); + } + + public boolean isEncodeEqual(String text1, String text2) { + return colognePhonetic(text1).equals(colognePhonetic(text2)); + } + + /* + * Converts the string to upper case and replaces germanic umlauts, and the “ß”. + */ + private String preprocess(String text) { + text = text.toUpperCase(Locale.GERMAN); + + char[] chrs = text.toCharArray(); + + for (int index = 0; index < chrs.length; index++) { + if (chrs[index] > 'Z') { + for (int replacement = 0; replacement < PREPROCESS_MAP.length; replacement++) { + if (chrs[index] == PREPROCESS_MAP[replacement][0]) { + chrs[index] = PREPROCESS_MAP[replacement][1]; + break; + } + } + } + } + return new String(chrs); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/DoubleMetaphone.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/DoubleMetaphone.java new file mode 100644 index 000000000..5069ade72 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/DoubleMetaphone.java @@ -0,0 +1,1106 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + * Encodes a string into a double metaphone value. + * This Implementation is based on the algorithm by Lawrence Philips. + * + * + * @author Apache Software Foundation + * @version $Id: DoubleMetaphone.java 1072742 2011-02-20 21:39:03Z ggregory $ + */ +public class DoubleMetaphone implements StringEncoder { + + /** + * "Vowels" to test for + */ + private static final String VOWELS = "AEIOUY"; + + /** + * Prefixes when present which are not pronounced + */ + private static final String[] SILENT_START = + { "GN", "KN", "PN", "WR", "PS" }; + private static final String[] L_R_N_M_B_H_F_V_W_SPACE = + { "L", "R", "N", "M", "B", "H", "F", "V", "W", " " }; + private static final String[] ES_EP_EB_EL_EY_IB_IL_IN_IE_EI_ER = + { "ES", "EP", "EB", "EL", "EY", "IB", "IL", "IN", "IE", "EI", "ER" }; + private static final String[] L_T_K_S_N_M_B_Z = + { "L", "T", "K", "S", "N", "M", "B", "Z" }; + + /** + * Maximum length of an encoding, default is 4 + */ + private int maxCodeLen = 4; + + /** + * Creates an instance of this DoubleMetaphone encoder + */ + public DoubleMetaphone() { + super(); + } + + /** + * Encode a value with Double Metaphone + * + * @param value String to encode + * @return an encoded string + */ + public String doubleMetaphone(String value) { + return doubleMetaphone(value, false); + } + + /** + * Encode a value with Double Metaphone, optionally using the alternate + * encoding. + * + * @param value String to encode + * @param alternate use alternate encode + * @return an encoded string + */ + public String doubleMetaphone(String value, boolean alternate) { + value = cleanInput(value); + if (value == null) { + return null; + } + + boolean slavoGermanic = isSlavoGermanic(value); + int index = isSilentStart(value) ? 1 : 0; + + DoubleMetaphoneResult result = new DoubleMetaphoneResult(this.getMaxCodeLen()); + + while (!result.isComplete() && index <= value.length() - 1) { + switch (value.charAt(index)) { + case 'A': + case 'E': + case 'I': + case 'O': + case 'U': + case 'Y': + index = handleAEIOUY(result, index); + break; + case 'B': + result.append('P'); + index = charAt(value, index + 1) == 'B' ? index + 2 : index + 1; + break; + case '\u00C7': + // A C with a Cedilla + result.append('S'); + index++; + break; + case 'C': + index = handleC(value, result, index); + break; + case 'D': + index = handleD(value, result, index); + break; + case 'F': + result.append('F'); + index = charAt(value, index + 1) == 'F' ? index + 2 : index + 1; + break; + case 'G': + index = handleG(value, result, index, slavoGermanic); + break; + case 'H': + index = handleH(value, result, index); + break; + case 'J': + index = handleJ(value, result, index, slavoGermanic); + break; + case 'K': + result.append('K'); + index = charAt(value, index + 1) == 'K' ? index + 2 : index + 1; + break; + case 'L': + index = handleL(value, result, index); + break; + case 'M': + result.append('M'); + index = conditionM0(value, index) ? index + 2 : index + 1; + break; + case 'N': + result.append('N'); + index = charAt(value, index + 1) == 'N' ? index + 2 : index + 1; + break; + case '\u00D1': + // N with a tilde (spanish ene) + result.append('N'); + index++; + break; + case 'P': + index = handleP(value, result, index); + break; + case 'Q': + result.append('K'); + index = charAt(value, index + 1) == 'Q' ? index + 2 : index + 1; + break; + case 'R': + index = handleR(value, result, index, slavoGermanic); + break; + case 'S': + index = handleS(value, result, index, slavoGermanic); + break; + case 'T': + index = handleT(value, result, index); + break; + case 'V': + result.append('F'); + index = charAt(value, index + 1) == 'V' ? index + 2 : index + 1; + break; + case 'W': + index = handleW(value, result, index); + break; + case 'X': + index = handleX(value, result, index); + break; + case 'Z': + index = handleZ(value, result, index, slavoGermanic); + break; + default: + index++; + break; + } + } + + return alternate ? result.getAlternate() : result.getPrimary(); + } + + /** + * Encode the value using DoubleMetaphone. It will only work if + * obj is a String (like Metaphone). + * + * @param obj Object to encode (should be of type String) + * @return An encoded Object (will be of type String) + * @throws EncoderException encode parameter is not of type String + */ + public Object encode(Object obj) throws EncoderException { + if (!(obj instanceof String)) { + throw new EncoderException("DoubleMetaphone encode parameter is not of type String"); + } + return doubleMetaphone((String) obj); + } + + /** + * Encode the value using DoubleMetaphone. + * + * @param value String to encode + * @return An encoded String + */ + public String encode(String value) { + return doubleMetaphone(value); + } + + /** + * Check if the Double Metaphone values of two String values + * are equal. + * + * @param value1 The left-hand side of the encoded {@link String#equals(Object)}. + * @param value2 The right-hand side of the encoded {@link String#equals(Object)}. + * @return true if the encoded Strings are equal; + * false otherwise. + * @see #isDoubleMetaphoneEqual(String,String,boolean) + */ + public boolean isDoubleMetaphoneEqual(String value1, String value2) { + return isDoubleMetaphoneEqual(value1, value2, false); + } + + /** + * Check if the Double Metaphone values of two String values + * are equal, optionally using the alternate value. + * + * @param value1 The left-hand side of the encoded {@link String#equals(Object)}. + * @param value2 The right-hand side of the encoded {@link String#equals(Object)}. + * @param alternate use the alternate value if true. + * @return true if the encoded Strings are equal; + * false otherwise. + */ + public boolean isDoubleMetaphoneEqual(String value1, + String value2, + boolean alternate) { + return doubleMetaphone(value1, alternate).equals(doubleMetaphone + (value2, alternate)); + } + + /** + * Returns the maxCodeLen. + * @return int + */ + public int getMaxCodeLen() { + return this.maxCodeLen; + } + + /** + * Sets the maxCodeLen. + * @param maxCodeLen The maxCodeLen to set + */ + public void setMaxCodeLen(int maxCodeLen) { + this.maxCodeLen = maxCodeLen; + } + + //-- BEGIN HANDLERS --// + + /** + * Handles 'A', 'E', 'I', 'O', 'U', and 'Y' cases + */ + private int handleAEIOUY(DoubleMetaphoneResult result, int + index) { + if (index == 0) { + result.append('A'); + } + return index + 1; + } + + /** + * Handles 'C' cases + */ + private int handleC(String value, + DoubleMetaphoneResult result, + int index) { + if (conditionC0(value, index)) { // very confusing, moved out + result.append('K'); + index += 2; + } else if (index == 0 && contains(value, index, 6, "CAESAR")) { + result.append('S'); + index += 2; + } else if (contains(value, index, 2, "CH")) { + index = handleCH(value, result, index); + } else if (contains(value, index, 2, "CZ") && + !contains(value, index - 2, 4, "WICZ")) { + //-- "Czerny" --// + result.append('S', 'X'); + index += 2; + } else if (contains(value, index + 1, 3, "CIA")) { + //-- "focaccia" --// + result.append('X'); + index += 3; + } else if (contains(value, index, 2, "CC") && + !(index == 1 && charAt(value, 0) == 'M')) { + //-- double "cc" but not "McClelland" --// + return handleCC(value, result, index); + } else if (contains(value, index, 2, "CK", "CG", "CQ")) { + result.append('K'); + index += 2; + } else if (contains(value, index, 2, "CI", "CE", "CY")) { + //-- Italian vs. English --// + if (contains(value, index, 3, "CIO", "CIE", "CIA")) { + result.append('S', 'X'); + } else { + result.append('S'); + } + index += 2; + } else { + result.append('K'); + if (contains(value, index + 1, 2, " C", " Q", " G")) { + //-- Mac Caffrey, Mac Gregor --// + index += 3; + } else if (contains(value, index + 1, 1, "C", "K", "Q") && + !contains(value, index + 1, 2, "CE", "CI")) { + index += 2; + } else { + index++; + } + } + + return index; + } + + /** + * Handles 'CC' cases + */ + private int handleCC(String value, + DoubleMetaphoneResult result, + int index) { + if (contains(value, index + 2, 1, "I", "E", "H") && + !contains(value, index + 2, 2, "HU")) { + //-- "bellocchio" but not "bacchus" --// + if ((index == 1 && charAt(value, index - 1) == 'A') || + contains(value, index - 1, 5, "UCCEE", "UCCES")) { + //-- "accident", "accede", "succeed" --// + result.append("KS"); + } else { + //-- "bacci", "bertucci", other Italian --// + result.append('X'); + } + index += 3; + } else { // Pierce's rule + result.append('K'); + index += 2; + } + + return index; + } + + /** + * Handles 'CH' cases + */ + private int handleCH(String value, + DoubleMetaphoneResult result, + int index) { + if (index > 0 && contains(value, index, 4, "CHAE")) { // Michael + result.append('K', 'X'); + return index + 2; + } else if (conditionCH0(value, index)) { + //-- Greek roots ("chemistry", "chorus", etc.) --// + result.append('K'); + return index + 2; + } else if (conditionCH1(value, index)) { + //-- Germanic, Greek, or otherwise 'ch' for 'kh' sound --// + result.append('K'); + return index + 2; + } else { + if (index > 0) { + if (contains(value, 0, 2, "MC")) { + result.append('K'); + } else { + result.append('X', 'K'); + } + } else { + result.append('X'); + } + return index + 2; + } + } + + /** + * Handles 'D' cases + */ + private int handleD(String value, + DoubleMetaphoneResult result, + int index) { + if (contains(value, index, 2, "DG")) { + //-- "Edge" --// + if (contains(value, index + 2, 1, "I", "E", "Y")) { + result.append('J'); + index += 3; + //-- "Edgar" --// + } else { + result.append("TK"); + index += 2; + } + } else if (contains(value, index, 2, "DT", "DD")) { + result.append('T'); + index += 2; + } else { + result.append('T'); + index++; + } + return index; + } + + /** + * Handles 'G' cases + */ + private int handleG(String value, + DoubleMetaphoneResult result, + int index, + boolean slavoGermanic) { + if (charAt(value, index + 1) == 'H') { + index = handleGH(value, result, index); + } else if (charAt(value, index + 1) == 'N') { + if (index == 1 && isVowel(charAt(value, 0)) && !slavoGermanic) { + result.append("KN", "N"); + } else if (!contains(value, index + 2, 2, "EY") && + charAt(value, index + 1) != 'Y' && !slavoGermanic) { + result.append("N", "KN"); + } else { + result.append("KN"); + } + index = index + 2; + } else if (contains(value, index + 1, 2, "LI") && !slavoGermanic) { + result.append("KL", "L"); + index += 2; + } else if (index == 0 && (charAt(value, index + 1) == 'Y' || contains(value, index + 1, 2, ES_EP_EB_EL_EY_IB_IL_IN_IE_EI_ER))) { + //-- -ges-, -gep-, -gel-, -gie- at beginning --// + result.append('K', 'J'); + index += 2; + } else if ((contains(value, index + 1, 2, "ER") || + charAt(value, index + 1) == 'Y') && + !contains(value, 0, 6, "DANGER", "RANGER", "MANGER") && + !contains(value, index - 1, 1, "E", "I") && + !contains(value, index - 1, 3, "RGY", "OGY")) { + //-- -ger-, -gy- --// + result.append('K', 'J'); + index += 2; + } else if (contains(value, index + 1, 1, "E", "I", "Y") || + contains(value, index - 1, 4, "AGGI", "OGGI")) { + //-- Italian "biaggi" --// + if ((contains(value, 0 ,4, "VAN ", "VON ") || contains(value, 0, 3, "SCH")) || contains(value, index + 1, 2, "ET")) { + //-- obvious germanic --// + result.append('K'); + } else if (contains(value, index + 1, 3, "IER")) { + result.append('J'); + } else { + result.append('J', 'K'); + } + index += 2; + } else if (charAt(value, index + 1) == 'G') { + index += 2; + result.append('K'); + } else { + index++; + result.append('K'); + } + return index; + } + + /** + * Handles 'GH' cases + */ + private int handleGH(String value, + DoubleMetaphoneResult result, + int index) { + if (index > 0 && !isVowel(charAt(value, index - 1))) { + result.append('K'); + index += 2; + } else if (index == 0) { + if (charAt(value, index + 2) == 'I') { + result.append('J'); + } else { + result.append('K'); + } + index += 2; + } else if ((index > 1 && contains(value, index - 2, 1, "B", "H", "D")) || + (index > 2 && contains(value, index - 3, 1, "B", "H", "D")) || + (index > 3 && contains(value, index - 4, 1, "B", "H"))) { + //-- Parker's rule (with some further refinements) - "hugh" + index += 2; + } else { + if (index > 2 && charAt(value, index - 1) == 'U' && + contains(value, index - 3, 1, "C", "G", "L", "R", "T")) { + //-- "laugh", "McLaughlin", "cough", "gough", "rough", "tough" + result.append('F'); + } else if (index > 0 && charAt(value, index - 1) != 'I') { + result.append('K'); + } + index += 2; + } + return index; + } + + /** + * Handles 'H' cases + */ + private int handleH(String value, + DoubleMetaphoneResult result, + int index) { + //-- only keep if first & before vowel or between 2 vowels --// + if ((index == 0 || isVowel(charAt(value, index - 1))) && + isVowel(charAt(value, index + 1))) { + result.append('H'); + index += 2; + //-- also takes car of "HH" --// + } else { + index++; + } + return index; + } + + /** + * Handles 'J' cases + */ + private int handleJ(String value, DoubleMetaphoneResult result, int index, + boolean slavoGermanic) { + if (contains(value, index, 4, "JOSE") || contains(value, 0, 4, "SAN ")) { + //-- obvious Spanish, "Jose", "San Jacinto" --// + if ((index == 0 && (charAt(value, index + 4) == ' ') || + value.length() == 4) || contains(value, 0, 4, "SAN ")) { + result.append('H'); + } else { + result.append('J', 'H'); + } + index++; + } else { + if (index == 0 && !contains(value, index, 4, "JOSE")) { + result.append('J', 'A'); + } else if (isVowel(charAt(value, index - 1)) && !slavoGermanic && + (charAt(value, index + 1) == 'A' || charAt(value, index + 1) == 'O')) { + result.append('J', 'H'); + } else if (index == value.length() - 1) { + result.append('J', ' '); + } else if (!contains(value, index + 1, 1, L_T_K_S_N_M_B_Z) && !contains(value, index - 1, 1, "S", "K", "L")) { + result.append('J'); + } + + if (charAt(value, index + 1) == 'J') { + index += 2; + } else { + index++; + } + } + return index; + } + + /** + * Handles 'L' cases + */ + private int handleL(String value, + DoubleMetaphoneResult result, + int index) { + if (charAt(value, index + 1) == 'L') { + if (conditionL0(value, index)) { + result.appendPrimary('L'); + } else { + result.append('L'); + } + index += 2; + } else { + index++; + result.append('L'); + } + return index; + } + + /** + * Handles 'P' cases + */ + private int handleP(String value, + DoubleMetaphoneResult result, + int index) { + if (charAt(value, index + 1) == 'H') { + result.append('F'); + index += 2; + } else { + result.append('P'); + index = contains(value, index + 1, 1, "P", "B") ? index + 2 : index + 1; + } + return index; + } + + /** + * Handles 'R' cases + */ + private int handleR(String value, + DoubleMetaphoneResult result, + int index, + boolean slavoGermanic) { + if (index == value.length() - 1 && !slavoGermanic && + contains(value, index - 2, 2, "IE") && + !contains(value, index - 4, 2, "ME", "MA")) { + result.appendAlternate('R'); + } else { + result.append('R'); + } + return charAt(value, index + 1) == 'R' ? index + 2 : index + 1; + } + + /** + * Handles 'S' cases + */ + private int handleS(String value, + DoubleMetaphoneResult result, + int index, + boolean slavoGermanic) { + if (contains(value, index - 1, 3, "ISL", "YSL")) { + //-- special cases "island", "isle", "carlisle", "carlysle" --// + index++; + } else if (index == 0 && contains(value, index, 5, "SUGAR")) { + //-- special case "sugar-" --// + result.append('X', 'S'); + index++; + } else if (contains(value, index, 2, "SH")) { + if (contains(value, index + 1, 4, + "HEIM", "HOEK", "HOLM", "HOLZ")) { + //-- germanic --// + result.append('S'); + } else { + result.append('X'); + } + index += 2; + } else if (contains(value, index, 3, "SIO", "SIA") || contains(value, index, 4, "SIAN")) { + //-- Italian and Armenian --// + if (slavoGermanic) { + result.append('S'); + } else { + result.append('S', 'X'); + } + index += 3; + } else if ((index == 0 && contains(value, index + 1, 1, "M", "N", "L", "W")) || contains(value, index + 1, 1, "Z")) { + //-- german & anglicisations, e.g. "smith" match "schmidt" // + // "snider" match "schneider" --// + //-- also, -sz- in slavic language altho in hungarian it // + // is pronounced "s" --// + result.append('S', 'X'); + index = contains(value, index + 1, 1, "Z") ? index + 2 : index + 1; + } else if (contains(value, index, 2, "SC")) { + index = handleSC(value, result, index); + } else { + if (index == value.length() - 1 && contains(value, index - 2, + 2, "AI", "OI")){ + //-- french e.g. "resnais", "artois" --// + result.appendAlternate('S'); + } else { + result.append('S'); + } + index = contains(value, index + 1, 1, "S", "Z") ? index + 2 : index + 1; + } + return index; + } + + /** + * Handles 'SC' cases + */ + private int handleSC(String value, + DoubleMetaphoneResult result, + int index) { + if (charAt(value, index + 2) == 'H') { + //-- Schlesinger's rule --// + if (contains(value, index + 3, + 2, "OO", "ER", "EN", "UY", "ED", "EM")) { + //-- Dutch origin, e.g. "school", "schooner" --// + if (contains(value, index + 3, 2, "ER", "EN")) { + //-- "schermerhorn", "schenker" --// + result.append("X", "SK"); + } else { + result.append("SK"); + } + } else { + if (index == 0 && !isVowel(charAt(value, 3)) && charAt(value, 3) != 'W') { + result.append('X', 'S'); + } else { + result.append('X'); + } + } + } else if (contains(value, index + 2, 1, "I", "E", "Y")) { + result.append('S'); + } else { + result.append("SK"); + } + return index + 3; + } + + /** + * Handles 'T' cases + */ + private int handleT(String value, + DoubleMetaphoneResult result, + int index) { + if (contains(value, index, 4, "TION")) { + result.append('X'); + index += 3; + } else if (contains(value, index, 3, "TIA", "TCH")) { + result.append('X'); + index += 3; + } else if (contains(value, index, 2, "TH") || contains(value, index, + 3, "TTH")) { + if (contains(value, index + 2, 2, "OM", "AM") || + //-- special case "thomas", "thames" or germanic --// + contains(value, 0, 4, "VAN ", "VON ") || + contains(value, 0, 3, "SCH")) { + result.append('T'); + } else { + result.append('0', 'T'); + } + index += 2; + } else { + result.append('T'); + index = contains(value, index + 1, 1, "T", "D") ? index + 2 : index + 1; + } + return index; + } + + /** + * Handles 'W' cases + */ + private int handleW(String value, + DoubleMetaphoneResult result, + int index) { + if (contains(value, index, 2, "WR")) { + //-- can also be in middle of word --// + result.append('R'); + index += 2; + } else { + if (index == 0 && (isVowel(charAt(value, index + 1)) || + contains(value, index, 2, "WH"))) { + if (isVowel(charAt(value, index + 1))) { + //-- Wasserman should match Vasserman --// + result.append('A', 'F'); + } else { + //-- need Uomo to match Womo --// + result.append('A'); + } + index++; + } else if ((index == value.length() - 1 && isVowel(charAt(value, index - 1))) || + contains(value, index - 1, + 5, "EWSKI", "EWSKY", "OWSKI", "OWSKY") || + contains(value, 0, 3, "SCH")) { + //-- Arnow should match Arnoff --// + result.appendAlternate('F'); + index++; + } else if (contains(value, index, 4, "WICZ", "WITZ")) { + //-- Polish e.g. "filipowicz" --// + result.append("TS", "FX"); + index += 4; + } else { + index++; + } + } + return index; + } + + /** + * Handles 'X' cases + */ + private int handleX(String value, + DoubleMetaphoneResult result, + int index) { + if (index == 0) { + result.append('S'); + index++; + } else { + if (!((index == value.length() - 1) && + (contains(value, index - 3, 3, "IAU", "EAU") || + contains(value, index - 2, 2, "AU", "OU")))) { + //-- French e.g. breaux --// + result.append("KS"); + } + index = contains(value, index + 1, 1, "C", "X") ? index + 2 : index + 1; + } + return index; + } + + /** + * Handles 'Z' cases + */ + private int handleZ(String value, DoubleMetaphoneResult result, int index, + boolean slavoGermanic) { + if (charAt(value, index + 1) == 'H') { + //-- Chinese pinyin e.g. "zhao" or Angelina "Zhang" --// + result.append('J'); + index += 2; + } else { + if (contains(value, index + 1, 2, "ZO", "ZI", "ZA") || (slavoGermanic && (index > 0 && charAt(value, index - 1) != 'T'))) { + result.append("S", "TS"); + } else { + result.append('S'); + } + index = charAt(value, index + 1) == 'Z' ? index + 2 : index + 1; + } + return index; + } + + //-- BEGIN CONDITIONS --// + + /** + * Complex condition 0 for 'C' + */ + private boolean conditionC0(String value, int index) { + if (contains(value, index, 4, "CHIA")) { + return true; + } else if (index <= 1) { + return false; + } else if (isVowel(charAt(value, index - 2))) { + return false; + } else if (!contains(value, index - 1, 3, "ACH")) { + return false; + } else { + char c = charAt(value, index + 2); + return (c != 'I' && c != 'E') || + contains(value, index - 2, 6, "BACHER", "MACHER"); + } + } + + /** + * Complex condition 0 for 'CH' + */ + private boolean conditionCH0(String value, int index) { + if (index != 0) { + return false; + } else if (!contains(value, index + 1, 5, "HARAC", "HARIS") && + !contains(value, index + 1, 3, "HOR", "HYM", "HIA", "HEM")) { + return false; + } else if (contains(value, 0, 5, "CHORE")) { + return false; + } else { + return true; + } + } + + /** + * Complex condition 1 for 'CH' + */ + private boolean conditionCH1(String value, int index) { + return ((contains(value, 0, 4, "VAN ", "VON ") || contains(value, 0, + 3, "SCH")) || + contains(value, index - 2, 6, "ORCHES", "ARCHIT", "ORCHID") || + contains(value, index + 2, 1, "T", "S") || + ((contains(value, index - 1, 1, "A", "O", "U", "E") || index == 0) && + (contains(value, index + 2, 1, L_R_N_M_B_H_F_V_W_SPACE) || index + 1 == value.length() - 1))); + } + + /** + * Complex condition 0 for 'L' + */ + private boolean conditionL0(String value, int index) { + if (index == value.length() - 3 && + contains(value, index - 1, 4, "ILLO", "ILLA", "ALLE")) { + return true; + } else if ((contains(value, value.length() - 2, 2, "AS", "OS") || + contains(value, value.length() - 1, 1, "A", "O")) && + contains(value, index - 1, 4, "ALLE")) { + return true; + } else { + return false; + } + } + + /** + * Complex condition 0 for 'M' + */ + private boolean conditionM0(String value, int index) { + if (charAt(value, index + 1) == 'M') { + return true; + } + return contains(value, index - 1, 3, "UMB") && + ((index + 1) == value.length() - 1 || contains(value, + index + 2, 2, "ER")); + } + + //-- BEGIN HELPER FUNCTIONS --// + + /** + * Determines whether or not a value is of slavo-germanic orgin. A value is + * of slavo-germanic origin if it contians any of 'W', 'K', 'CZ', or 'WITZ'. + */ + private boolean isSlavoGermanic(String value) { + return value.indexOf('W') > -1 || value.indexOf('K') > -1 || + value.indexOf("CZ") > -1 || value.indexOf("WITZ") > -1; + } + + /** + * Determines whether or not a character is a vowel or not + */ + private boolean isVowel(char ch) { + return VOWELS.indexOf(ch) != -1; + } + + /** + * Determines whether or not the value starts with a silent letter. It will + * return true if the value starts with any of 'GN', 'KN', + * 'PN', 'WR' or 'PS'. + */ + private boolean isSilentStart(String value) { + boolean result = false; + for (int i = 0; i < SILENT_START.length; i++) { + if (value.startsWith(SILENT_START[i])) { + result = true; + break; + } + } + return result; + } + + /** + * Cleans the input + */ + private String cleanInput(String input) { + if (input == null) { + return null; + } + input = input.trim(); + if (input.length() == 0) { + return null; + } + return input.toUpperCase(java.util.Locale.ENGLISH); + } + + /** + * Gets the character at index index if available, otherwise + * it returns Character.MIN_VALUE so that there is some sort + * of a default + */ + protected char charAt(String value, int index) { + if (index < 0 || index >= value.length()) { + return Character.MIN_VALUE; + } + return value.charAt(index); + } + + /** + * Shortcut method with 1 criteria + */ + private static boolean contains(String value, int start, int length, + String criteria) { + return contains(value, start, length, + new String[] { criteria }); + } + + /** + * Shortcut method with 2 criteria + */ + private static boolean contains(String value, int start, int length, + String criteria1, String criteria2) { + return contains(value, start, length, + new String[] { criteria1, criteria2 }); + } + + /** + * Shortcut method with 3 criteria + */ + private static boolean contains(String value, int start, int length, + String criteria1, String criteria2, + String criteria3) { + return contains(value, start, length, + new String[] { criteria1, criteria2, criteria3 }); + } + + /** + * Shortcut method with 4 criteria + */ + private static boolean contains(String value, int start, int length, + String criteria1, String criteria2, + String criteria3, String criteria4) { + return contains(value, start, length, + new String[] { criteria1, criteria2, criteria3, + criteria4 }); + } + + /** + * Shortcut method with 5 criteria + */ + private static boolean contains(String value, int start, int length, + String criteria1, String criteria2, + String criteria3, String criteria4, + String criteria5) { + return contains(value, start, length, + new String[] { criteria1, criteria2, criteria3, + criteria4, criteria5 }); + } + + /** + * Shortcut method with 6 criteria + */ + private static boolean contains(String value, int start, int length, + String criteria1, String criteria2, + String criteria3, String criteria4, + String criteria5, String criteria6) { + return contains(value, start, length, + new String[] { criteria1, criteria2, criteria3, + criteria4, criteria5, criteria6 }); + } + + /** + * Determines whether value contains any of the criteria starting at index start and + * matching up to length length + */ + protected static boolean contains(String value, int start, int length, + String[] criteria) { + boolean result = false; + if (start >= 0 && start + length <= value.length()) { + String target = value.substring(start, start + length); + + for (int i = 0; i < criteria.length; i++) { + if (target.equals(criteria[i])) { + result = true; + break; + } + } + } + return result; + } + + //-- BEGIN INNER CLASSES --// + + /** + * Inner class for storing results, since there is the optional alternate + * encoding. + */ + public class DoubleMetaphoneResult { + + private StringBuffer primary = new StringBuffer(getMaxCodeLen()); + private StringBuffer alternate = new StringBuffer(getMaxCodeLen()); + private int maxLength; + + public DoubleMetaphoneResult(int maxLength) { + this.maxLength = maxLength; + } + + public void append(char value) { + appendPrimary(value); + appendAlternate(value); + } + + public void append(char primary, char alternate) { + appendPrimary(primary); + appendAlternate(alternate); + } + + public void appendPrimary(char value) { + if (this.primary.length() < this.maxLength) { + this.primary.append(value); + } + } + + public void appendAlternate(char value) { + if (this.alternate.length() < this.maxLength) { + this.alternate.append(value); + } + } + + public void append(String value) { + appendPrimary(value); + appendAlternate(value); + } + + public void append(String primary, String alternate) { + appendPrimary(primary); + appendAlternate(alternate); + } + + public void appendPrimary(String value) { + int addChars = this.maxLength - this.primary.length(); + if (value.length() <= addChars) { + this.primary.append(value); + } else { + this.primary.append(value.substring(0, addChars)); + } + } + + public void appendAlternate(String value) { + int addChars = this.maxLength - this.alternate.length(); + if (value.length() <= addChars) { + this.alternate.append(value); + } else { + this.alternate.append(value.substring(0, addChars)); + } + } + + public String getPrimary() { + return this.primary.toString(); + } + + public String getAlternate() { + return this.alternate.toString(); + } + + public boolean isComplete() { + return this.primary.length() >= this.maxLength && + this.alternate.length() >= this.maxLength; + } + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Metaphone.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Metaphone.java new file mode 100644 index 000000000..ec7cd3813 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Metaphone.java @@ -0,0 +1,408 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + * Encodes a string into a Metaphone value. + *

    + * Initial Java implementation by William B. Brogden. December, 1997. + * Permission given by wbrogden for code to be used anywhere. + *

    + *

    + * Hanging on the Metaphone by Lawrence Philips in Computer Language of Dec. 1990, p + * 39. + *

    + *

    + * Note, that this does not match the algorithm that ships with PHP, or the algorithm + * found in the Perl Text:Metaphone-1.96. + * They have had undocumented changes from the originally published algorithm. + * For more information, see CODEC-57. + *

    + * + * @author Apache Software Foundation + * @version $Id: Metaphone.java 1080867 2011-03-12 06:06:46Z ggregory $ + */ +public class Metaphone implements StringEncoder { + + /** + * Five values in the English language + */ + private static final String VOWELS = "AEIOU" ; + + /** + * Variable used in Metaphone algorithm + */ + private static final String FRONTV = "EIY" ; + + /** + * Variable used in Metaphone algorithm + */ + private static final String VARSON = "CSPTG" ; + + /** + * The max code length for metaphone is 4 + */ + private int maxCodeLen = 4 ; + + /** + * Creates an instance of the Metaphone encoder + */ + public Metaphone() { + super(); + } + + /** + * Find the metaphone value of a String. This is similar to the + * soundex algorithm, but better at finding similar sounding words. + * All input is converted to upper case. + * Limitations: Input format is expected to be a single ASCII word + * with only characters in the A - Z range, no punctuation or numbers. + * + * @param txt String to find the metaphone code for + * @return A metaphone code corresponding to the String supplied + */ + public String metaphone(String txt) { + boolean hard = false ; + if ((txt == null) || (txt.length() == 0)) { + return "" ; + } + // single character is itself + if (txt.length() == 1) { + return txt.toUpperCase(java.util.Locale.ENGLISH) ; + } + + char[] inwd = txt.toUpperCase(java.util.Locale.ENGLISH).toCharArray() ; + + StringBuffer local = new StringBuffer(40); // manipulate + StringBuffer code = new StringBuffer(10) ; // output + // handle initial 2 characters exceptions + switch(inwd[0]) { + case 'K' : + case 'G' : + case 'P' : /* looking for KN, etc*/ + if (inwd[1] == 'N') { + local.append(inwd, 1, inwd.length - 1); + } else { + local.append(inwd); + } + break; + case 'A': /* looking for AE */ + if (inwd[1] == 'E') { + local.append(inwd, 1, inwd.length - 1); + } else { + local.append(inwd); + } + break; + case 'W' : /* looking for WR or WH */ + if (inwd[1] == 'R') { // WR -> R + local.append(inwd, 1, inwd.length - 1); + break ; + } + if (inwd[1] == 'H') { + local.append(inwd, 1, inwd.length - 1); + local.setCharAt(0, 'W'); // WH -> W + } else { + local.append(inwd); + } + break; + case 'X' : /* initial X becomes S */ + inwd[0] = 'S'; + local.append(inwd); + break ; + default : + local.append(inwd); + } // now local has working string with initials fixed + + int wdsz = local.length(); + int n = 0 ; + + while ((code.length() < this.getMaxCodeLen()) && + (n < wdsz) ) { // max code size of 4 works well + char symb = local.charAt(n) ; + // remove duplicate letters except C + if ((symb != 'C') && (isPreviousChar( local, n, symb )) ) { + n++ ; + } else { // not dup + switch(symb) { + case 'A' : case 'E' : case 'I' : case 'O' : case 'U' : + if (n == 0) { + code.append(symb); + } + break ; // only use vowel if leading char + case 'B' : + if ( isPreviousChar(local, n, 'M') && + isLastChar(wdsz, n) ) { // B is silent if word ends in MB + break; + } + code.append(symb); + break; + case 'C' : // lots of C special cases + /* discard if SCI, SCE or SCY */ + if ( isPreviousChar(local, n, 'S') && + !isLastChar(wdsz, n) && + (FRONTV.indexOf(local.charAt(n + 1)) >= 0) ) { + break; + } + if (regionMatch(local, n, "CIA")) { // "CIA" -> X + code.append('X'); + break; + } + if (!isLastChar(wdsz, n) && + (FRONTV.indexOf(local.charAt(n + 1)) >= 0)) { + code.append('S'); + break; // CI,CE,CY -> S + } + if (isPreviousChar(local, n, 'S') && + isNextChar(local, n, 'H') ) { // SCH->sk + code.append('K') ; + break ; + } + if (isNextChar(local, n, 'H')) { // detect CH + if ((n == 0) && + (wdsz >= 3) && + isVowel(local,2) ) { // CH consonant -> K consonant + code.append('K'); + } else { + code.append('X'); // CHvowel -> X + } + } else { + code.append('K'); + } + break ; + case 'D' : + if (!isLastChar(wdsz, n + 1) && + isNextChar(local, n, 'G') && + (FRONTV.indexOf(local.charAt(n + 2)) >= 0)) { // DGE DGI DGY -> J + code.append('J'); n += 2 ; + } else { + code.append('T'); + } + break ; + case 'G' : // GH silent at end or before consonant + if (isLastChar(wdsz, n + 1) && + isNextChar(local, n, 'H')) { + break; + } + if (!isLastChar(wdsz, n + 1) && + isNextChar(local,n,'H') && + !isVowel(local,n+2)) { + break; + } + if ((n > 0) && + ( regionMatch(local, n, "GN") || + regionMatch(local, n, "GNED") ) ) { + break; // silent G + } + if (isPreviousChar(local, n, 'G')) { + // NOTE: Given that duplicated chars are removed, I don't see how this can ever be true + hard = true ; + } else { + hard = false ; + } + if (!isLastChar(wdsz, n) && + (FRONTV.indexOf(local.charAt(n + 1)) >= 0) && + (!hard)) { + code.append('J'); + } else { + code.append('K'); + } + break ; + case 'H': + if (isLastChar(wdsz, n)) { + break ; // terminal H + } + if ((n > 0) && + (VARSON.indexOf(local.charAt(n - 1)) >= 0)) { + break; + } + if (isVowel(local,n+1)) { + code.append('H'); // Hvowel + } + break; + case 'F': + case 'J' : + case 'L' : + case 'M': + case 'N' : + case 'R' : + code.append(symb); + break; + case 'K' : + if (n > 0) { // not initial + if (!isPreviousChar(local, n, 'C')) { + code.append(symb); + } + } else { + code.append(symb); // initial K + } + break ; + case 'P' : + if (isNextChar(local,n,'H')) { + // PH -> F + code.append('F'); + } else { + code.append(symb); + } + break ; + case 'Q' : + code.append('K'); + break; + case 'S' : + if (regionMatch(local,n,"SH") || + regionMatch(local,n,"SIO") || + regionMatch(local,n,"SIA")) { + code.append('X'); + } else { + code.append('S'); + } + break; + case 'T' : + if (regionMatch(local,n,"TIA") || + regionMatch(local,n,"TIO")) { + code.append('X'); + break; + } + if (regionMatch(local,n,"TCH")) { + // Silent if in "TCH" + break; + } + // substitute numeral 0 for TH (resembles theta after all) + if (regionMatch(local,n,"TH")) { + code.append('0'); + } else { + code.append('T'); + } + break ; + case 'V' : + code.append('F'); break ; + case 'W' : case 'Y' : // silent if not followed by vowel + if (!isLastChar(wdsz,n) && + isVowel(local,n+1)) { + code.append(symb); + } + break ; + case 'X' : + code.append('K'); code.append('S'); + break ; + case 'Z' : + code.append('S'); break ; + } // end switch + n++ ; + } // end else from symb != 'C' + if (code.length() > this.getMaxCodeLen()) { + code.setLength(this.getMaxCodeLen()); + } + } + return code.toString(); + } + + private boolean isVowel(StringBuffer string, int index) { + return VOWELS.indexOf(string.charAt(index)) >= 0; + } + + private boolean isPreviousChar(StringBuffer string, int index, char c) { + boolean matches = false; + if( index > 0 && + index < string.length() ) { + matches = string.charAt(index - 1) == c; + } + return matches; + } + + private boolean isNextChar(StringBuffer string, int index, char c) { + boolean matches = false; + if( index >= 0 && + index < string.length() - 1 ) { + matches = string.charAt(index + 1) == c; + } + return matches; + } + + private boolean regionMatch(StringBuffer string, int index, String test) { + boolean matches = false; + if( index >= 0 && + (index + test.length() - 1) < string.length() ) { + String substring = string.substring( index, index + test.length()); + matches = substring.equals( test ); + } + return matches; + } + + private boolean isLastChar(int wdsz, int n) { + return n + 1 == wdsz; + } + + + /** + * Encodes an Object using the metaphone algorithm. This method + * is provided in order to satisfy the requirements of the + * Encoder interface, and will throw an EncoderException if the + * supplied object is not of type java.lang.String. + * + * @param pObject Object to encode + * @return An object (or type java.lang.String) containing the + * metaphone code which corresponds to the String supplied. + * @throws EncoderException if the parameter supplied is not + * of type java.lang.String + */ + public Object encode(Object pObject) throws EncoderException { + if (!(pObject instanceof String)) { + throw new EncoderException("Parameter supplied to Metaphone encode is not of type java.lang.String"); + } + return metaphone((String) pObject); + } + + /** + * Encodes a String using the Metaphone algorithm. + * + * @param pString String object to encode + * @return The metaphone code corresponding to the String supplied + */ + public String encode(String pString) { + return metaphone(pString); + } + + /** + * Tests is the metaphones of two strings are identical. + * + * @param str1 First of two strings to compare + * @param str2 Second of two strings to compare + * @return true if the metaphones of these strings are identical, + * false otherwise. + */ + public boolean isMetaphoneEqual(String str1, String str2) { + return metaphone(str1).equals(metaphone(str2)); + } + + /** + * Returns the maxCodeLen. + * @return int + */ + public int getMaxCodeLen() { return this.maxCodeLen; } + + /** + * Sets the maxCodeLen. + * @param maxCodeLen The maxCodeLen to set + */ + public void setMaxCodeLen(int maxCodeLen) { this.maxCodeLen = maxCodeLen; } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/RefinedSoundex.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/RefinedSoundex.java new file mode 100644 index 000000000..7fb730cea --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/RefinedSoundex.java @@ -0,0 +1,203 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + * Encodes a string into a Refined Soundex value. A refined soundex code is + * optimized for spell checking words. Soundex method originally developed by + * Margaret Odell and Robert Russell. + * + * @author Apache Software Foundation + * @version $Id: RefinedSoundex.java 1064455 2011-01-28 04:40:27Z ggregory $ + */ +public class RefinedSoundex implements StringEncoder { + + /** + * @since 1.4 + */ + public static final String US_ENGLISH_MAPPING_STRING = "01360240043788015936020505"; + + /** + * RefinedSoundex is *refined* for a number of reasons one being that the + * mappings have been altered. This implementation contains default + * mappings for US English. + */ + private static final char[] US_ENGLISH_MAPPING = US_ENGLISH_MAPPING_STRING.toCharArray(); + + /** + * Every letter of the alphabet is "mapped" to a numerical value. This char + * array holds the values to which each letter is mapped. This + * implementation contains a default map for US_ENGLISH + */ + private final char[] soundexMapping; + + /** + * This static variable contains an instance of the RefinedSoundex using + * the US_ENGLISH mapping. + */ + public static final RefinedSoundex US_ENGLISH = new RefinedSoundex(); + + /** + * Creates an instance of the RefinedSoundex object using the default US + * English mapping. + */ + public RefinedSoundex() { + this.soundexMapping = US_ENGLISH_MAPPING; + } + + /** + * Creates a refined soundex instance using a custom mapping. This + * constructor can be used to customize the mapping, and/or possibly + * provide an internationalized mapping for a non-Western character set. + * + * @param mapping + * Mapping array to use when finding the corresponding code for + * a given character + */ + public RefinedSoundex(char[] mapping) { + this.soundexMapping = new char[mapping.length]; + System.arraycopy(mapping, 0, this.soundexMapping, 0, mapping.length); + } + + /** + * Creates a refined Soundex instance using a custom mapping. This constructor can be used to customize the mapping, + * and/or possibly provide an internationalized mapping for a non-Western character set. + * + * @param mapping + * Mapping string to use when finding the corresponding code for a given character + * @since 1.4 + */ + public RefinedSoundex(String mapping) { + this.soundexMapping = mapping.toCharArray(); + } + + /** + * Returns the number of characters in the two encoded Strings that are the + * same. This return value ranges from 0 to the length of the shortest + * encoded String: 0 indicates little or no similarity, and 4 out of 4 (for + * example) indicates strong similarity or identical values. For refined + * Soundex, the return value can be greater than 4. + * + * @param s1 + * A String that will be encoded and compared. + * @param s2 + * A String that will be encoded and compared. + * @return The number of characters in the two encoded Strings that are the + * same from 0 to to the length of the shortest encoded String. + * + * @see SoundexUtils#difference(StringEncoder,String,String) + * @see + * MS T-SQL DIFFERENCE + * + * @throws EncoderException + * if an error occurs encoding one of the strings + * @since 1.3 + */ + public int difference(String s1, String s2) throws EncoderException { + return SoundexUtils.difference(this, s1, s2); + } + + /** + * Encodes an Object using the refined soundex algorithm. This method is + * provided in order to satisfy the requirements of the Encoder interface, + * and will throw an EncoderException if the supplied object is not of type + * java.lang.String. + * + * @param pObject + * Object to encode + * @return An object (or type java.lang.String) containing the refined + * soundex code which corresponds to the String supplied. + * @throws EncoderException + * if the parameter supplied is not of type java.lang.String + */ + public Object encode(Object pObject) throws EncoderException { + if (!(pObject instanceof String)) { + throw new EncoderException("Parameter supplied to RefinedSoundex encode is not of type java.lang.String"); + } + return soundex((String) pObject); + } + + /** + * Encodes a String using the refined soundex algorithm. + * + * @param pString + * A String object to encode + * @return A Soundex code corresponding to the String supplied + */ + public String encode(String pString) { + return soundex(pString); + } + + /** + * Returns the mapping code for a given character. The mapping codes are + * maintained in an internal char array named soundexMapping, and the + * default values of these mappings are US English. + * + * @param c + * char to get mapping for + * @return A character (really a numeral) to return for the given char + */ + char getMappingCode(char c) { + if (!Character.isLetter(c)) { + return 0; + } + return this.soundexMapping[Character.toUpperCase(c) - 'A']; + } + + /** + * Retreives the Refined Soundex code for a given String object. + * + * @param str + * String to encode using the Refined Soundex algorithm + * @return A soundex code for the String supplied + */ + public String soundex(String str) { + if (str == null) { + return null; + } + str = SoundexUtils.clean(str); + if (str.length() == 0) { + return str; + } + + StringBuffer sBuf = new StringBuffer(); + sBuf.append(str.charAt(0)); + + char last, current; + last = '*'; + + for (int i = 0; i < str.length(); i++) { + + current = getMappingCode(str.charAt(i)); + if (current == last) { + continue; + } else if (current != 0) { + sBuf.append(current); + } + + last = current; + + } + + return sBuf.toString(); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Soundex.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Soundex.java new file mode 100644 index 000000000..76805bca3 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/Soundex.java @@ -0,0 +1,279 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + * Encodes a string into a Soundex value. Soundex is an encoding used to relate similar names, but can also be used as a + * general purpose scheme to find word with similar phonemes. + * + * @author Apache Software Foundation + * @version $Id: Soundex.java 1064454 2011-01-28 04:40:02Z ggregory $ + */ +public class Soundex implements StringEncoder { + + /** + * This is a default mapping of the 26 letters used in US English. A value of 0 for a letter position + * means do not encode. + *

    + * (This constant is provided as both an implementation convenience and to allow Javadoc to pick + * up the value for the constant values page.) + *

    + * + * @see #US_ENGLISH_MAPPING + */ + public static final String US_ENGLISH_MAPPING_STRING = "01230120022455012623010202"; + + /** + * This is a default mapping of the 26 letters used in US English. A value of 0 for a letter position + * means do not encode. + * + * @see Soundex#Soundex(char[]) + */ + private static final char[] US_ENGLISH_MAPPING = US_ENGLISH_MAPPING_STRING.toCharArray(); + + /** + * An instance of Soundex using the US_ENGLISH_MAPPING mapping. + * + * @see #US_ENGLISH_MAPPING + */ + public static final Soundex US_ENGLISH = new Soundex(); + + + /** + * Encodes the Strings and returns the number of characters in the two encoded Strings that are the same. This + * return value ranges from 0 through 4: 0 indicates little or no similarity, and 4 indicates strong similarity or + * identical values. + * + * @param s1 + * A String that will be encoded and compared. + * @param s2 + * A String that will be encoded and compared. + * @return The number of characters in the two encoded Strings that are the same from 0 to 4. + * + * @see SoundexUtils#difference(StringEncoder,String,String) + * @see MS + * T-SQL DIFFERENCE + * + * @throws EncoderException + * if an error occurs encoding one of the strings + * @since 1.3 + */ + public int difference(String s1, String s2) throws EncoderException { + return SoundexUtils.difference(this, s1, s2); + } + + /** + * The maximum length of a Soundex code - Soundex codes are only four characters by definition. + * + * @deprecated This feature is not needed since the encoding size must be constant. Will be removed in 2.0. + */ + private int maxLength = 4; + + /** + * Every letter of the alphabet is "mapped" to a numerical value. This char array holds the values to which each + * letter is mapped. This implementation contains a default map for US_ENGLISH + */ + private final char[] soundexMapping; + + /** + * Creates an instance using US_ENGLISH_MAPPING + * + * @see Soundex#Soundex(char[]) + * @see Soundex#US_ENGLISH_MAPPING + */ + public Soundex() { + this.soundexMapping = US_ENGLISH_MAPPING; + } + + /** + * Creates a soundex instance using the given mapping. This constructor can be used to provide an internationalized + * mapping for a non-Western character set. + * + * Every letter of the alphabet is "mapped" to a numerical value. This char array holds the values to which each + * letter is mapped. This implementation contains a default map for US_ENGLISH + * + * @param mapping + * Mapping array to use when finding the corresponding code for a given character + */ + public Soundex(char[] mapping) { + this.soundexMapping = new char[mapping.length]; + System.arraycopy(mapping, 0, this.soundexMapping, 0, mapping.length); + } + + /** + * Creates a refined soundex instance using a custom mapping. This constructor can be used to customize the mapping, + * and/or possibly provide an internationalized mapping for a non-Western character set. + * + * @param mapping + * Mapping string to use when finding the corresponding code for a given character + * @since 1.4 + */ + public Soundex(String mapping) { + this.soundexMapping = mapping.toCharArray(); + } + + /** + * Encodes an Object using the soundex algorithm. This method is provided in order to satisfy the requirements of + * the Encoder interface, and will throw an EncoderException if the supplied object is not of type java.lang.String. + * + * @param pObject + * Object to encode + * @return An object (or type java.lang.String) containing the soundex code which corresponds to the String + * supplied. + * @throws EncoderException + * if the parameter supplied is not of type java.lang.String + * @throws IllegalArgumentException + * if a character is not mapped + */ + public Object encode(Object pObject) throws EncoderException { + if (!(pObject instanceof String)) { + throw new EncoderException("Parameter supplied to Soundex encode is not of type java.lang.String"); + } + return soundex((String) pObject); + } + + /** + * Encodes a String using the soundex algorithm. + * + * @param pString + * A String object to encode + * @return A Soundex code corresponding to the String supplied + * @throws IllegalArgumentException + * if a character is not mapped + */ + public String encode(String pString) { + return soundex(pString); + } + + /** + * Used internally by the SoundEx algorithm. + * + * Consonants from the same code group separated by W or H are treated as one. + * + * @param str + * the cleaned working string to encode (in upper case). + * @param index + * the character position to encode + * @return Mapping code for a particular character + * @throws IllegalArgumentException + * if the character is not mapped + */ + private char getMappingCode(String str, int index) { + // map() throws IllegalArgumentException + char mappedChar = this.map(str.charAt(index)); + // HW rule check + if (index > 1 && mappedChar != '0') { + char hwChar = str.charAt(index - 1); + if ('H' == hwChar || 'W' == hwChar) { + char preHWChar = str.charAt(index - 2); + char firstCode = this.map(preHWChar); + if (firstCode == mappedChar || 'H' == preHWChar || 'W' == preHWChar) { + return 0; + } + } + } + return mappedChar; + } + + /** + * Returns the maxLength. Standard Soundex + * + * @deprecated This feature is not needed since the encoding size must be constant. Will be removed in 2.0. + * @return int + */ + public int getMaxLength() { + return this.maxLength; + } + + /** + * Returns the soundex mapping. + * + * @return soundexMapping. + */ + private char[] getSoundexMapping() { + return this.soundexMapping; + } + + /** + * Maps the given upper-case character to its Soundex code. + * + * @param ch + * An upper-case character. + * @return A Soundex code. + * @throws IllegalArgumentException + * Thrown if ch is not mapped. + */ + private char map(char ch) { + int index = ch - 'A'; + if (index < 0 || index >= this.getSoundexMapping().length) { + throw new IllegalArgumentException("The character is not mapped: " + ch); + } + return this.getSoundexMapping()[index]; + } + + /** + * Sets the maxLength. + * + * @deprecated This feature is not needed since the encoding size must be constant. Will be removed in 2.0. + * @param maxLength + * The maxLength to set + */ + public void setMaxLength(int maxLength) { + this.maxLength = maxLength; + } + + /** + * Retrieves the Soundex code for a given String object. + * + * @param str + * String to encode using the Soundex algorithm + * @return A soundex code for the String supplied + * @throws IllegalArgumentException + * if a character is not mapped + */ + public String soundex(String str) { + if (str == null) { + return null; + } + str = SoundexUtils.clean(str); + if (str.length() == 0) { + return str; + } + char out[] = {'0', '0', '0', '0'}; + char last, mapped; + int incount = 1, count = 1; + out[0] = str.charAt(0); + // getMappingCode() throws IllegalArgumentException + last = getMappingCode(str, 0); + while ((incount < str.length()) && (count < out.length)) { + mapped = getMappingCode(str, incount++); + if (mapped != 0) { + if ((mapped != '0') && (mapped != last)) { + out[count++] = mapped; + } + last = mapped; + } + } + return new String(out); + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/SoundexUtils.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/SoundexUtils.java new file mode 100644 index 000000000..3e5a16a5a --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/SoundexUtils.java @@ -0,0 +1,124 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.language; + +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + * Utility methods for {@link Soundex} and {@link RefinedSoundex} classes. + * + * @author Apache Software Foundation + * @version $Id: SoundexUtils.java 658834 2008-05-21 19:57:51Z niallp $ + * @since 1.3 + */ +final class SoundexUtils { + + /** + * Cleans up the input string before Soundex processing by only returning + * upper case letters. + * + * @param str + * The String to clean. + * @return A clean String. + */ + static String clean(String str) { + if (str == null || str.length() == 0) { + return str; + } + int len = str.length(); + char[] chars = new char[len]; + int count = 0; + for (int i = 0; i < len; i++) { + if (Character.isLetter(str.charAt(i))) { + chars[count++] = str.charAt(i); + } + } + if (count == len) { + return str.toUpperCase(java.util.Locale.ENGLISH); + } + return new String(chars, 0, count).toUpperCase(java.util.Locale.ENGLISH); + } + + /** + * Encodes the Strings and returns the number of characters in the two + * encoded Strings that are the same. + *
      + *
    • For Soundex, this return value ranges from 0 through 4: 0 indicates + * little or no similarity, and 4 indicates strong similarity or identical + * values.
    • + *
    • For refined Soundex, the return value can be greater than 4.
    • + *
    + * + * @param encoder + * The encoder to use to encode the Strings. + * @param s1 + * A String that will be encoded and compared. + * @param s2 + * A String that will be encoded and compared. + * @return The number of characters in the two Soundex encoded Strings that + * are the same. + * + * @see #differenceEncoded(String,String) + * @see + * MS T-SQL DIFFERENCE + * + * @throws EncoderException + * if an error occurs encoding one of the strings + */ + static int difference(StringEncoder encoder, String s1, String s2) throws EncoderException { + return differenceEncoded(encoder.encode(s1), encoder.encode(s2)); + } + + /** + * Returns the number of characters in the two Soundex encoded Strings that + * are the same. + *
      + *
    • For Soundex, this return value ranges from 0 through 4: 0 indicates + * little or no similarity, and 4 indicates strong similarity or identical + * values.
    • + *
    • For refined Soundex, the return value can be greater than 4.
    • + *
    + * + * @param es1 + * An encoded String. + * @param es2 + * An encoded String. + * @return The number of characters in the two Soundex encoded Strings that + * are the same. + * + * @see + * MS T-SQL DIFFERENCE + */ + static int differenceEncoded(String es1, String es2) { + + if (es1 == null || es2 == null) { + return 0; + } + int lengthToMatch = Math.min(es1.length(), es2.length()); + int diff = 0; + for (int i = 0; i < lengthToMatch; i++) { + if (es1.charAt(i) == es2.charAt(i)) { + diff++; + } + } + return diff; + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/package.html b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/package.html new file mode 100644 index 000000000..6e3376689 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/language/package.html @@ -0,0 +1,21 @@ + + + + Language and phonetic encoders. + + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/BCodec.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/BCodec.java new file mode 100644 index 000000000..b694888eb --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/BCodec.java @@ -0,0 +1,209 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.net; + +import java.io.UnsupportedEncodingException; + +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.CharEncoding; +import org.mozilla.apache.commons.codec.StringDecoder; +import org.mozilla.apache.commons.codec.StringEncoder; +import org.mozilla.apache.commons.codec.binary.Base64; + +/** + *

    + * Identical to the Base64 encoding defined by RFC + * 1521 and allows a character set to be specified. + *

    + * + *

    + * RFC 1522 describes techniques to allow the encoding of non-ASCII + * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message + * handling software. + *

    + * + * @see MIME (Multipurpose Internet Mail Extensions) Part Two: Message + * Header Extensions for Non-ASCII Text + * + * @author Apache Software Foundation + * @since 1.3 + * @version $Id: BCodec.java 797857 2009-07-25 23:43:33Z ggregory $ + */ +public class BCodec extends RFC1522Codec implements StringEncoder, StringDecoder { + /** + * The default charset used for string decoding and encoding. + */ + private final String charset; + + /** + * Default constructor. + */ + public BCodec() { + this(CharEncoding.UTF_8); + } + + /** + * Constructor which allows for the selection of a default charset + * + * @param charset + * the default string charset to use. + * + * @see Standard charsets + */ + public BCodec(final String charset) { + super(); + this.charset = charset; + } + + protected String getEncoding() { + return "B"; + } + + protected byte[] doEncoding(byte[] bytes) { + if (bytes == null) { + return null; + } + return Base64.encodeBase64(bytes); + } + + protected byte[] doDecoding(byte[] bytes) { + if (bytes == null) { + return null; + } + return Base64.decodeBase64(bytes); + } + + /** + * Encodes a string into its Base64 form using the specified charset. Unsafe characters are escaped. + * + * @param value + * string to convert to Base64 form + * @param charset + * the charset for value + * @return Base64 string + * + * @throws EncoderException + * thrown if a failure condition is encountered during the encoding process. + */ + public String encode(final String value, final String charset) throws EncoderException { + if (value == null) { + return null; + } + try { + return encodeText(value, charset); + } catch (UnsupportedEncodingException e) { + throw new EncoderException(e.getMessage(), e); + } + } + + /** + * Encodes a string into its Base64 form using the default charset. Unsafe characters are escaped. + * + * @param value + * string to convert to Base64 form + * @return Base64 string + * + * @throws EncoderException + * thrown if a failure condition is encountered during the encoding process. + */ + public String encode(String value) throws EncoderException { + if (value == null) { + return null; + } + return encode(value, getDefaultCharset()); + } + + /** + * Decodes a Base64 string into its original form. Escaped characters are converted back to their original + * representation. + * + * @param value + * Base64 string to convert into its original form + * @return original string + * @throws DecoderException + * A decoder exception is thrown if a failure condition is encountered during the decode process. + */ + public String decode(String value) throws DecoderException { + if (value == null) { + return null; + } + try { + return decodeText(value); + } catch (UnsupportedEncodingException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Encodes an object into its Base64 form using the default charset. Unsafe characters are escaped. + * + * @param value + * object to convert to Base64 form + * @return Base64 object + * + * @throws EncoderException + * thrown if a failure condition is encountered during the encoding process. + */ + public Object encode(Object value) throws EncoderException { + if (value == null) { + return null; + } else if (value instanceof String) { + return encode((String) value); + } else { + throw new EncoderException("Objects of type " + + value.getClass().getName() + + " cannot be encoded using BCodec"); + } + } + + /** + * Decodes a Base64 object into its original form. Escaped characters are converted back to their original + * representation. + * + * @param value + * Base64 object to convert into its original form + * + * @return original object + * + * @throws DecoderException + * Thrown if the argument is not a String. Thrown if a failure condition is + * encountered during the decode process. + */ + public Object decode(Object value) throws DecoderException { + if (value == null) { + return null; + } else if (value instanceof String) { + return decode((String) value); + } else { + throw new DecoderException("Objects of type " + + value.getClass().getName() + + " cannot be decoded using BCodec"); + } + } + + /** + * The default charset used for string decoding and encoding. + * + * @return the default string charset. + */ + public String getDefaultCharset() { + return this.charset; + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QCodec.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QCodec.java new file mode 100644 index 000000000..d174bcdff --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QCodec.java @@ -0,0 +1,312 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.net; + +import java.io.UnsupportedEncodingException; +import java.util.BitSet; + +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.CharEncoding; +import org.mozilla.apache.commons.codec.StringDecoder; +import org.mozilla.apache.commons.codec.StringEncoder; + +/** + *

    + * Similar to the Quoted-Printable content-transfer-encoding defined in RFC 1521 and designed to allow text containing mostly ASCII + * characters to be decipherable on an ASCII terminal without decoding. + *

    + * + *

    + * RFC 1522 describes techniques to allow the encoding of non-ASCII + * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message + * handling software. + *

    + * + * @see MIME (Multipurpose Internet Mail Extensions) Part Two: Message + * Header Extensions for Non-ASCII Text + * + * @author Apache Software Foundation + * @since 1.3 + * @version $Id: QCodec.java 797857 2009-07-25 23:43:33Z ggregory $ + */ +public class QCodec extends RFC1522Codec implements StringEncoder, StringDecoder { + /** + * The default charset used for string decoding and encoding. + */ + private final String charset; + + /** + * BitSet of printable characters as defined in RFC 1522. + */ + private static final BitSet PRINTABLE_CHARS = new BitSet(256); + // Static initializer for printable chars collection + static { + // alpha characters + PRINTABLE_CHARS.set(' '); + PRINTABLE_CHARS.set('!'); + PRINTABLE_CHARS.set('"'); + PRINTABLE_CHARS.set('#'); + PRINTABLE_CHARS.set('$'); + PRINTABLE_CHARS.set('%'); + PRINTABLE_CHARS.set('&'); + PRINTABLE_CHARS.set('\''); + PRINTABLE_CHARS.set('('); + PRINTABLE_CHARS.set(')'); + PRINTABLE_CHARS.set('*'); + PRINTABLE_CHARS.set('+'); + PRINTABLE_CHARS.set(','); + PRINTABLE_CHARS.set('-'); + PRINTABLE_CHARS.set('.'); + PRINTABLE_CHARS.set('/'); + for (int i = '0'; i <= '9'; i++) { + PRINTABLE_CHARS.set(i); + } + PRINTABLE_CHARS.set(':'); + PRINTABLE_CHARS.set(';'); + PRINTABLE_CHARS.set('<'); + PRINTABLE_CHARS.set('>'); + PRINTABLE_CHARS.set('@'); + for (int i = 'A'; i <= 'Z'; i++) { + PRINTABLE_CHARS.set(i); + } + PRINTABLE_CHARS.set('['); + PRINTABLE_CHARS.set('\\'); + PRINTABLE_CHARS.set(']'); + PRINTABLE_CHARS.set('^'); + PRINTABLE_CHARS.set('`'); + for (int i = 'a'; i <= 'z'; i++) { + PRINTABLE_CHARS.set(i); + } + PRINTABLE_CHARS.set('{'); + PRINTABLE_CHARS.set('|'); + PRINTABLE_CHARS.set('}'); + PRINTABLE_CHARS.set('~'); + } + + private static final byte BLANK = 32; + + private static final byte UNDERSCORE = 95; + + private boolean encodeBlanks = false; + + /** + * Default constructor. + */ + public QCodec() { + this(CharEncoding.UTF_8); + } + + /** + * Constructor which allows for the selection of a default charset + * + * @param charset + * the default string charset to use. + * + * @see Standard charsets + */ + public QCodec(final String charset) { + super(); + this.charset = charset; + } + + protected String getEncoding() { + return "Q"; + } + + protected byte[] doEncoding(byte[] bytes) { + if (bytes == null) { + return null; + } + byte[] data = QuotedPrintableCodec.encodeQuotedPrintable(PRINTABLE_CHARS, bytes); + if (this.encodeBlanks) { + for (int i = 0; i < data.length; i++) { + if (data[i] == BLANK) { + data[i] = UNDERSCORE; + } + } + } + return data; + } + + protected byte[] doDecoding(byte[] bytes) throws DecoderException { + if (bytes == null) { + return null; + } + boolean hasUnderscores = false; + for (int i = 0; i < bytes.length; i++) { + if (bytes[i] == UNDERSCORE) { + hasUnderscores = true; + break; + } + } + if (hasUnderscores) { + byte[] tmp = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + if (b != UNDERSCORE) { + tmp[i] = b; + } else { + tmp[i] = BLANK; + } + } + return QuotedPrintableCodec.decodeQuotedPrintable(tmp); + } + return QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } + + /** + * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped. + * + * @param pString + * string to convert to quoted-printable form + * @param charset + * the charset for pString + * @return quoted-printable string + * + * @throws EncoderException + * thrown if a failure condition is encountered during the encoding process. + */ + public String encode(final String pString, final String charset) throws EncoderException { + if (pString == null) { + return null; + } + try { + return encodeText(pString, charset); + } catch (UnsupportedEncodingException e) { + throw new EncoderException(e.getMessage(), e); + } + } + + /** + * Encodes a string into its quoted-printable form using the default charset. Unsafe characters are escaped. + * + * @param pString + * string to convert to quoted-printable form + * @return quoted-printable string + * + * @throws EncoderException + * thrown if a failure condition is encountered during the encoding process. + */ + public String encode(String pString) throws EncoderException { + if (pString == null) { + return null; + } + return encode(pString, getDefaultCharset()); + } + + /** + * Decodes a quoted-printable string into its original form. Escaped characters are converted back to their original + * representation. + * + * @param pString + * quoted-printable string to convert into its original form + * + * @return original string + * + * @throws DecoderException + * A decoder exception is thrown if a failure condition is encountered during the decode process. + */ + public String decode(String pString) throws DecoderException { + if (pString == null) { + return null; + } + try { + return decodeText(pString); + } catch (UnsupportedEncodingException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Encodes an object into its quoted-printable form using the default charset. Unsafe characters are escaped. + * + * @param pObject + * object to convert to quoted-printable form + * @return quoted-printable object + * + * @throws EncoderException + * thrown if a failure condition is encountered during the encoding process. + */ + public Object encode(Object pObject) throws EncoderException { + if (pObject == null) { + return null; + } else if (pObject instanceof String) { + return encode((String) pObject); + } else { + throw new EncoderException("Objects of type " + + pObject.getClass().getName() + + " cannot be encoded using Q codec"); + } + } + + /** + * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original + * representation. + * + * @param pObject + * quoted-printable object to convert into its original form + * + * @return original object + * + * @throws DecoderException + * Thrown if the argument is not a String. Thrown if a failure condition is + * encountered during the decode process. + */ + public Object decode(Object pObject) throws DecoderException { + if (pObject == null) { + return null; + } else if (pObject instanceof String) { + return decode((String) pObject); + } else { + throw new DecoderException("Objects of type " + + pObject.getClass().getName() + + " cannot be decoded using Q codec"); + } + } + + /** + * The default charset used for string decoding and encoding. + * + * @return the default string charset. + */ + public String getDefaultCharset() { + return this.charset; + } + + /** + * Tests if optional tranformation of SPACE characters is to be used + * + * @return true if SPACE characters are to be transformed, false otherwise + */ + public boolean isEncodeBlanks() { + return this.encodeBlanks; + } + + /** + * Defines whether optional tranformation of SPACE characters is to be used + * + * @param b + * true if SPACE characters are to be transformed, false otherwise + */ + public void setEncodeBlanks(boolean b) { + this.encodeBlanks = b; + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QuotedPrintableCodec.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QuotedPrintableCodec.java new file mode 100644 index 000000000..c9b5e6172 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/QuotedPrintableCodec.java @@ -0,0 +1,388 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.net; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.BitSet; + +import org.mozilla.apache.commons.codec.BinaryDecoder; +import org.mozilla.apache.commons.codec.BinaryEncoder; +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.CharEncoding; +import org.mozilla.apache.commons.codec.StringDecoder; +import org.mozilla.apache.commons.codec.StringEncoder; +import org.mozilla.apache.commons.codec.binary.StringUtils; + +/** + *

    + * Codec for the Quoted-Printable section of RFC 1521. + *

    + *

    + * The Quoted-Printable encoding is intended to represent data that largely consists of octets that correspond to + * printable characters in the ASCII character set. It encodes the data in such a way that the resulting octets are + * unlikely to be modified by mail transport. If the data being encoded are mostly ASCII text, the encoded form of the + * data remains largely recognizable by humans. A body which is entirely ASCII may also be encoded in Quoted-Printable + * to ensure the integrity of the data should the message pass through a character- translating, and/or line-wrapping + * gateway. + *

    + * + *

    + * Note: + *

    + *

    + * Rules #3, #4, and #5 of the quoted-printable spec are not implemented yet because the complete quoted-printable spec + * does not lend itself well into the byte[] oriented codec framework. Complete the codec once the steamable codec + * framework is ready. The motivation behind providing the codec in a partial form is that it can already come in handy + * for those applications that do not require quoted-printable line formatting (rules #3, #4, #5), for instance Q codec. + *

    + * + * @see RFC 1521 MIME (Multipurpose Internet Mail Extensions) Part One: + * Mechanisms for Specifying and Describing the Format of Internet Message Bodies + * + * @author Apache Software Foundation + * @since 1.3 + * @version $Id: QuotedPrintableCodec.java 1080712 2011-03-11 18:26:59Z ggregory $ + */ +public class QuotedPrintableCodec implements BinaryEncoder, BinaryDecoder, StringEncoder, StringDecoder { + /** + * The default charset used for string decoding and encoding. + */ + private final String charset; + + /** + * BitSet of printable characters as defined in RFC 1521. + */ + private static final BitSet PRINTABLE_CHARS = new BitSet(256); + + private static final byte ESCAPE_CHAR = '='; + + private static final byte TAB = 9; + + private static final byte SPACE = 32; + // Static initializer for printable chars collection + static { + // alpha characters + for (int i = 33; i <= 60; i++) { + PRINTABLE_CHARS.set(i); + } + for (int i = 62; i <= 126; i++) { + PRINTABLE_CHARS.set(i); + } + PRINTABLE_CHARS.set(TAB); + PRINTABLE_CHARS.set(SPACE); + } + + /** + * Default constructor. + */ + public QuotedPrintableCodec() { + this(CharEncoding.UTF_8); + } + + /** + * Constructor which allows for the selection of a default charset + * + * @param charset + * the default string charset to use. + */ + public QuotedPrintableCodec(String charset) { + super(); + this.charset = charset; + } + + /** + * Encodes byte into its quoted-printable representation. + * + * @param b + * byte to encode + * @param buffer + * the buffer to write to + */ + private static final void encodeQuotedPrintable(int b, ByteArrayOutputStream buffer) { + buffer.write(ESCAPE_CHAR); + char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16)); + char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16)); + buffer.write(hex1); + buffer.write(hex2); + } + + /** + * Encodes an array of bytes into an array of quoted-printable 7-bit characters. Unsafe characters are escaped. + * + *

    + * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in + * RFC 1521 and is suitable for encoding binary data and unformatted text. + *

    + * + * @param printable + * bitset of characters deemed quoted-printable + * @param bytes + * array of bytes to be encoded + * @return array of bytes containing quoted-printable data + */ + public static final byte[] encodeQuotedPrintable(BitSet printable, byte[] bytes) { + if (bytes == null) { + return null; + } + if (printable == null) { + printable = PRINTABLE_CHARS; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i]; + if (b < 0) { + b = 256 + b; + } + if (printable.get(b)) { + buffer.write(b); + } else { + encodeQuotedPrintable(b, buffer); + } + } + return buffer.toByteArray(); + } + + /** + * Decodes an array quoted-printable characters into an array of original bytes. Escaped characters are converted + * back to their original representation. + * + *

    + * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in + * RFC 1521. + *

    + * + * @param bytes + * array of quoted-printable characters + * @return array of original bytes + * @throws DecoderException + * Thrown if quoted-printable decoding is unsuccessful + */ + public static final byte[] decodeQuotedPrintable(byte[] bytes) throws DecoderException { + if (bytes == null) { + return null; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i]; + if (b == ESCAPE_CHAR) { + try { + int u = Utils.digit16(bytes[++i]); + int l = Utils.digit16(bytes[++i]); + buffer.write((char) ((u << 4) + l)); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DecoderException("Invalid quoted-printable encoding", e); + } + } else { + buffer.write(b); + } + } + return buffer.toByteArray(); + } + + /** + * Encodes an array of bytes into an array of quoted-printable 7-bit characters. Unsafe characters are escaped. + * + *

    + * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in + * RFC 1521 and is suitable for encoding binary data and unformatted text. + *

    + * + * @param bytes + * array of bytes to be encoded + * @return array of bytes containing quoted-printable data + */ + public byte[] encode(byte[] bytes) { + return encodeQuotedPrintable(PRINTABLE_CHARS, bytes); + } + + /** + * Decodes an array of quoted-printable characters into an array of original bytes. Escaped characters are converted + * back to their original representation. + * + *

    + * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in + * RFC 1521. + *

    + * + * @param bytes + * array of quoted-printable characters + * @return array of original bytes + * @throws DecoderException + * Thrown if quoted-printable decoding is unsuccessful + */ + public byte[] decode(byte[] bytes) throws DecoderException { + return decodeQuotedPrintable(bytes); + } + + /** + * Encodes a string into its quoted-printable form using the default string charset. Unsafe characters are escaped. + * + *

    + * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in + * RFC 1521 and is suitable for encoding binary data. + *

    + * + * @param pString + * string to convert to quoted-printable form + * @return quoted-printable string + * + * @throws EncoderException + * Thrown if quoted-printable encoding is unsuccessful + * + * @see #getDefaultCharset() + */ + public String encode(String pString) throws EncoderException { + if (pString == null) { + return null; + } + try { + return encode(pString, getDefaultCharset()); + } catch (UnsupportedEncodingException e) { + throw new EncoderException(e.getMessage(), e); + } + } + + /** + * Decodes a quoted-printable string into its original form using the specified string charset. Escaped characters + * are converted back to their original representation. + * + * @param pString + * quoted-printable string to convert into its original form + * @param charset + * the original string charset + * @return original string + * @throws DecoderException + * Thrown if quoted-printable decoding is unsuccessful + * @throws UnsupportedEncodingException + * Thrown if charset is not supported + */ + public String decode(String pString, String charset) throws DecoderException, UnsupportedEncodingException { + if (pString == null) { + return null; + } + return new String(decode(StringUtils.getBytesUsAscii(pString)), charset); + } + + /** + * Decodes a quoted-printable string into its original form using the default string charset. Escaped characters are + * converted back to their original representation. + * + * @param pString + * quoted-printable string to convert into its original form + * @return original string + * @throws DecoderException + * Thrown if quoted-printable decoding is unsuccessful. + * Thrown if charset is not supported. + * @see #getDefaultCharset() + */ + public String decode(String pString) throws DecoderException { + if (pString == null) { + return null; + } + try { + return decode(pString, getDefaultCharset()); + } catch (UnsupportedEncodingException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Encodes an object into its quoted-printable safe form. Unsafe characters are escaped. + * + * @param pObject + * string to convert to a quoted-printable form + * @return quoted-printable object + * @throws EncoderException + * Thrown if quoted-printable encoding is not applicable to objects of this type or if encoding is + * unsuccessful + */ + public Object encode(Object pObject) throws EncoderException { + if (pObject == null) { + return null; + } else if (pObject instanceof byte[]) { + return encode((byte[]) pObject); + } else if (pObject instanceof String) { + return encode((String) pObject); + } else { + throw new EncoderException("Objects of type " + + pObject.getClass().getName() + + " cannot be quoted-printable encoded"); + } + } + + /** + * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original + * representation. + * + * @param pObject + * quoted-printable object to convert into its original form + * @return original object + * @throws DecoderException + * Thrown if the argument is not a String or byte[]. Thrown if a failure condition is + * encountered during the decode process. + */ + public Object decode(Object pObject) throws DecoderException { + if (pObject == null) { + return null; + } else if (pObject instanceof byte[]) { + return decode((byte[]) pObject); + } else if (pObject instanceof String) { + return decode((String) pObject); + } else { + throw new DecoderException("Objects of type " + + pObject.getClass().getName() + + " cannot be quoted-printable decoded"); + } + } + + /** + * Returns the default charset used for string decoding and encoding. + * + * @return the default string charset. + */ + public String getDefaultCharset() { + return this.charset; + } + + /** + * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped. + * + *

    + * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in + * RFC 1521 and is suitable for encoding binary data and unformatted text. + *

    + * + * @param pString + * string to convert to quoted-printable form + * @param charset + * the charset for pString + * @return quoted-printable string + * + * @throws UnsupportedEncodingException + * Thrown if the charset is not supported + */ + public String encode(String pString, String charset) throws UnsupportedEncodingException { + if (pString == null) { + return null; + } + return StringUtils.newStringUsAscii(encode(pString.getBytes(charset))); + } +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/RFC1522Codec.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/RFC1522Codec.java new file mode 100644 index 000000000..f11a450cb --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/RFC1522Codec.java @@ -0,0 +1,179 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.net; + +import java.io.UnsupportedEncodingException; + +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.binary.StringUtils; + +/** + *

    + * Implements methods common to all codecs defined in RFC 1522. + *

    + * + *

    + * RFC 1522 + * describes techniques to allow the encoding of non-ASCII text in + * various portions of a RFC 822 [2] message header, in a manner which + * is unlikely to confuse existing message handling software. + *

    + + * @see + * MIME (Multipurpose Internet Mail Extensions) Part Two: + * Message Header Extensions for Non-ASCII Text + *

    + * + * @author Apache Software Foundation + * @since 1.3 + * @version $Id: RFC1522Codec.java 798428 2009-07-28 07:32:49Z ggregory $ + */ +abstract class RFC1522Codec { + + /** + * Separator. + */ + protected static final char SEP = '?'; + + /** + * Prefix + */ + protected static final String POSTFIX = "?="; + + /** + * Postfix + */ + protected static final String PREFIX = "=?"; + + /** + * Applies an RFC 1522 compliant encoding scheme to the given string of text with the + * given charset. This method constructs the "encoded-word" header common to all the + * RFC 1522 codecs and then invokes {@link #doEncoding(byte [])} method of a concrete + * class to perform the specific enconding. + * + * @param text a string to encode + * @param charset a charset to be used + * + * @return RFC 1522 compliant "encoded-word" + * + * @throws EncoderException thrown if there is an error conidition during the Encoding + * process. + * @throws UnsupportedEncodingException thrown if charset is not supported + * + * @see Standard charsets + */ + protected String encodeText(final String text, final String charset) + throws EncoderException, UnsupportedEncodingException + { + if (text == null) { + return null; + } + StringBuffer buffer = new StringBuffer(); + buffer.append(PREFIX); + buffer.append(charset); + buffer.append(SEP); + buffer.append(getEncoding()); + buffer.append(SEP); + byte [] rawdata = doEncoding(text.getBytes(charset)); + buffer.append(StringUtils.newStringUsAscii(rawdata)); + buffer.append(POSTFIX); + return buffer.toString(); + } + + /** + * Applies an RFC 1522 compliant decoding scheme to the given string of text. This method + * processes the "encoded-word" header common to all the RFC 1522 codecs and then invokes + * {@link #doEncoding(byte [])} method of a concrete class to perform the specific deconding. + * + * @param text a string to decode + * @return A new decoded String or null if the input is null. + * + * @throws DecoderException thrown if there is an error conidition during the Decoding + * process. + * @throws UnsupportedEncodingException thrown if charset specified in the "encoded-word" + * header is not supported + */ + protected String decodeText(final String text) + throws DecoderException, UnsupportedEncodingException + { + if (text == null) { + return null; + } + if ((!text.startsWith(PREFIX)) || (!text.endsWith(POSTFIX))) { + throw new DecoderException("RFC 1522 violation: malformed encoded content"); + } + int terminator = text.length() - 2; + int from = 2; + int to = text.indexOf(SEP, from); + if (to == terminator) { + throw new DecoderException("RFC 1522 violation: charset token not found"); + } + String charset = text.substring(from, to); + if (charset.equals("")) { + throw new DecoderException("RFC 1522 violation: charset not specified"); + } + from = to + 1; + to = text.indexOf(SEP, from); + if (to == terminator) { + throw new DecoderException("RFC 1522 violation: encoding token not found"); + } + String encoding = text.substring(from, to); + if (!getEncoding().equalsIgnoreCase(encoding)) { + throw new DecoderException("This codec cannot decode " + + encoding + " encoded content"); + } + from = to + 1; + to = text.indexOf(SEP, from); + byte[] data = StringUtils.getBytesUsAscii(text.substring(from, to)); + data = doDecoding(data); + return new String(data, charset); + } + + /** + * Returns the codec name (referred to as encoding in the RFC 1522) + * + * @return name of the codec + */ + protected abstract String getEncoding(); + + /** + * Encodes an array of bytes using the defined encoding scheme + * + * @param bytes Data to be encoded + * + * @return A byte array containing the encoded data + * + * @throws EncoderException thrown if the Encoder encounters a failure condition + * during the encoding process. + */ + protected abstract byte[] doEncoding(byte[] bytes) throws EncoderException; + + /** + * Decodes an array of bytes using the defined encoding scheme + * + * @param bytes Data to be decoded + * + * @return a byte array that contains decoded data + * + * @throws DecoderException A decoder exception is thrown if a Decoder encounters a + * failure condition during the decode process. + */ + protected abstract byte[] doDecoding(byte[] bytes) throws DecoderException; +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/URLCodec.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/URLCodec.java new file mode 100644 index 000000000..74699310f --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/URLCodec.java @@ -0,0 +1,362 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.net; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.BitSet; + +import org.mozilla.apache.commons.codec.BinaryDecoder; +import org.mozilla.apache.commons.codec.BinaryEncoder; +import org.mozilla.apache.commons.codec.DecoderException; +import org.mozilla.apache.commons.codec.EncoderException; +import org.mozilla.apache.commons.codec.CharEncoding; +import org.mozilla.apache.commons.codec.StringDecoder; +import org.mozilla.apache.commons.codec.StringEncoder; +import org.mozilla.apache.commons.codec.binary.StringUtils; + +/** + *

    Implements the 'www-form-urlencoded' encoding scheme, + * also misleadingly known as URL encoding.

    + * + *

    For more detailed information please refer to + * + * Chapter 17.13.4 'Form content types' of the + * HTML 4.01 Specification

    + * + *

    + * This codec is meant to be a replacement for standard Java classes + * {@link java.net.URLEncoder} and {@link java.net.URLDecoder} + * on older Java platforms, as these classes in Java versions below + * 1.4 rely on the platform's default charset encoding. + *

    + * + * @author Apache Software Foundation + * @since 1.2 + * @version $Id: URLCodec.java 1079537 2011-03-08 20:56:19Z ggregory $ + */ +public class URLCodec implements BinaryEncoder, BinaryDecoder, StringEncoder, StringDecoder { + + /** + * Radix used in encoding and decoding. + */ + static final int RADIX = 16; + + /** + * The default charset used for string decoding and encoding. Consider this field final. The next major release may + * break compatibility and make this field be final. + */ + protected String charset; + + /** + * Release 1.5 made this field final. + */ + protected static final byte ESCAPE_CHAR = '%'; + /** + * BitSet of www-form-url safe characters. + */ + protected static final BitSet WWW_FORM_URL = new BitSet(256); + + // Static initializer for www_form_url + static { + // alpha characters + for (int i = 'a'; i <= 'z'; i++) { + WWW_FORM_URL.set(i); + } + for (int i = 'A'; i <= 'Z'; i++) { + WWW_FORM_URL.set(i); + } + // numeric characters + for (int i = '0'; i <= '9'; i++) { + WWW_FORM_URL.set(i); + } + // special chars + WWW_FORM_URL.set('-'); + WWW_FORM_URL.set('_'); + WWW_FORM_URL.set('.'); + WWW_FORM_URL.set('*'); + // blank to be replaced with + + WWW_FORM_URL.set(' '); + } + + + /** + * Default constructor. + */ + public URLCodec() { + this(CharEncoding.UTF_8); + } + + /** + * Constructor which allows for the selection of a default charset + * + * @param charset the default string charset to use. + */ + public URLCodec(String charset) { + super(); + this.charset = charset; + } + + /** + * Encodes an array of bytes into an array of URL safe 7-bit characters. Unsafe characters are escaped. + * + * @param urlsafe + * bitset of characters deemed URL safe + * @param bytes + * array of bytes to convert to URL safe characters + * @return array of bytes containing URL safe characters + */ + public static final byte[] encodeUrl(BitSet urlsafe, byte[] bytes) { + if (bytes == null) { + return null; + } + if (urlsafe == null) { + urlsafe = WWW_FORM_URL; + } + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i]; + if (b < 0) { + b = 256 + b; + } + if (urlsafe.get(b)) { + if (b == ' ') { + b = '+'; + } + buffer.write(b); + } else { + buffer.write(ESCAPE_CHAR); + char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX)); + char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX)); + buffer.write(hex1); + buffer.write(hex2); + } + } + return buffer.toByteArray(); + } + + /** + * Decodes an array of URL safe 7-bit characters into an array of + * original bytes. Escaped characters are converted back to their + * original representation. + * + * @param bytes array of URL safe characters + * @return array of original bytes + * @throws DecoderException Thrown if URL decoding is unsuccessful + */ + public static final byte[] decodeUrl(byte[] bytes) throws DecoderException { + if (bytes == null) { + return null; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i]; + if (b == '+') { + buffer.write(' '); + } else if (b == ESCAPE_CHAR) { + try { + int u = Utils.digit16(bytes[++i]); + int l = Utils.digit16(bytes[++i]); + buffer.write((char) ((u << 4) + l)); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DecoderException("Invalid URL encoding: ", e); + } + } else { + buffer.write(b); + } + } + return buffer.toByteArray(); + } + + /** + * Encodes an array of bytes into an array of URL safe 7-bit + * characters. Unsafe characters are escaped. + * + * @param bytes array of bytes to convert to URL safe characters + * @return array of bytes containing URL safe characters + */ + public byte[] encode(byte[] bytes) { + return encodeUrl(WWW_FORM_URL, bytes); + } + + + /** + * Decodes an array of URL safe 7-bit characters into an array of + * original bytes. Escaped characters are converted back to their + * original representation. + * + * @param bytes array of URL safe characters + * @return array of original bytes + * @throws DecoderException Thrown if URL decoding is unsuccessful + */ + public byte[] decode(byte[] bytes) throws DecoderException { + return decodeUrl(bytes); + } + + /** + * Encodes a string into its URL safe form using the specified string charset. Unsafe characters are escaped. + * + * @param pString + * string to convert to a URL safe form + * @param charset + * the charset for pString + * @return URL safe string + * @throws UnsupportedEncodingException + * Thrown if charset is not supported + */ + public String encode(String pString, String charset) throws UnsupportedEncodingException { + if (pString == null) { + return null; + } + return StringUtils.newStringUsAscii(encode(pString.getBytes(charset))); + } + + /** + * Encodes a string into its URL safe form using the default string + * charset. Unsafe characters are escaped. + * + * @param pString string to convert to a URL safe form + * @return URL safe string + * @throws EncoderException Thrown if URL encoding is unsuccessful + * + * @see #getDefaultCharset() + */ + public String encode(String pString) throws EncoderException { + if (pString == null) { + return null; + } + try { + return encode(pString, getDefaultCharset()); + } catch (UnsupportedEncodingException e) { + throw new EncoderException(e.getMessage(), e); + } + } + + + /** + * Decodes a URL safe string into its original form using the + * specified encoding. Escaped characters are converted back + * to their original representation. + * + * @param pString URL safe string to convert into its original form + * @param charset the original string charset + * @return original string + * @throws DecoderException Thrown if URL decoding is unsuccessful + * @throws UnsupportedEncodingException Thrown if charset is not + * supported + */ + public String decode(String pString, String charset) throws DecoderException, UnsupportedEncodingException { + if (pString == null) { + return null; + } + return new String(decode(StringUtils.getBytesUsAscii(pString)), charset); + } + + /** + * Decodes a URL safe string into its original form using the default + * string charset. Escaped characters are converted back to their + * original representation. + * + * @param pString URL safe string to convert into its original form + * @return original string + * @throws DecoderException Thrown if URL decoding is unsuccessful + * + * @see #getDefaultCharset() + */ + public String decode(String pString) throws DecoderException { + if (pString == null) { + return null; + } + try { + return decode(pString, getDefaultCharset()); + } catch (UnsupportedEncodingException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Encodes an object into its URL safe form. Unsafe characters are + * escaped. + * + * @param pObject string to convert to a URL safe form + * @return URL safe object + * @throws EncoderException Thrown if URL encoding is not + * applicable to objects of this type or + * if encoding is unsuccessful + */ + public Object encode(Object pObject) throws EncoderException { + if (pObject == null) { + return null; + } else if (pObject instanceof byte[]) { + return encode((byte[])pObject); + } else if (pObject instanceof String) { + return encode((String)pObject); + } else { + throw new EncoderException("Objects of type " + + pObject.getClass().getName() + " cannot be URL encoded"); + + } + } + + /** + * Decodes a URL safe object into its original form. Escaped characters are converted back to their original + * representation. + * + * @param pObject + * URL safe object to convert into its original form + * @return original object + * @throws DecoderException + * Thrown if the argument is not a String or byte[]. Thrown if a failure condition is + * encountered during the decode process. + */ + public Object decode(Object pObject) throws DecoderException { + if (pObject == null) { + return null; + } else if (pObject instanceof byte[]) { + return decode((byte[]) pObject); + } else if (pObject instanceof String) { + return decode((String) pObject); + } else { + throw new DecoderException("Objects of type " + pObject.getClass().getName() + " cannot be URL decoded"); + + } + } + + /** + * The String encoding used for decoding and encoding. + * + * @return Returns the encoding. + * + * @deprecated Use {@link #getDefaultCharset()}, will be removed in 2.0. + */ + public String getEncoding() { + return this.charset; + } + + /** + * The default charset used for string decoding and encoding. + * + * @return the default string charset. + */ + public String getDefaultCharset() { + return this.charset; + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/Utils.java b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/Utils.java new file mode 100644 index 000000000..adfe84513 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/Utils.java @@ -0,0 +1,50 @@ +// Mozilla has modified this file - see http://hg.mozilla.org/ for details. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mozilla.apache.commons.codec.net; + +import org.mozilla.apache.commons.codec.DecoderException; + +/** + * Utility methods for this package. + * + * @author
    Gary Gregory + * @version $Id: Utils.java 798611 2009-07-28 17:10:44Z ggregory $ + * @since 1.4 + */ +class Utils { + + /** + * Returns the numeric value of the character b in radix 16. + * + * @param b + * The byte to be converted. + * @return The numeric value represented by the character in radix 16. + * + * @throws DecoderException + * Thrown when the byte is not valid per {@link Character#digit(char,int)} + */ + static int digit16(byte b) throws DecoderException { + int i = Character.digit((char) b, 16); + if (i == -1) { + throw new DecoderException("Invalid URL encoding: not a valid digit (radix " + URLCodec.RADIX + "): " + b); + } + return i; + } + +} diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/package.html b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/package.html new file mode 100644 index 000000000..2b8ceab2e --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/net/package.html @@ -0,0 +1,23 @@ + + + +

    + Network related encoding and decoding. +

    + + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/overview.html b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/overview.html new file mode 100644 index 000000000..29939dba5 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/overview.html @@ -0,0 +1,29 @@ + + + + +

    +This document is the API specification for the Apache Commons Codec Library, version 1.3. +

    +

    +This library requires a JRE version of 1.2.2 or greater. +The hypertext links originating from this document point to Sun's version 1.3 API as the 1.2.2 API documentation +is no longer on-line. +

    + + diff --git a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/package.html b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/package.html new file mode 100644 index 000000000..da4a3ea52 --- /dev/null +++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/package.html @@ -0,0 +1,100 @@ + + + + + + +

    Interfaces and classes used by + the various implementations in the sub-packages.

    + +

    Definitive implementations of commonly used encoders and decoders.

    + +

    Codec is currently comprised of a modest set of utilities and a + simple framework for String encoding and decoding in three categories: + Binary Encoders, Language Encoders, and Network Encoders.

    + +

    Binary Encoders

    + + + + + + + + + + + + + + +
    + + org.apache.commons.codec.binary.Base64 + + Provides Base64 content-transfer-encoding as defined in + RFC 2045 + Production
    + + org.apache.commons.codec.binary.Hex + + Converts an array of bytes into an array of characters + representing the hexidecimal values of each byte in order + Production
    +

    + Language Encoders +

    +

    + Codec contains a number of commonly used language and phonetic + encoders +

    + + + + + + + + + + + + + +
    + org.apache.commons.codec.language.Soundex + Implementation of the Soundex algorithm.Production
    + org.apache.commons.codec.language.Metaphone + Implementation of the Metaphone algorithm.Production
    +

    Network Encoders

    +

    +

    Codec contains network related encoders

    + + + + + + + + +
    + org.apache.commons.codec.net.URLCodec + Implements the 'www-form-urlencoded' encoding scheme.Production
    +
    + + -- cgit v1.2.3