summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/org/apache/commons/codec/BinaryDecoder.java41
-rw-r--r--src/org/apache/commons/codec/BinaryEncoder.java41
-rw-r--r--src/org/apache/commons/codec/Decoder.java54
-rw-r--r--src/org/apache/commons/codec/DecoderException.java37
-rw-r--r--src/org/apache/commons/codec/Encoder.java45
-rw-r--r--src/org/apache/commons/codec/EncoderException.java39
-rw-r--r--src/org/apache/commons/codec/StringDecoder.java39
-rw-r--r--src/org/apache/commons/codec/StringEncoder.java39
-rw-r--r--src/org/apache/commons/codec/StringEncoderComparator.java83
-rw-r--r--src/org/apache/commons/codec/binary/Base64.java524
-rw-r--r--src/org/apache/commons/codec/binary/BinaryCodec.java285
-rw-r--r--src/org/apache/commons/codec/binary/Hex.java192
-rw-r--r--src/org/apache/commons/codec/binary/package.html20
-rw-r--r--src/org/apache/commons/codec/language/DoubleMetaphone.java1103
-rw-r--r--src/org/apache/commons/codec/language/Metaphone.java399
-rw-r--r--src/org/apache/commons/codec/language/RefinedSoundex.java186
-rw-r--r--src/org/apache/commons/codec/language/Soundex.java274
-rw-r--r--src/org/apache/commons/codec/language/SoundexUtils.java122
-rw-r--r--src/org/apache/commons/codec/language/package.html20
-rw-r--r--src/org/apache/commons/codec/net/BCodec.java207
-rw-r--r--src/org/apache/commons/codec/net/QCodec.java309
-rw-r--r--src/org/apache/commons/codec/net/QuotedPrintableCodec.java387
-rw-r--r--src/org/apache/commons/codec/net/RFC1522Codec.java161
-rw-r--r--src/org/apache/commons/codec/net/StringEncodings.java52
-rw-r--r--src/org/apache/commons/codec/net/URLCodec.java364
-rw-r--r--src/org/apache/commons/codec/net/package.html22
-rw-r--r--src/org/apache/commons/codec/overview.html28
-rw-r--r--src/org/apache/commons/codec/package.html99
-rw-r--r--src/org/apache/commons/logging/Log.java245
-rw-r--r--src/org/apache/commons/logging/LogConfigurationException.java97
-rw-r--r--src/org/apache/commons/logging/LogFactory.java1740
-rw-r--r--src/org/apache/commons/logging/LogSource.java261
-rw-r--r--src/org/apache/commons/logging/impl/Jdk14Logger.java303
-rw-r--r--src/org/apache/commons/logging/impl/LogFactoryImpl.java1400
-rw-r--r--src/org/apache/commons/logging/impl/NoOpLog.java106
-rw-r--r--src/org/apache/commons/logging/impl/SimpleLog.java709
-rw-r--r--src/org/apache/commons/logging/impl/WeakHashtable.java478
-rw-r--r--src/org/apache/commons/logging/impl/package.html21
-rw-r--r--src/org/apache/commons/logging/package.html254
-rw-r--r--src/org/apache/http/ConnectionClosedException.java58
-rw-r--r--src/org/apache/http/ConnectionReuseStrategy.java75
-rw-r--r--src/org/apache/http/FormattedHeader.java68
-rw-r--r--src/org/apache/http/Header.java64
-rw-r--r--src/org/apache/http/HeaderElement.java59
-rw-r--r--src/org/apache/http/HeaderElementIterator.java61
-rw-r--r--src/org/apache/http/HeaderIterator.java64
-rw-r--r--src/org/apache/http/HttpClientConnection.java112
-rw-r--r--src/org/apache/http/HttpConnection.java110
-rw-r--r--src/org/apache/http/HttpConnectionMetrics.java79
-rw-r--r--src/org/apache/http/HttpEntity.java196
-rw-r--r--src/org/apache/http/HttpEntityEnclosingRequest.java63
-rw-r--r--src/org/apache/http/HttpException.java75
-rw-r--r--src/org/apache/http/HttpHost.java218
-rw-r--r--src/org/apache/http/HttpInetConnection.java55
-rw-r--r--src/org/apache/http/HttpMessage.java191
-rw-r--r--src/org/apache/http/HttpRequest.java51
-rw-r--r--src/org/apache/http/HttpRequestFactory.java51
-rw-r--r--src/org/apache/http/HttpRequestInterceptor.java69
-rw-r--r--src/org/apache/http/HttpResponse.java161
-rw-r--r--src/org/apache/http/HttpResponseFactory.java76
-rw-r--r--src/org/apache/http/HttpResponseInterceptor.java68
-rw-r--r--src/org/apache/http/HttpServerConnection.java96
-rw-r--r--src/org/apache/http/HttpStatus.java182
-rw-r--r--src/org/apache/http/HttpVersion.java104
-rw-r--r--src/org/apache/http/MalformedChunkCodingException.java59
-rw-r--r--src/org/apache/http/MethodNotSupportedException.java67
-rw-r--r--src/org/apache/http/NameValuePair.java108
-rw-r--r--src/org/apache/http/NoHttpResponseException.java58
-rw-r--r--src/org/apache/http/ParseException.java65
-rw-r--r--src/org/apache/http/ProtocolException.java72
-rw-r--r--src/org/apache/http/ProtocolVersion.java287
-rw-r--r--src/org/apache/http/ReasonPhraseCatalog.java63
-rw-r--r--src/org/apache/http/RequestLine.java53
-rw-r--r--src/org/apache/http/StatusLine.java55
-rw-r--r--src/org/apache/http/TokenIterator.java67
-rw-r--r--src/org/apache/http/UnsupportedHttpVersionException.java64
-rw-r--r--src/org/apache/http/auth/AUTH.java66
-rw-r--r--src/org/apache/http/auth/AuthScheme.java140
-rw-r--r--src/org/apache/http/auth/AuthSchemeFactory.java46
-rw-r--r--src/org/apache/http/auth/AuthSchemeRegistry.java149
-rw-r--r--src/org/apache/http/auth/AuthScope.java292
-rw-r--r--src/org/apache/http/auth/AuthState.java147
-rw-r--r--src/org/apache/http/auth/AuthenticationException.java73
-rw-r--r--src/org/apache/http/auth/BasicUserPrincipal.java90
-rw-r--r--src/org/apache/http/auth/Credentials.java49
-rw-r--r--src/org/apache/http/auth/InvalidCredentialsException.java71
-rw-r--r--src/org/apache/http/auth/MalformedChallengeException.java73
-rw-r--r--src/org/apache/http/auth/NTCredentials.java180
-rw-r--r--src/org/apache/http/auth/NTUserPrincipal.java113
-rw-r--r--src/org/apache/http/auth/UsernamePasswordCredentials.java126
-rw-r--r--src/org/apache/http/auth/package.html41
-rw-r--r--src/org/apache/http/auth/params/AuthPNames.java56
-rw-r--r--src/org/apache/http/auth/params/AuthParamBean.java47
-rw-r--r--src/org/apache/http/auth/params/AuthParams.java94
-rw-r--r--src/org/apache/http/auth/params/package.html40
-rw-r--r--src/org/apache/http/client/AuthenticationHandler.java61
-rw-r--r--src/org/apache/http/client/CircularRedirectException.java70
-rw-r--r--src/org/apache/http/client/ClientProtocolException.java60
-rw-r--r--src/org/apache/http/client/CookieStore.java76
-rw-r--r--src/org/apache/http/client/CredentialsProvider.java72
-rw-r--r--src/org/apache/http/client/HttpClient.java249
-rw-r--r--src/org/apache/http/client/HttpRequestRetryHandler.java66
-rw-r--r--src/org/apache/http/client/HttpResponseException.java51
-rw-r--r--src/org/apache/http/client/NonRepeatableRequestException.java63
-rw-r--r--src/org/apache/http/client/RedirectException.java72
-rw-r--r--src/org/apache/http/client/RedirectHandler.java79
-rw-r--r--src/org/apache/http/client/RequestDirector.java92
-rw-r--r--src/org/apache/http/client/ResponseHandler.java59
-rw-r--r--src/org/apache/http/client/UserTokenHandler.java64
-rw-r--r--src/org/apache/http/client/entity/UrlEncodedFormEntity.java76
-rw-r--r--src/org/apache/http/client/methods/AbortableHttpRequest.java91
-rw-r--r--src/org/apache/http/client/methods/HttpDelete.java76
-rw-r--r--src/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java81
-rw-r--r--src/org/apache/http/client/methods/HttpGet.java83
-rw-r--r--src/org/apache/http/client/methods/HttpHead.java83
-rw-r--r--src/org/apache/http/client/methods/HttpOptions.java105
-rw-r--r--src/org/apache/http/client/methods/HttpPost.java87
-rw-r--r--src/org/apache/http/client/methods/HttpPut.java79
-rw-r--r--src/org/apache/http/client/methods/HttpRequestBase.java182
-rw-r--r--src/org/apache/http/client/methods/HttpTrace.java82
-rw-r--r--src/org/apache/http/client/methods/HttpUriRequest.java80
-rw-r--r--src/org/apache/http/client/methods/package.html40
-rw-r--r--src/org/apache/http/client/package.html41
-rw-r--r--src/org/apache/http/client/params/AllClientPNames.java65
-rw-r--r--src/org/apache/http/client/params/AuthPolicy.java58
-rw-r--r--src/org/apache/http/client/params/ClientPNames.java139
-rw-r--r--src/org/apache/http/client/params/ClientParamBean.java92
-rw-r--r--src/org/apache/http/client/params/CookiePolicy.java66
-rw-r--r--src/org/apache/http/client/params/HttpClientParams.java101
-rw-r--r--src/org/apache/http/client/params/package.html40
-rw-r--r--src/org/apache/http/client/protocol/ClientContext.java52
-rw-r--r--src/org/apache/http/client/protocol/ClientContextConfigurer.java72
-rw-r--r--src/org/apache/http/client/protocol/RequestAddCookies.java192
-rw-r--r--src/org/apache/http/client/protocol/RequestDefaultHeaders.java74
-rw-r--r--src/org/apache/http/client/protocol/RequestProxyAuthentication.java104
-rw-r--r--src/org/apache/http/client/protocol/RequestTargetAuthentication.java105
-rw-r--r--src/org/apache/http/client/protocol/ResponseProcessCookies.java146
-rw-r--r--src/org/apache/http/client/protocol/package.html40
-rw-r--r--src/org/apache/http/client/utils/CloneUtils.java75
-rw-r--r--src/org/apache/http/client/utils/URIUtils.java209
-rw-r--r--src/org/apache/http/client/utils/URLEncodedUtils.java191
-rw-r--r--src/org/apache/http/client/utils/package.html40
-rw-r--r--src/org/apache/http/conn/BasicEofSensorWatcher.java121
-rw-r--r--src/org/apache/http/conn/BasicManagedEntity.java220
-rw-r--r--src/org/apache/http/conn/ClientConnectionManager.java129
-rw-r--r--src/org/apache/http/conn/ClientConnectionManagerFactory.java50
-rw-r--r--src/org/apache/http/conn/ClientConnectionOperator.java120
-rw-r--r--src/org/apache/http/conn/ClientConnectionRequest.java73
-rw-r--r--src/org/apache/http/conn/ConnectTimeoutException.java64
-rw-r--r--src/org/apache/http/conn/ConnectionKeepAliveStrategy.java71
-rw-r--r--src/org/apache/http/conn/ConnectionPoolTimeoutException.java62
-rw-r--r--src/org/apache/http/conn/ConnectionReleaseTrigger.java86
-rw-r--r--src/org/apache/http/conn/EofSensorInputStream.java326
-rw-r--r--src/org/apache/http/conn/EofSensorWatcher.java112
-rw-r--r--src/org/apache/http/conn/HttpHostConnectException.java57
-rw-r--r--src/org/apache/http/conn/ManagedClientConnection.java261
-rw-r--r--src/org/apache/http/conn/MultihomePlainSocketFactory.java212
-rw-r--r--src/org/apache/http/conn/OperatedClientConnection.java174
-rw-r--r--src/org/apache/http/conn/package.html91
-rw-r--r--src/org/apache/http/conn/params/ConnConnectionPNames.java65
-rw-r--r--src/org/apache/http/conn/params/ConnConnectionParamBean.java55
-rw-r--r--src/org/apache/http/conn/params/ConnManagerPNames.java73
-rw-r--r--src/org/apache/http/conn/params/ConnManagerParamBean.java62
-rw-r--r--src/org/apache/http/conn/params/ConnManagerParams.java169
-rw-r--r--src/org/apache/http/conn/params/ConnPerRoute.java51
-rw-r--r--src/org/apache/http/conn/params/ConnPerRouteBean.java114
-rw-r--r--src/org/apache/http/conn/params/ConnRoutePNames.java84
-rw-r--r--src/org/apache/http/conn/params/ConnRouteParamBean.java67
-rw-r--r--src/org/apache/http/conn/params/ConnRouteParams.java203
-rw-r--r--src/org/apache/http/conn/params/package.html40
-rw-r--r--src/org/apache/http/conn/routing/BasicRouteDirector.java181
-rw-r--r--src/org/apache/http/conn/routing/HttpRoute.java443
-rw-r--r--src/org/apache/http/conn/routing/HttpRouteDirector.java88
-rw-r--r--src/org/apache/http/conn/routing/HttpRoutePlanner.java69
-rw-r--r--src/org/apache/http/conn/routing/RouteInfo.java194
-rw-r--r--src/org/apache/http/conn/routing/RouteTracker.java439
-rw-r--r--src/org/apache/http/conn/routing/package.html62
-rw-r--r--src/org/apache/http/conn/scheme/HostNameResolver.java41
-rw-r--r--src/org/apache/http/conn/scheme/LayeredSocketFactory.java72
-rw-r--r--src/org/apache/http/conn/scheme/PlainSocketFactory.java182
-rw-r--r--src/org/apache/http/conn/scheme/Scheme.java223
-rw-r--r--src/org/apache/http/conn/scheme/SchemeRegistry.java187
-rw-r--r--src/org/apache/http/conn/scheme/SocketFactory.java138
-rw-r--r--src/org/apache/http/conn/ssl/AbstractVerifier.java343
-rw-r--r--src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java54
-rw-r--r--src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java62
-rw-r--r--src/org/apache/http/conn/ssl/SSLSocketFactory.java384
-rw-r--r--src/org/apache/http/conn/ssl/StrictHostnameVerifier.java69
-rw-r--r--src/org/apache/http/conn/ssl/X509HostnameVerifier.java86
-rw-r--r--src/org/apache/http/conn/ssl/package.html40
-rw-r--r--src/org/apache/http/conn/util/InetAddressUtils.java72
-rw-r--r--src/org/apache/http/cookie/ClientCookie.java67
-rw-r--r--src/org/apache/http/cookie/Cookie.java139
-rw-r--r--src/org/apache/http/cookie/CookieAttributeHandler.java78
-rw-r--r--src/org/apache/http/cookie/CookieIdentityComparator.java68
-rw-r--r--src/org/apache/http/cookie/CookieOrigin.java108
-rw-r--r--src/org/apache/http/cookie/CookiePathComparator.java81
-rw-r--r--src/org/apache/http/cookie/CookieSpec.java115
-rw-r--r--src/org/apache/http/cookie/CookieSpecFactory.java46
-rw-r--r--src/org/apache/http/cookie/CookieSpecRegistry.java160
-rw-r--r--src/org/apache/http/cookie/MalformedCookieException.java74
-rw-r--r--src/org/apache/http/cookie/SM.java48
-rw-r--r--src/org/apache/http/cookie/SetCookie.java115
-rw-r--r--src/org/apache/http/cookie/SetCookie2.java66
-rw-r--r--src/org/apache/http/cookie/package.html41
-rw-r--r--src/org/apache/http/cookie/params/CookieSpecPNames.java68
-rw-r--r--src/org/apache/http/cookie/params/CookieSpecParamBean.java53
-rw-r--r--src/org/apache/http/cookie/params/package.html40
-rw-r--r--src/org/apache/http/entity/AbstractHttpEntity.java213
-rw-r--r--src/org/apache/http/entity/BasicHttpEntity.java146
-rw-r--r--src/org/apache/http/entity/BufferedHttpEntity.java120
-rw-r--r--src/org/apache/http/entity/ByteArrayEntity.java94
-rw-r--r--src/org/apache/http/entity/ContentLengthStrategy.java54
-rw-r--r--src/org/apache/http/entity/ContentProducer.java53
-rw-r--r--src/org/apache/http/entity/EntityTemplate.java86
-rw-r--r--src/org/apache/http/entity/FileEntity.java106
-rw-r--r--src/org/apache/http/entity/HttpEntityWrapper.java113
-rw-r--r--src/org/apache/http/entity/InputStreamEntity.java116
-rw-r--r--src/org/apache/http/entity/SerializableEntity.java107
-rw-r--r--src/org/apache/http/entity/StringEntity.java106
-rw-r--r--src/org/apache/http/entity/package.html58
-rw-r--r--src/org/apache/http/impl/AbstractHttpClientConnection.java212
-rw-r--r--src/org/apache/http/impl/AbstractHttpServerConnection.java202
-rw-r--r--src/org/apache/http/impl/DefaultConnectionReuseStrategy.java182
-rw-r--r--src/org/apache/http/impl/DefaultHttpClientConnection.java87
-rw-r--r--src/org/apache/http/impl/DefaultHttpRequestFactory.java113
-rw-r--r--src/org/apache/http/impl/DefaultHttpResponseFactory.java121
-rw-r--r--src/org/apache/http/impl/DefaultHttpServerConnection.java85
-rw-r--r--src/org/apache/http/impl/EnglishReasonPhraseCatalog.java234
-rw-r--r--src/org/apache/http/impl/HttpConnectionMetricsImpl.java146
-rw-r--r--src/org/apache/http/impl/NoConnectionReuseStrategy.java65
-rw-r--r--src/org/apache/http/impl/SocketHttpClientConnection.java208
-rw-r--r--src/org/apache/http/impl/SocketHttpServerConnection.java202
-rw-r--r--src/org/apache/http/impl/auth/AuthSchemeBase.java128
-rw-r--r--src/org/apache/http/impl/auth/BasicScheme.java185
-rw-r--r--src/org/apache/http/impl/auth/BasicSchemeFactory.java50
-rw-r--r--src/org/apache/http/impl/auth/DigestScheme.java484
-rw-r--r--src/org/apache/http/impl/auth/DigestSchemeFactory.java50
-rw-r--r--src/org/apache/http/impl/auth/NTLMEngine.java76
-rw-r--r--src/org/apache/http/impl/auth/NTLMEngineException.java70
-rw-r--r--src/org/apache/http/impl/auth/NTLMScheme.java149
-rw-r--r--src/org/apache/http/impl/auth/RFC2617Scheme.java119
-rw-r--r--src/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java71
-rw-r--r--src/org/apache/http/impl/auth/package.html4
-rw-r--r--src/org/apache/http/impl/client/AbstractAuthenticationHandler.java165
-rw-r--r--src/org/apache/http/impl/client/AbstractHttpClient.java697
-rw-r--r--src/org/apache/http/impl/client/BasicCookieStore.java162
-rw-r--r--src/org/apache/http/impl/client/BasicCredentialsProvider.java143
-rw-r--r--src/org/apache/http/impl/client/BasicResponseHandler.java79
-rw-r--r--src/org/apache/http/impl/client/ClientParamsStack.java282
-rw-r--r--src/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java76
-rw-r--r--src/org/apache/http/impl/client/DefaultHttpClient.java332
-rw-r--r--src/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java134
-rw-r--r--src/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java72
-rw-r--r--src/org/apache/http/impl/client/DefaultRedirectHandler.java183
-rw-r--r--src/org/apache/http/impl/client/DefaultRequestDirector.java1088
-rw-r--r--src/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java72
-rw-r--r--src/org/apache/http/impl/client/DefaultUserTokenHandler.java88
-rw-r--r--src/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java83
-rw-r--r--src/org/apache/http/impl/client/RedirectLocations.java71
-rw-r--r--src/org/apache/http/impl/client/RequestWrapper.java170
-rw-r--r--src/org/apache/http/impl/client/RoutedRequest.java73
-rw-r--r--src/org/apache/http/impl/client/TunnelRefusedException.java52
-rw-r--r--src/org/apache/http/impl/client/package.html4
-rw-r--r--src/org/apache/http/impl/conn/AbstractClientConnAdapter.java399
-rw-r--r--src/org/apache/http/impl/conn/AbstractPoolEntry.java322
-rw-r--r--src/org/apache/http/impl/conn/AbstractPooledConnAdapter.java188
-rw-r--r--src/org/apache/http/impl/conn/DefaultClientConnection.java259
-rw-r--r--src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java216
-rw-r--r--src/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java121
-rw-r--r--src/org/apache/http/impl/conn/DefaultResponseParser.java103
-rw-r--r--src/org/apache/http/impl/conn/IdleConnectionHandler.java190
-rw-r--r--src/org/apache/http/impl/conn/LoggingSessionInputBuffer.java117
-rw-r--r--src/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java109
-rw-r--r--src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java290
-rw-r--r--src/org/apache/http/impl/conn/SingleClientConnManager.java444
-rw-r--r--src/org/apache/http/impl/conn/Wire.java160
-rw-r--r--src/org/apache/http/impl/conn/package.html5
-rw-r--r--src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java332
-rw-r--r--src/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java88
-rw-r--r--src/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java80
-rw-r--r--src/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java83
-rw-r--r--src/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java698
-rw-r--r--src/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java68
-rw-r--r--src/org/apache/http/impl/conn/tsccm/RefQueueHandler.java48
-rw-r--r--src/org/apache/http/impl/conn/tsccm/RefQueueWorker.java139
-rw-r--r--src/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java301
-rw-r--r--src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java282
-rw-r--r--src/org/apache/http/impl/conn/tsccm/WaitingThread.java197
-rw-r--r--src/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java62
-rw-r--r--src/org/apache/http/impl/conn/tsccm/doc-files/tsccm-structure.pngbin0 -> 4224 bytes
-rw-r--r--src/org/apache/http/impl/conn/tsccm/package.html205
-rw-r--r--src/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java50
-rw-r--r--src/org/apache/http/impl/cookie/AbstractCookieSpec.java110
-rw-r--r--src/org/apache/http/impl/cookie/BasicClientCookie.java376
-rw-r--r--src/org/apache/http/impl/cookie/BasicClientCookie2.java101
-rw-r--r--src/org/apache/http/impl/cookie/BasicCommentHandler.java50
-rw-r--r--src/org/apache/http/impl/cookie/BasicDomainHandler.java122
-rw-r--r--src/org/apache/http/impl/cookie/BasicExpiresHandler.java65
-rw-r--r--src/org/apache/http/impl/cookie/BasicMaxAgeHandler.java66
-rw-r--r--src/org/apache/http/impl/cookie/BasicPathHandler.java91
-rw-r--r--src/org/apache/http/impl/cookie/BasicSecureHandler.java63
-rw-r--r--src/org/apache/http/impl/cookie/BestMatchSpec.java186
-rw-r--r--src/org/apache/http/impl/cookie/BestMatchSpecFactory.java57
-rw-r--r--src/org/apache/http/impl/cookie/BrowserCompatSpec.java188
-rw-r--r--src/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java56
-rw-r--r--src/org/apache/http/impl/cookie/CookieSpecBase.java131
-rw-r--r--src/org/apache/http/impl/cookie/DateParseException.java60
-rw-r--r--src/org/apache/http/impl/cookie/DateUtils.java261
-rw-r--r--src/org/apache/http/impl/cookie/NetscapeDomainHandler.java106
-rw-r--r--src/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java78
-rw-r--r--src/org/apache/http/impl/cookie/NetscapeDraftSpec.java182
-rw-r--r--src/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java56
-rw-r--r--src/org/apache/http/impl/cookie/RFC2109DomainHandler.java126
-rw-r--r--src/org/apache/http/impl/cookie/RFC2109Spec.java246
-rw-r--r--src/org/apache/http/impl/cookie/RFC2109SpecFactory.java57
-rw-r--r--src/org/apache/http/impl/cookie/RFC2109VersionHandler.java74
-rw-r--r--src/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java66
-rw-r--r--src/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java66
-rw-r--r--src/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java195
-rw-r--r--src/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java168
-rw-r--r--src/org/apache/http/impl/cookie/RFC2965Spec.java259
-rw-r--r--src/org/apache/http/impl/cookie/RFC2965SpecFactory.java57
-rw-r--r--src/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java96
-rw-r--r--src/org/apache/http/impl/cookie/package.html4
-rw-r--r--src/org/apache/http/impl/entity/EntityDeserializer.java115
-rw-r--r--src/org/apache/http/impl/entity/EntitySerializer.java101
-rw-r--r--src/org/apache/http/impl/entity/LaxContentLengthStrategy.java260
-rw-r--r--src/org/apache/http/impl/entity/StrictContentLengthStrategy.java220
-rw-r--r--src/org/apache/http/impl/entity/package.html41
-rw-r--r--src/org/apache/http/impl/io/AbstractMessageParser.java187
-rw-r--r--src/org/apache/http/impl/io/AbstractMessageWriter.java85
-rw-r--r--src/org/apache/http/impl/io/AbstractSessionInputBuffer.java271
-rw-r--r--src/org/apache/http/impl/io/AbstractSessionOutputBuffer.java179
-rw-r--r--src/org/apache/http/impl/io/ChunkedInputStream.java294
-rw-r--r--src/org/apache/http/impl/io/ChunkedOutputStream.java191
-rw-r--r--src/org/apache/http/impl/io/ContentLengthInputStream.java220
-rw-r--r--src/org/apache/http/impl/io/ContentLengthOutputStream.java132
-rw-r--r--src/org/apache/http/impl/io/HttpRequestParser.java80
-rw-r--r--src/org/apache/http/impl/io/HttpRequestWriter.java59
-rw-r--r--src/org/apache/http/impl/io/HttpResponseParser.java81
-rw-r--r--src/org/apache/http/impl/io/HttpResponseWriter.java59
-rw-r--r--src/org/apache/http/impl/io/HttpTransportMetricsImpl.java63
-rw-r--r--src/org/apache/http/impl/io/IdentityInputStream.java90
-rw-r--r--src/org/apache/http/impl/io/IdentityOutputStream.java100
-rw-r--r--src/org/apache/http/impl/io/SocketInputBuffer.java115
-rw-r--r--src/org/apache/http/impl/io/SocketOutputBuffer.java79
-rw-r--r--src/org/apache/http/impl/io/package.html48
-rw-r--r--src/org/apache/http/impl/package.html41
-rw-r--r--src/org/apache/http/io/HttpMessageParser.java53
-rw-r--r--src/org/apache/http/io/HttpMessageWriter.java53
-rw-r--r--src/org/apache/http/io/HttpTransportMetrics.java46
-rw-r--r--src/org/apache/http/io/SessionInputBuffer.java63
-rw-r--r--src/org/apache/http/io/SessionOutputBuffer.java63
-rw-r--r--src/org/apache/http/io/package.html47
-rw-r--r--src/org/apache/http/message/AbstractHttpMessage.java166
-rw-r--r--src/org/apache/http/message/BasicHeader.java143
-rw-r--r--src/org/apache/http/message/BasicHeaderElement.java243
-rw-r--r--src/org/apache/http/message/BasicHeaderElementIterator.java161
-rw-r--r--src/org/apache/http/message/BasicHeaderIterator.java180
-rw-r--r--src/org/apache/http/message/BasicHeaderValueFormatter.java441
-rw-r--r--src/org/apache/http/message/BasicHeaderValueParser.java420
-rw-r--r--src/org/apache/http/message/BasicHttpEntityEnclosingRequest.java81
-rw-r--r--src/org/apache/http/message/BasicHttpRequest.java98
-rw-r--r--src/org/apache/http/message/BasicHttpResponse.java204
-rw-r--r--src/org/apache/http/message/BasicLineFormatter.java346
-rw-r--r--src/org/apache/http/message/BasicLineParser.java504
-rw-r--r--src/org/apache/http/message/BasicListHeaderIterator.java196
-rw-r--r--src/org/apache/http/message/BasicNameValuePair.java189
-rw-r--r--src/org/apache/http/message/BasicRequestLine.java99
-rw-r--r--src/org/apache/http/message/BasicStatusLine.java124
-rw-r--r--src/org/apache/http/message/BasicTokenIterator.java429
-rw-r--r--src/org/apache/http/message/BufferedHeader.java133
-rw-r--r--src/org/apache/http/message/HeaderGroup.java295
-rw-r--r--src/org/apache/http/message/HeaderValueFormatter.java141
-rw-r--r--src/org/apache/http/message/HeaderValueParser.java214
-rw-r--r--src/org/apache/http/message/LineFormatter.java153
-rw-r--r--src/org/apache/http/message/LineParser.java156
-rw-r--r--src/org/apache/http/message/ParserCursor.java102
-rw-r--r--src/org/apache/http/message/package.html49
-rw-r--r--src/org/apache/http/package.html51
-rw-r--r--src/org/apache/http/params/AbstractHttpParams.java116
-rw-r--r--src/org/apache/http/params/BasicHttpParams.java160
-rw-r--r--src/org/apache/http/params/CoreConnectionPNames.java131
-rw-r--r--src/org/apache/http/params/CoreProtocolPNames.java132
-rw-r--r--src/org/apache/http/params/DefaultedHttpParams.java101
-rw-r--r--src/org/apache/http/params/HttpAbstractParamBean.java44
-rw-r--r--src/org/apache/http/params/HttpConnectionParamBean.java64
-rw-r--r--src/org/apache/http/params/HttpConnectionParams.java224
-rw-r--r--src/org/apache/http/params/HttpParams.java187
-rw-r--r--src/org/apache/http/params/HttpProtocolParamBean.java62
-rw-r--r--src/org/apache/http/params/HttpProtocolParams.java176
-rw-r--r--src/org/apache/http/params/package.html40
-rw-r--r--src/org/apache/http/protocol/BasicHttpContext.java95
-rw-r--r--src/org/apache/http/protocol/BasicHttpProcessor.java337
-rw-r--r--src/org/apache/http/protocol/DefaultedHttpContext.java79
-rw-r--r--src/org/apache/http/protocol/ExecutionContext.java52
-rw-r--r--src/org/apache/http/protocol/HTTP.java99
-rw-r--r--src/org/apache/http/protocol/HttpContext.java58
-rw-r--r--src/org/apache/http/protocol/HttpDateGenerator.java81
-rw-r--r--src/org/apache/http/protocol/HttpExpectationVerifier.java73
-rw-r--r--src/org/apache/http/protocol/HttpProcessor.java56
-rw-r--r--src/org/apache/http/protocol/HttpRequestExecutor.java322
-rw-r--r--src/org/apache/http/protocol/HttpRequestHandler.java53
-rw-r--r--src/org/apache/http/protocol/HttpRequestHandlerRegistry.java82
-rw-r--r--src/org/apache/http/protocol/HttpRequestHandlerResolver.java46
-rw-r--r--src/org/apache/http/protocol/HttpRequestInterceptorList.java120
-rw-r--r--src/org/apache/http/protocol/HttpResponseInterceptorList.java121
-rw-r--r--src/org/apache/http/protocol/HttpService.java249
-rw-r--r--src/org/apache/http/protocol/RequestConnControl.java67
-rw-r--r--src/org/apache/http/protocol/RequestContent.java101
-rw-r--r--src/org/apache/http/protocol/RequestDate.java72
-rw-r--r--src/org/apache/http/protocol/RequestExpectContinue.java78
-rw-r--r--src/org/apache/http/protocol/RequestTargetHost.java98
-rw-r--r--src/org/apache/http/protocol/RequestUserAgent.java69
-rw-r--r--src/org/apache/http/protocol/ResponseConnControl.java104
-rw-r--r--src/org/apache/http/protocol/ResponseContent.java101
-rw-r--r--src/org/apache/http/protocol/ResponseDate.java73
-rw-r--r--src/org/apache/http/protocol/ResponseServer.java71
-rw-r--r--src/org/apache/http/protocol/SyncBasicHttpContext.java61
-rw-r--r--src/org/apache/http/protocol/UriPatternMatcher.java127
-rw-r--r--src/org/apache/http/protocol/package.html112
-rw-r--r--src/org/apache/http/svn.info8
-rw-r--r--src/org/apache/http/util/ByteArrayBuffer.java162
-rw-r--r--src/org/apache/http/util/CharArrayBuffer.java264
-rw-r--r--src/org/apache/http/util/EncodingUtils.java185
-rw-r--r--src/org/apache/http/util/EntityUtils.java149
-rw-r--r--src/org/apache/http/util/ExceptionUtils.java85
-rw-r--r--src/org/apache/http/util/LangUtils.java88
-rw-r--r--src/org/apache/http/util/VersionInfo.java317
-rw-r--r--src/org/apache/http/util/package.html45
431 files changed, 60859 insertions, 0 deletions
diff --git a/src/org/apache/commons/codec/BinaryDecoder.java b/src/org/apache/commons/codec/BinaryDecoder.java
new file mode 100644
index 0000000..7aebabf
--- /dev/null
+++ b/src/org/apache/commons/codec/BinaryDecoder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * Defines common decoding methods for byte array decoders.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: BinaryDecoder.java,v 1.10 2004/06/15 18:14:15 ggregory Exp $
+ */
+public interface BinaryDecoder extends Decoder {
+
+ /**
+ * Decodes a byte array and returns the results as a byte array.
+ *
+ * @param pArray 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[] pArray) throws DecoderException;
+}
+
diff --git a/src/org/apache/commons/codec/BinaryEncoder.java b/src/org/apache/commons/codec/BinaryEncoder.java
new file mode 100644
index 0000000..52859ed
--- /dev/null
+++ b/src/org/apache/commons/codec/BinaryEncoder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * Defines common encoding methods for byte array encoders.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: BinaryEncoder.java,v 1.10 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface BinaryEncoder extends Encoder {
+
+ /**
+ * Encodes a byte array and return the encoded data
+ * as a byte array.
+ *
+ * @param pArray 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[] pArray) throws EncoderException;
+}
+
diff --git a/src/org/apache/commons/codec/Decoder.java b/src/org/apache/commons/codec/Decoder.java
new file mode 100644
index 0000000..184920c
--- /dev/null
+++ b/src/org/apache/commons/codec/Decoder.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * <p>Provides the highest level of abstraction for Decoders.
+ * This is the sister interface of {@link Encoder}. All
+ * Decoders implement this common generic interface.</p>
+ *
+ * <p>Allows a user to pass a generic Object to any Decoder
+ * implementation in the codec package.</p>
+ *
+ * <p>One of the two interfaces at the center of the codec package.</p>
+ *
+ * @author Apache Software Foundation
+ * @version $Id: Decoder.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+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 java.lang.ClassCastException} occurs
+ * this decode method will throw a DecoderException.
+ *
+ * @param pObject an 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 pObject) throws DecoderException;
+}
+
diff --git a/src/org/apache/commons/codec/DecoderException.java b/src/org/apache/commons/codec/DecoderException.java
new file mode 100644
index 0000000..f35c016
--- /dev/null
+++ b/src/org/apache/commons/codec/DecoderException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * Thrown when a Decoder has encountered a failure condition during a decode.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: DecoderException.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+public class DecoderException extends Exception {
+
+ /**
+ * Creates a DecoderException
+ *
+ * @param pMessage A message with meaning to a human
+ */
+ public DecoderException(String pMessage) {
+ super(pMessage);
+ }
+
+}
+
diff --git a/src/org/apache/commons/codec/Encoder.java b/src/org/apache/commons/codec/Encoder.java
new file mode 100644
index 0000000..fa339ee
--- /dev/null
+++ b/src/org/apache/commons/codec/Encoder.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * <p>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.</p>
+ *
+ * @author Apache Software Foundation
+ * @version $Id: Encoder.java,v 1.10 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface Encoder {
+
+ /**
+ * Encodes an "Object" and returns the encoded content
+ * as an Object. The Objects here may just be <code>byte[]</code>
+ * or <code>String</code>s depending on the implementation used.
+ *
+ * @param pObject 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 pObject) throws EncoderException;
+}
+
diff --git a/src/org/apache/commons/codec/EncoderException.java b/src/org/apache/commons/codec/EncoderException.java
new file mode 100644
index 0000000..0e202c1
--- /dev/null
+++ b/src/org/apache/commons/codec/EncoderException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * Thrown when there is a failure condition during the encoding process. This
+ * exception is thrown when an 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,v 1.10 2004/02/29 04:08:31 tobrien Exp $
+ */
+public class EncoderException extends Exception {
+
+ /**
+ * Creates a new instance of this exception with an useful message.
+ *
+ * @param pMessage a useful message relating to the encoder specific error.
+ */
+ public EncoderException(String pMessage) {
+ super(pMessage);
+ }
+}
+
diff --git a/src/org/apache/commons/codec/StringDecoder.java b/src/org/apache/commons/codec/StringDecoder.java
new file mode 100644
index 0000000..9b1a0cd
--- /dev/null
+++ b/src/org/apache/commons/codec/StringDecoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * Decodes a String into a String.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: StringDecoder.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface StringDecoder extends Decoder {
+
+ /**
+ * Decodes a String and returns a String.
+ *
+ * @param pString a String to encode
+ *
+ * @return the encoded String
+ *
+ * @throws DecoderException thrown if there is
+ * an error conidition during the Encoding process.
+ */
+ String decode(String pString) throws DecoderException;
+}
+
diff --git a/src/org/apache/commons/codec/StringEncoder.java b/src/org/apache/commons/codec/StringEncoder.java
new file mode 100644
index 0000000..46f5404
--- /dev/null
+++ b/src/org/apache/commons/codec/StringEncoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+/**
+ * Encodes a String into a String.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: StringEncoder.java,v 1.9 2004/02/29 04:08:31 tobrien Exp $
+ */
+public interface StringEncoder extends Encoder {
+
+ /**
+ * Encodes a String and returns a String.
+ *
+ * @param pString a String to encode
+ *
+ * @return the encoded String
+ *
+ * @throws EncoderException thrown if there is
+ * an error conidition during the Encoding process.
+ */
+ String encode(String pString) throws EncoderException;
+}
+
diff --git a/src/org/apache/commons/codec/StringEncoderComparator.java b/src/org/apache/commons/codec/StringEncoderComparator.java
new file mode 100644
index 0000000..6d29af2
--- /dev/null
+++ b/src/org/apache/commons/codec/StringEncoderComparator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec;
+
+import java.util.Comparator;
+
+/**
+ * Strings are comparable, and this comparator allows
+ * you to configure it with an instance of a class
+ * which implements 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,v 1.14 2004/06/21 23:24:17 ggregory Exp $
+ */
+public class StringEncoderComparator implements Comparator {
+
+ /**
+ * Internal encoder instance.
+ */
+ private StringEncoder stringEncoder;
+
+ /**
+ * Constructs a new instance.
+ */
+ public StringEncoderComparator() {
+ // no init.
+ }
+
+ /**
+ * 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 <code>0</code>.
+ *
+ * @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
+ */
+ public int compare(Object o1, Object o2) {
+
+ int compareCode = 0;
+
+ try {
+ Comparable s1 = (Comparable) ((Encoder) this.stringEncoder).encode(o1);
+ Comparable s2 = (Comparable) ((Encoder) this.stringEncoder).encode(o2);
+ compareCode = s1.compareTo(s2);
+ }
+ catch (EncoderException ee) {
+ compareCode = 0;
+ }
+ return compareCode;
+ }
+
+}
diff --git a/src/org/apache/commons/codec/binary/Base64.java b/src/org/apache/commons/codec/binary/Base64.java
new file mode 100644
index 0000000..ea479e9
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/Base64.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.binary;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * Provides Base64 encoding and decoding as defined by RFC 2045.
+ *
+ * <p>This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite>
+ * from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
+ * Format of Internet Message Bodies</cite> by Freed and Borenstein.</p>
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
+ * @author Apache Software Foundation
+ * @since 1.0-dev
+ * @version $Id: Base64.java,v 1.20 2004/05/24 00:21:24 ggregory Exp $
+ */
+public class Base64 implements BinaryEncoder, BinaryDecoder {
+
+ /**
+ * Chunk size per RFC 2045 section 6.8.
+ *
+ * <p>The {@value} character limit does not count the trailing CRLF, but counts
+ * all other characters, including any equal signs.</p>
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
+ */
+ static final int CHUNK_SIZE = 76;
+
+ /**
+ * Chunk separator per RFC 2045 section 2.1.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
+ */
+ static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
+
+ /**
+ * The base length.
+ */
+ static final int BASELENGTH = 255;
+
+ /**
+ * Lookup length.
+ */
+ static final int LOOKUPLENGTH = 64;
+
+ /**
+ * Used to calculate the number of bits in a byte.
+ */
+ static final int EIGHTBIT = 8;
+
+ /**
+ * Used when encoding something which has fewer than 24 bits.
+ */
+ static final int SIXTEENBIT = 16;
+
+ /**
+ * Used to determine how many bits data contains.
+ */
+ static final int TWENTYFOURBITGROUP = 24;
+
+ /**
+ * Used to get the number of Quadruples.
+ */
+ static final int FOURBYTE = 4;
+
+ /**
+ * Used to test the sign of a byte.
+ */
+ static final int SIGN = -128;
+
+ /**
+ * Byte used to pad output.
+ */
+ static final byte PAD = (byte) '=';
+
+ // Create arrays to hold the base64 characters and a
+ // lookup for base64 chars
+ private static byte[] base64Alphabet = new byte[BASELENGTH];
+ private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+
+ // Populating the lookup and character arrays
+ static {
+ for (int i = 0; i < BASELENGTH; i++) {
+ base64Alphabet[i] = (byte) -1;
+ }
+ for (int i = 'Z'; i >= 'A'; i--) {
+ base64Alphabet[i] = (byte) (i - 'A');
+ }
+ for (int i = 'z'; i >= 'a'; i--) {
+ base64Alphabet[i] = (byte) (i - 'a' + 26);
+ }
+ for (int i = '9'; i >= '0'; i--) {
+ base64Alphabet[i] = (byte) (i - '0' + 52);
+ }
+
+ base64Alphabet['+'] = 62;
+ base64Alphabet['/'] = 63;
+
+ for (int i = 0; i <= 25; i++) {
+ lookUpBase64Alphabet[i] = (byte) ('A' + i);
+ }
+
+ for (int i = 26, j = 0; i <= 51; i++, j++) {
+ lookUpBase64Alphabet[i] = (byte) ('a' + j);
+ }
+
+ for (int i = 52, j = 0; i <= 61; i++, j++) {
+ lookUpBase64Alphabet[i] = (byte) ('0' + j);
+ }
+
+ lookUpBase64Alphabet[62] = (byte) '+';
+ lookUpBase64Alphabet[63] = (byte) '/';
+ }
+
+ private static boolean isBase64(byte octect) {
+ if (octect == PAD) {
+ return true;
+ } else if (base64Alphabet[octect] == -1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Tests a given byte array to see if it contains
+ * only valid characters within the Base64 alphabet.
+ *
+ * @param arrayOctect 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
+ */
+ public static boolean isArrayByteBase64(byte[] arrayOctect) {
+
+ arrayOctect = discardWhitespace(arrayOctect);
+
+ int length = arrayOctect.length;
+ if (length == 0) {
+ // shouldn't a 0 length array be valid base64 data?
+ // return false;
+ return true;
+ }
+ for (int i = 0; i < length; i++) {
+ if (!isBase64(arrayOctect[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 Base64 characters
+ */
+ public static byte[] encodeBase64(byte[] binaryData) {
+ return encodeBase64(binaryData, false);
+ }
+
+ /**
+ * 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);
+ }
+
+
+ /**
+ * Decodes an Object using the base64 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[].
+ *
+ * @param pObject Object to decode
+ * @return An object (of type byte[]) containing the
+ * binary data which corresponds to the byte[] supplied.
+ * @throws DecoderException if the parameter supplied is not
+ * of type byte[]
+ */
+ public Object decode(Object pObject) throws DecoderException {
+ if (!(pObject instanceof byte[])) {
+ throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
+ }
+ return decode((byte[]) pObject);
+ }
+
+ /**
+ * Decodes a byte[] containing containing
+ * characters in the Base64 alphabet.
+ *
+ * @param pArray A byte array containing Base64 character data
+ * @return a byte array containing binary data
+ */
+ public byte[] decode(byte[] pArray) {
+ return decodeBase64(pArray);
+ }
+
+ /**
+ * 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 isChunked is true this encoder will chunk
+ * the base64 output into 76 character blocks
+ * @return Base64-encoded data.
+ */
+ public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
+ int lengthDataBits = binaryData.length * EIGHTBIT;
+ int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+ int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+ byte encodedData[] = null;
+ int encodedDataLength = 0;
+ int nbrChunks = 0;
+
+ if (fewerThan24bits != 0) {
+ //data not divisible by 24 bit
+ encodedDataLength = (numberTriplets + 1) * 4;
+ } else {
+ // 16 or 8 bit
+ encodedDataLength = numberTriplets * 4;
+ }
+
+ // If the output is to be "chunked" into 76 character sections,
+ // for compliance with RFC 2045 MIME, then it is important to
+ // allow for extra length to account for the separator(s)
+ if (isChunked) {
+
+ nbrChunks =
+ (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
+ encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
+ }
+
+ encodedData = new byte[encodedDataLength];
+
+ byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ int i = 0;
+ int nextSeparatorIndex = CHUNK_SIZE;
+ int chunksSoFar = 0;
+
+ //log.debug("number of triplets = " + numberTriplets);
+ for (i = 0; i < numberTriplets; i++) {
+ dataIndex = i * 3;
+ b1 = binaryData[dataIndex];
+ b2 = binaryData[dataIndex + 1];
+ b3 = binaryData[dataIndex + 2];
+
+ //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
+
+ l = (byte) (b2 & 0x0f);
+ k = (byte) (b1 & 0x03);
+
+ byte val1 =
+ ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+ byte val2 =
+ ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+ byte val3 =
+ ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+
+ encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+ //log.debug( "val2 = " + val2 );
+ //log.debug( "k4 = " + (k<<4) );
+ //log.debug( "vak = " + (val2 | (k<<4)) );
+ encodedData[encodedIndex + 1] =
+ lookUpBase64Alphabet[val2 | (k << 4)];
+ encodedData[encodedIndex + 2] =
+ lookUpBase64Alphabet[(l << 2) | val3];
+ encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
+
+ encodedIndex += 4;
+
+ // If we are chunking, let's put a chunk separator down.
+ if (isChunked) {
+ // this assumes that CHUNK_SIZE % 4 == 0
+ if (encodedIndex == nextSeparatorIndex) {
+ System.arraycopy(
+ CHUNK_SEPARATOR,
+ 0,
+ encodedData,
+ encodedIndex,
+ CHUNK_SEPARATOR.length);
+ chunksSoFar++;
+ nextSeparatorIndex =
+ (CHUNK_SIZE * (chunksSoFar + 1)) +
+ (chunksSoFar * CHUNK_SEPARATOR.length);
+ encodedIndex += CHUNK_SEPARATOR.length;
+ }
+ }
+ }
+
+ // form integral number of 6-bit groups
+ dataIndex = i * 3;
+
+ if (fewerThan24bits == EIGHTBIT) {
+ b1 = binaryData[dataIndex];
+ k = (byte) (b1 & 0x03);
+ //log.debug("b1=" + b1);
+ //log.debug("b1<<2 = " + (b1>>2) );
+ byte val1 =
+ ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+ encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+ encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
+ encodedData[encodedIndex + 2] = PAD;
+ encodedData[encodedIndex + 3] = PAD;
+ } else if (fewerThan24bits == SIXTEENBIT) {
+
+ b1 = binaryData[dataIndex];
+ b2 = binaryData[dataIndex + 1];
+ l = (byte) (b2 & 0x0f);
+ k = (byte) (b1 & 0x03);
+
+ byte val1 =
+ ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+ byte val2 =
+ ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+
+ encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+ encodedData[encodedIndex + 1] =
+ lookUpBase64Alphabet[val2 | (k << 4)];
+ encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
+ encodedData[encodedIndex + 3] = PAD;
+ }
+
+ if (isChunked) {
+ // we also add a separator to the end of the final chunk.
+ if (chunksSoFar < nbrChunks) {
+ System.arraycopy(
+ CHUNK_SEPARATOR,
+ 0,
+ encodedData,
+ encodedDataLength - CHUNK_SEPARATOR.length,
+ CHUNK_SEPARATOR.length);
+ }
+ }
+
+ return encodedData;
+ }
+
+ /**
+ * Decodes Base64 data into octects
+ *
+ * @param base64Data Byte array containing Base64 data
+ * @return Array containing decoded data.
+ */
+ public static byte[] decodeBase64(byte[] base64Data) {
+ // RFC 2045 requires that we discard ALL non-Base64 characters
+ base64Data = discardNonBase64(base64Data);
+
+ // handle the edge case, so we don't have to worry about it later
+ if (base64Data.length == 0) {
+ return new byte[0];
+ }
+
+ int numberQuadruple = base64Data.length / FOURBYTE;
+ byte decodedData[] = null;
+ byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+ // Throw away anything not in base64Data
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ {
+ // this sizes the output array properly - rlw
+ int lastData = base64Data.length;
+ // ignore the '=' padding
+ while (base64Data[lastData - 1] == PAD) {
+ if (--lastData == 0) {
+ return new byte[0];
+ }
+ }
+ decodedData = new byte[lastData - numberQuadruple];
+ }
+
+ for (int i = 0; i < numberQuadruple; i++) {
+ dataIndex = i * 4;
+ marker0 = base64Data[dataIndex + 2];
+ marker1 = base64Data[dataIndex + 3];
+
+ b1 = base64Alphabet[base64Data[dataIndex]];
+ b2 = base64Alphabet[base64Data[dataIndex + 1]];
+
+ if (marker0 != PAD && marker1 != PAD) {
+ //No PAD e.g 3cQl
+ b3 = base64Alphabet[marker0];
+ b4 = base64Alphabet[marker1];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
+ } else if (marker0 == PAD) {
+ //Two PAD e.g. 3c[Pad][Pad]
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ } else if (marker1 == PAD) {
+ //One PAD e.g. 3cQ[Pad]
+ b3 = base64Alphabet[marker0];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ }
+ encodedIndex += 3;
+ }
+ return decodedData;
+ }
+
+ /**
+ * Discards any whitespace from a base-64 encoded block.
+ *
+ * @param data The base-64 encoded data to discard the whitespace
+ * from.
+ * @return The data, less whitespace (see RFC 2045).
+ */
+ static byte[] discardWhitespace(byte[] data) {
+ byte groomedData[] = new byte[data.length];
+ int bytesCopied = 0;
+
+ for (int i = 0; i < data.length; i++) {
+ switch (data[i]) {
+ case (byte) ' ' :
+ case (byte) '\n' :
+ case (byte) '\r' :
+ case (byte) '\t' :
+ break;
+ default:
+ groomedData[bytesCopied++] = data[i];
+ }
+ }
+
+ byte packedData[] = new byte[bytesCopied];
+
+ System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+ return packedData;
+ }
+
+ /**
+ * Discards any characters outside of the base64 alphabet, per
+ * the requirements on page 25 of RFC 2045 - "Any characters
+ * outside of the base64 alphabet are to be ignored in base64
+ * encoded data."
+ *
+ * @param data The base-64 encoded data to groom
+ * @return The data, less non-base64 characters (see RFC 2045).
+ */
+ static byte[] discardNonBase64(byte[] data) {
+ byte groomedData[] = new byte[data.length];
+ int bytesCopied = 0;
+
+ for (int i = 0; i < data.length; i++) {
+ if (isBase64(data[i])) {
+ groomedData[bytesCopied++] = data[i];
+ }
+ }
+
+ byte packedData[] = new byte[bytesCopied];
+
+ System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+ return packedData;
+ }
+
+
+ // Implementation of the Encoder Interface
+
+ /**
+ * Encodes an Object using the base64 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
+ * base64 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 Base64 encode is not a byte[]");
+ }
+ return encode((byte[]) pObject);
+ }
+
+ /**
+ * Encodes a byte[] containing binary data, into a byte[] containing
+ * characters in the Base64 alphabet.
+ *
+ * @param pArray a byte array containing binary data
+ * @return A byte array containing only Base64 character data
+ */
+ public byte[] encode(byte[] pArray) {
+ return encodeBase64(pArray, false);
+ }
+
+}
diff --git a/src/org/apache/commons/codec/binary/BinaryCodec.java b/src/org/apache/commons/codec/binary/BinaryCodec.java
new file mode 100644
index 0000000..98c6409
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/BinaryCodec.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.binary;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * Translates between byte arrays and strings of "0"s and "1"s.
+ *
+ * <b>TODO:</b> may want to add more bit vector functions like and/or/xor/nand.
+ * <B>TODO:</b> also might be good to generate boolean[]
+ * from byte[] et. cetera.
+ *
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id $
+ */
+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.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.apache.commons.codec.Encoder#encode(java.lang.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.apache.commons.codec.Decoder#decode(java.lang.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.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.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 byte 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 (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;
+ }
+
+ /**
+ * 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.apache.commons.codec.BinaryEncoder#encode(byte[])
+ */
+ public static byte[] toAsciiBytes(byte[] raw) {
+ if (raw == null || raw.length == 0) {
+ 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.apache.commons.codec.BinaryEncoder#encode(byte[])
+ */
+ public static char[] toAsciiChars(byte[] raw) {
+ if (raw == null || raw.length == 0) {
+ 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.apache.commons.codec.BinaryEncoder#encode(byte[])
+ */
+ public static String toAsciiString(byte[] raw) {
+ return new String(toAsciiChars(raw));
+ }
+}
diff --git a/src/org/apache/commons/codec/binary/Hex.java b/src/org/apache/commons/codec/binary/Hex.java
new file mode 100644
index 0000000..78f5510
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/Hex.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.binary;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * Hex encoder and decoder.
+ *
+ * @since 1.1
+ * @author Apache Software Foundation
+ * @version $Id: Hex.java,v 1.13 2004/04/18 18:22:33 ggregory Exp $
+ */
+public class Hex implements BinaryEncoder, BinaryDecoder {
+
+ /**
+ * Used building output as Hex
+ */
+ private static final char[] DIGITS = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+
+ /**
+ * Converts an array of characters representing hexidecimal 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 hexidecimal 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 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 charcter " + ch + " at index " + index);
+ }
+ return digit;
+ }
+
+ /**
+ * Converts an array of bytes into an array of characters representing the hexidecimal 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 hexidecimal characters
+ */
+ public static char[] encodeHex(byte[] data) {
+
+ 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++] = DIGITS[(0xF0 & data[i]) >>> 4 ];
+ out[j++] = DIGITS[ 0x0F & data[i] ];
+ }
+
+ return out;
+ }
+
+ /**
+ * Converts an array of character bytes representing hexidecimal 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 hexidecimal 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 {
+ return decodeHex(new String(array).toCharArray());
+ }
+
+ /**
+ * Converts a String or an array of character bytes representing hexidecimal 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 hexidecimal 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());
+ }
+ }
+
+ /**
+ * Converts an array of bytes into an array of bytes for the characters representing the
+ * hexidecimal 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 array a byte[] to convert to Hex characters
+ * @return A byte[] containing the bytes of the hexidecimal characters
+ * @see #encodeHex(byte[])
+ */
+ public byte[] encode(byte[] array) {
+ return new String(encodeHex(array)).getBytes();
+ }
+
+ /**
+ * Converts a String or an array of bytes into an array of characters representing the
+ * hexidecimal 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.
+ *
+ * @param object a String, or byte[] to convert to Hex characters
+ * @return A char[] containing hexidecimal 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() : (byte[]) object;
+ return encodeHex(byteArray);
+ } catch (ClassCastException e) {
+ throw new EncoderException(e.getMessage());
+ }
+ }
+
+}
+
diff --git a/src/org/apache/commons/codec/binary/package.html b/src/org/apache/commons/codec/binary/package.html
new file mode 100644
index 0000000..844d918
--- /dev/null
+++ b/src/org/apache/commons/codec/binary/package.html
@@ -0,0 +1,20 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+
+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.
+-->
+<html>
+ <body>
+ Base64, Binary, and Hexadecimal String encoding and decoding.
+ </body>
+</html>
diff --git a/src/org/apache/commons/codec/language/DoubleMetaphone.java b/src/org/apache/commons/codec/language/DoubleMetaphone.java
new file mode 100644
index 0000000..1cad991
--- /dev/null
+++ b/src/org/apache/commons/codec/language/DoubleMetaphone.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Encodes a string into a double metaphone value.
+ * This Implementation is based on the algorithm by <CITE>Lawrence Philips</CITE>.
+ * <ul>
+ * <li>Original Article: <a
+ * href="http://www.cuj.com/documents/s=8038/cuj0006philips/">
+ * http://www.cuj.com/documents/s=8038/cuj0006philips/</a></li>
+ * <li>Original Source Code: <a href="ftp://ftp.cuj.com/pub/2000/1806/philips.zip">
+ * ftp://ftp.cuj.com/pub/2000/1806/philips.zip</a></li>
+ * </ul>
+ *
+ * @author Apache Software Foundation
+ * @version $Id: DoubleMetaphone.java,v 1.24 2004/06/05 18:32:04 ggregory Exp $
+ */
+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
+ */
+ protected 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(value, 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
+ * <code>obj</code> is a <code>String</code> (like <code>Metaphone</code>).
+ *
+ * @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 <code>String</code> 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 <code>true</code> if the encoded <code>String</code>s are equal;
+ * <code>false</code> 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 <code>String</code> 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 <code>true</code>.
+ * @return <code>true</code> if the encoded <code>String</code>s are equal;
+ * <code>false</code> 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(String value, 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, 4, "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) {
+ result.append('L');
+ if (charAt(value, index + 1) == 'L') {
+ if (conditionL0(value, index)) {
+ result.appendAlternate(' ');
+ }
+ index += 2;
+ } else {
+ index++;
+ }
+ 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, index - 1, 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 <code>true</code> 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();
+ }
+
+ /**
+ * Gets the character at index <code>index</code> if available, otherwise
+ * it returns <code>Character.MIN_VALUE</code> 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 <code>value</code> contains any of the criteria
+ starting
+ * at index <code>start</code> and matching up to length <code>length</code>
+ */
+ 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/src/org/apache/commons/codec/language/Metaphone.java b/src/org/apache/commons/codec/language/Metaphone.java
new file mode 100644
index 0000000..dce2c72
--- /dev/null
+++ b/src/org/apache/commons/codec/language/Metaphone.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Encodes a string into a metaphone value.
+ * <p>
+ * Initial Java implementation by <CITE>William B. Brogden. December, 1997</CITE>.
+ * Permission given by <CITE>wbrogden</CITE> for code to be used anywhere.
+ * </p>
+ * <p>
+ * <CITE>Hanging on the Metaphone</CITE> by <CITE>Lawrence Philips</CITE> in <CITE>Computer Language of Dec. 1990, p
+ * 39.</CITE>
+ * </p>
+ *
+ * @author Apache Software Foundation
+ * @version $Id: Metaphone.java,v 1.20 2004/06/05 18:32:04 ggregory Exp $
+ */
+public class Metaphone implements StringEncoder {
+
+ /**
+ * Five values in the English language
+ */
+ private String vowels = "AEIOU" ;
+
+ /**
+ * Variable used in Metaphone algorithm
+ */
+ private String frontv = "EIY" ;
+
+ /**
+ * Variable used in Metaphone algorithm
+ */
+ private 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() ;
+ }
+
+ char[] inwd = txt.toUpperCase().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) &&
+ (this.frontv.indexOf(local.charAt(n + 1)) >= 0) ) {
+ break;
+ }
+ if (regionMatch(local, n, "CIA")) { // "CIA" -> X
+ code.append('X');
+ break;
+ }
+ if (!isLastChar(wdsz, n) &&
+ (this.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') &&
+ (this.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')) {
+ hard = true ;
+ } else {
+ hard = false ;
+ }
+ if (!isLastChar(wdsz, n) &&
+ (this.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) &&
+ (this.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 (this.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 java.lang.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/src/org/apache/commons/codec/language/RefinedSoundex.java b/src/org/apache/commons/codec/language/RefinedSoundex.java
new file mode 100644
index 0000000..dbf60fe
--- /dev/null
+++ b/src/org/apache/commons/codec/language/RefinedSoundex.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.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
+ * <CITE>Margaret Odell</CITE> and <CITE>Robert Russell</CITE>.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: RefinedSoundex.java,v 1.21 2004/06/05 18:32:04 ggregory Exp $
+ */
+public class RefinedSoundex implements StringEncoder {
+
+ /**
+ * This static variable contains an instance of the RefinedSoundex using
+ * the US_ENGLISH mapping.
+ */
+ public static final RefinedSoundex US_ENGLISH = new RefinedSoundex();
+
+ /**
+ * RefinedSoundex is *refined* for a number of reasons one being that the
+ * mappings have been altered. This implementation contains default
+ * mappings for US English.
+ */
+ public static final char[] US_ENGLISH_MAPPING = "01360240043788015936020505".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 char[] soundexMapping;
+
+ /**
+ * Creates an instance of the RefinedSoundex object using the default US
+ * English mapping.
+ */
+ public RefinedSoundex() {
+ this(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 = mapping;
+ }
+
+ // BEGIN android-note
+ // Removed @see reference to SoundexUtils below, since the class isn't
+ // public.
+ // END android-note
+ /**
+ * 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 <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp">
+ * MS T-SQL DIFFERENCE</a>
+ *
+ * @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 java.lang.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/src/org/apache/commons/codec/language/Soundex.java b/src/org/apache/commons/codec/language/Soundex.java
new file mode 100644
index 0000000..61ce440
--- /dev/null
+++ b/src/org/apache/commons/codec/language/Soundex.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.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,v 1.26 2004/07/07 23:15:24 ggregory Exp $
+ */
+public class Soundex implements StringEncoder {
+
+ /**
+ * An instance of Soundex using the US_ENGLISH_MAPPING mapping.
+ *
+ * @see #US_ENGLISH_MAPPING
+ */
+ public static final Soundex US_ENGLISH = new Soundex();
+
+ /**
+ * This is a default mapping of the 26 letters used in US English. A value of <code>0</code> for a letter position
+ * means do not encode.
+ * <p>
+ * (This constant is provided as both an implementation convenience and to allow Javadoc to pick
+ * up the value for the constant values page.)
+ * </p>
+ *
+ * @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 <code>0</code> for a letter position
+ * means do not encode.
+ *
+ * @see Soundex#Soundex(char[])
+ */
+ public static final char[] US_ENGLISH_MAPPING = US_ENGLISH_MAPPING_STRING.toCharArray();
+
+ // BEGIN android-note
+ // Removed @see reference to SoundexUtils below, since the class isn't
+ // public.
+ // END android-note
+ /**
+ * 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 <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp"> MS
+ * T-SQL DIFFERENCE </a>
+ *
+ * @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 char[] soundexMapping;
+
+ /**
+ * Creates an instance using US_ENGLISH_MAPPING
+ *
+ * @see Soundex#Soundex(char[])
+ * @see Soundex#US_ENGLISH_MAPPING
+ */
+ public Soundex() {
+ this(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.setSoundexMapping(mapping);
+ }
+
+ /**
+ * 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) {
+ 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 it's Soudex code.
+ *
+ * @param ch
+ * An upper-case character.
+ * @return A Soundex code.
+ * @throws IllegalArgumentException
+ * Thrown if <code>ch</code> 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;
+ }
+
+ /**
+ * Sets the soundexMapping.
+ *
+ * @param soundexMapping
+ * The soundexMapping to set.
+ */
+ private void setSoundexMapping(char[] soundexMapping) {
+ this.soundexMapping = soundexMapping;
+ }
+
+ /**
+ * Retreives 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);
+ 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/src/org/apache/commons/codec/language/SoundexUtils.java b/src/org/apache/commons/codec/language/SoundexUtils.java
new file mode 100644
index 0000000..48f2d87
--- /dev/null
+++ b/src/org/apache/commons/codec/language/SoundexUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.language;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * Utility methods for {@link Soundex} and {@link RefinedSoundex} classes.
+ *
+ * @author Apache Software Foundation
+ * @version $Id: SoundexUtils.java,v 1.5 2004/03/17 18:31:35 ggregory Exp $
+ * @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();
+ }
+ return new String(chars, 0, count).toUpperCase();
+ }
+
+ /**
+ * Encodes the Strings and returns the number of characters in the two
+ * encoded Strings that are the same.
+ * <ul>
+ * <li>For Soundex, this return value ranges from 0 through 4: 0 indicates
+ * little or no similarity, and 4 indicates strong similarity or identical
+ * values.</li>
+ * <li>For refined Soundex, the return value can be greater than 4.</li>
+ * </ul>
+ *
+ * @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 <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp">
+ * MS T-SQL DIFFERENCE</a>
+ *
+ * @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.
+ * <ul>
+ * <li>For Soundex, this return value ranges from 0 through 4: 0 indicates
+ * little or no similarity, and 4 indicates strong similarity or identical
+ * values.</li>
+ * <li>For refined Soundex, the return value can be greater than 4.</li>
+ * </ul>
+ *
+ * @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 <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_de-dz_8co5.asp">
+ * MS T-SQL DIFFERENCE</a>
+ */
+ 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/src/org/apache/commons/codec/language/package.html b/src/org/apache/commons/codec/language/package.html
new file mode 100644
index 0000000..fab8e4c
--- /dev/null
+++ b/src/org/apache/commons/codec/language/package.html
@@ -0,0 +1,20 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+
+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.
+-->
+<html>
+ <body>
+ Language and phonetic encoders.
+ </body>
+</html>
diff --git a/src/org/apache/commons/codec/net/BCodec.java b/src/org/apache/commons/codec/net/BCodec.java
new file mode 100644
index 0000000..b164100
--- /dev/null
+++ b/src/org/apache/commons/codec/net/BCodec.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.net;
+
+import java.io.UnsupportedEncodingException;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * <p>
+ * Identical to the Base64 encoding defined by <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC
+ * 1521</a> and allows a character set to be specified.
+ * </p>
+ *
+ * <p>
+ * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> 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.
+ * </p>
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
+ * Header Extensions for Non-ASCII Text</a>
+ *
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: BCodec.java,v 1.5 2004/04/13 22:46:37 ggregory Exp $
+ */
+public class BCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
+ /**
+ * The default charset used for string decoding and encoding.
+ */
+ private String charset = StringEncodings.UTF8;
+
+ /**
+ * Default constructor.
+ */
+ public BCodec() {
+ super();
+ }
+
+ /**
+ * Constructor which allows for the selection of a default charset
+ *
+ * @param charset
+ * the default string charset to use.
+ *
+ * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+ * encoding names</a>
+ */
+ public BCodec(final String charset) {
+ super();
+ this.charset = charset;
+ }
+
+ protected String getEncoding() {
+ return "B";
+ }
+
+ protected byte[] doEncoding(byte[] bytes) throws EncoderException {
+ if (bytes == null) {
+ return null;
+ }
+ return Base64.encodeBase64(bytes);
+ }
+
+ protected byte[] doDecoding(byte[] bytes) throws DecoderException {
+ 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 pString
+ * @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());
+ }
+ }
+
+ /**
+ * 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());
+ }
+ }
+
+ /**
+ * 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
+ * A decoder exception is 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/src/org/apache/commons/codec/net/QCodec.java b/src/org/apache/commons/codec/net/QCodec.java
new file mode 100644
index 0000000..5736080
--- /dev/null
+++ b/src/org/apache/commons/codec/net/QCodec.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.net;
+
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * <p>
+ * Similar to the Quoted-Printable content-transfer-encoding defined in <a
+ * href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521</a> and designed to allow text containing mostly ASCII
+ * characters to be decipherable on an ASCII terminal without decoding.
+ * </p>
+ *
+ * <p>
+ * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> 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.
+ * </p>
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
+ * Header Extensions for Non-ASCII Text</a>
+ *
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: QCodec.java,v 1.6 2004/05/24 00:24:32 ggregory Exp $
+ */
+public class QCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
+ /**
+ * The default charset used for string decoding and encoding.
+ */
+ private String charset = StringEncodings.UTF8;
+
+ /**
+ * 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 byte BLANK = 32;
+
+ private static byte UNDERSCORE = 95;
+
+ private boolean encodeBlanks = false;
+
+ /**
+ * Default constructor.
+ */
+ public QCodec() {
+ super();
+ }
+
+ /**
+ * Constructor which allows for the selection of a default charset
+ *
+ * @param charset
+ * the default string charset to use.
+ *
+ * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+ * encoding names</a>
+ */
+ public QCodec(final String charset) {
+ super();
+ this.charset = charset;
+ }
+
+ protected String getEncoding() {
+ return "Q";
+ }
+
+ protected byte[] doEncoding(byte[] bytes) throws EncoderException {
+ 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());
+ }
+ }
+
+ /**
+ * 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());
+ }
+ }
+
+ /**
+ * 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
+ * A decoder exception is 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 <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
+ */
+ public boolean isEncodeBlanks() {
+ return this.encodeBlanks;
+ }
+
+ /**
+ * Defines whether optional tranformation of SPACE characters is to be used
+ *
+ * @param b
+ * <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
+ */
+ public void setEncodeBlanks(boolean b) {
+ this.encodeBlanks = b;
+ }
+}
diff --git a/src/org/apache/commons/codec/net/QuotedPrintableCodec.java b/src/org/apache/commons/codec/net/QuotedPrintableCodec.java
new file mode 100644
index 0000000..c2fcd27
--- /dev/null
+++ b/src/org/apache/commons/codec/net/QuotedPrintableCodec.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.net;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * <p>
+ * Codec for the Quoted-Printable section of <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521 </a>.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * Note:
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc1521.txt"> RFC 1521 MIME (Multipurpose Internet Mail Extensions) Part One:
+ * Mechanisms for Specifying and Describing the Format of Internet Message Bodies </a>
+ *
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: QuotedPrintableCodec.java,v 1.7 2004/04/09 22:21:07 ggregory Exp $
+ */
+public class QuotedPrintableCodec implements BinaryEncoder, BinaryDecoder, StringEncoder, StringDecoder {
+ /**
+ * The default charset used for string decoding and encoding.
+ */
+ private String charset = StringEncodings.UTF8;
+
+ /**
+ * BitSet of printable characters as defined in RFC 1521.
+ */
+ private static final BitSet PRINTABLE_CHARS = new BitSet(256);
+
+ private static byte ESCAPE_CHAR = '=';
+
+ private static byte TAB = 9;
+
+ private static 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() {
+ super();
+ }
+
+ /**
+ * 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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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.
+ *
+ * <p>
+ * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+ * RFC 1521.
+ * </p>
+ *
+ * @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 = Character.digit((char) bytes[++i], 16);
+ int l = Character.digit((char) bytes[++i], 16);
+ if (u == -1 || l == -1) {
+ throw new DecoderException("Invalid quoted-printable encoding");
+ }
+ buffer.write((char) ((u << 4) + l));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new DecoderException("Invalid quoted-printable encoding");
+ }
+ } 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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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.
+ *
+ * <p>
+ * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
+ * RFC 1521.
+ * </p>
+ *
+ * @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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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());
+ }
+ }
+
+ /**
+ * 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(pString.getBytes(StringEncodings.US_ASCII)), 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
+ * @throws UnsupportedEncodingException
+ * 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());
+ }
+ }
+
+ /**
+ * 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 quoted-printable decoding is not applicable to objects of this type if decoding is
+ * unsuccessful
+ */
+ 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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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 new String(encode(pString.getBytes(charset)), StringEncodings.US_ASCII);
+ }
+}
diff --git a/src/org/apache/commons/codec/net/RFC1522Codec.java b/src/org/apache/commons/codec/net/RFC1522Codec.java
new file mode 100644
index 0000000..0acf921
--- /dev/null
+++ b/src/org/apache/commons/codec/net/RFC1522Codec.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.net;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+
+/**
+ * <p>
+ * Implements methods common to all codecs defined in RFC 1522.
+ * </p>
+ *
+ * <p>
+ * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a>
+ * 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.
+ * </p>
+
+ * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">
+ * MIME (Multipurpose Internet Mail Extensions) Part Two:
+ * Message Header Extensions for Non-ASCII Text</a>
+ * </p>
+ *
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: RFC1522Codec.java,v 1.2 2004/04/09 22:21:43 ggregory Exp $
+ */
+abstract class RFC1522Codec {
+
+ /**
+ * 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 <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+ * encoding names</a>
+ */
+ protected String encodeText(final String text, final String charset)
+ throws EncoderException, UnsupportedEncodingException
+ {
+ if (text == null) {
+ return null;
+ }
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("=?");
+ buffer.append(charset);
+ buffer.append('?');
+ buffer.append(getEncoding());
+ buffer.append('?');
+ byte [] rawdata = doEncoding(text.getBytes(charset));
+ buffer.append(new String(rawdata, StringEncodings.US_ASCII));
+ buffer.append("?=");
+ 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
+ *
+ * @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("=?")) || (!text.endsWith("?="))) {
+ throw new DecoderException("RFC 1522 violation: malformed encoded content");
+ }
+ int termnator = text.length() - 2;
+ int from = 2;
+ int to = text.indexOf("?", from);
+ if ((to == -1) || (to == termnator)) {
+ 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("?", from);
+ if ((to == -1) || (to == termnator)) {
+ 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("?", from);
+ byte[] data = text.substring(from, to).getBytes(StringEncodings.US_ASCII);
+ 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/src/org/apache/commons/codec/net/StringEncodings.java b/src/org/apache/commons/codec/net/StringEncodings.java
new file mode 100644
index 0000000..e7f6bb8
--- /dev/null
+++ b/src/org/apache/commons/codec/net/StringEncodings.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.net;
+
+/**
+ * String encodings used in this package.
+ *
+ * @author Apache Software Foundation
+ * @since 1.3
+ * @version $Id: StringEncodings.java,v 1.2 2004/04/09 22:21:07 ggregory Exp $
+ */
+interface StringEncodings {
+ /**
+ * <p>
+ * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set.
+ * </p>
+ * <p>
+ * Every implementation of the Java platform is required to support this character encoding.
+ * </p>
+ *
+ * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+ * encoding names</a>
+ */
+ String US_ASCII = "US-ASCII";
+
+ /**
+ * <p>
+ * Eight-bit Unicode Transformation Format.
+ * </p>
+ * <p>
+ * Every implementation of the Java platform is required to support this character encoding.
+ * </p>
+ *
+ * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
+ * encoding names</a>
+ */
+ String UTF8 = "UTF-8";
+}
diff --git a/src/org/apache/commons/codec/net/URLCodec.java b/src/org/apache/commons/codec/net/URLCodec.java
new file mode 100644
index 0000000..1bc3507
--- /dev/null
+++ b/src/org/apache/commons/codec/net/URLCodec.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.codec.net;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.StringDecoder;
+import org.apache.commons.codec.StringEncoder;
+
+/**
+ * <p>Implements the 'www-form-urlencoded' encoding scheme,
+ * also misleadingly known as URL encoding.</p>
+ *
+ * <p>For more detailed information please refer to
+ * <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1">
+ * Chapter 17.13.4 'Form content types'</a> of the
+ * <a href="http://www.w3.org/TR/html4/">HTML 4.01 Specification<a></p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @author Apache Software Foundation
+ * @since 1.2
+ * @version $Id: URLCodec.java,v 1.19 2004/03/29 07:59:00 ggregory Exp $
+ */
+public class URLCodec implements BinaryEncoder, BinaryDecoder, StringEncoder, StringDecoder {
+
+ /**
+ * The default charset used for string decoding and encoding.
+ */
+ protected String charset = StringEncodings.UTF8;
+
+ protected static 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() {
+ super();
+ }
+
+ /**
+ * 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('%');
+ 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);
+ }
+ }
+ 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 == '%') {
+ try {
+ int u = Character.digit((char)bytes[++i], 16);
+ int l = Character.digit((char)bytes[++i], 16);
+ if (u == -1 || l == -1) {
+ throw new DecoderException("Invalid URL encoding");
+ }
+ buffer.write((char)((u << 4) + l));
+ } catch(ArrayIndexOutOfBoundsException e) {
+ throw new DecoderException("Invalid URL encoding");
+ }
+ } 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 new String(encode(pString.getBytes(charset)), StringEncodings.US_ASCII);
+ }
+
+
+ /**
+ * 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());
+ }
+ }
+
+
+ /**
+ * 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(pString.getBytes(StringEncodings.US_ASCII)), 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());
+ }
+ }
+
+ /**
+ * 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 URL decoding is not
+ * applicable to objects of this type
+ * if decoding is unsuccessful
+ */
+ 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 <code>String</code> encoding used for decoding and encoding.
+ *
+ * @return Returns the encoding.
+ *
+ * @deprecated use #getDefaultCharset()
+ */
+ 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/src/org/apache/commons/codec/net/package.html b/src/org/apache/commons/codec/net/package.html
new file mode 100644
index 0000000..4607c57
--- /dev/null
+++ b/src/org/apache/commons/codec/net/package.html
@@ -0,0 +1,22 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+
+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.
+-->
+<html>
+ <body>
+ <p>
+ Network related encoding and decoding.
+ </p>
+ </body>
+</html>
diff --git a/src/org/apache/commons/codec/overview.html b/src/org/apache/commons/codec/overview.html
new file mode 100644
index 0000000..6b6f6c9
--- /dev/null
+++ b/src/org/apache/commons/codec/overview.html
@@ -0,0 +1,28 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+
+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.
+-->
+<!-- $Id: overview.html,v 1.6 2004/05/17 17:06:10 ggregory Exp $ -->
+<html>
+<body>
+<p>
+This document is the API specification for the Apache Jakarta Commons Codec Library, version 1.3.
+</p>
+<p>
+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.
+</p>
+</body>
+</html>
diff --git a/src/org/apache/commons/codec/package.html b/src/org/apache/commons/codec/package.html
new file mode 100644
index 0000000..b7ccf03
--- /dev/null
+++ b/src/org/apache/commons/codec/package.html
@@ -0,0 +1,99 @@
+<!--
+Copyright 2003-2004 The Apache Software Foundation.
+
+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.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ </head>
+ <body>
+ <p>A small set of interfaces used by
+ the various implementations in the sub-packages.</p>
+
+ <p>Definitive implementations of commonly used encoders and decoders.</p>
+
+ <p>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. </p>
+
+ <h4><a name="Common Encoders">Binary Encoders</a></h4>
+
+ <table border="1" width="100%" cellspacing="2" cellpadding="3">
+ <tbody>
+ <tr>
+ <td>
+ <a href="binary/Base64.html">
+ org.apache.commons.codec.binary.Base64</a>
+ </td>
+ <td>
+ Provides Base64 content-transfer-encoding as defined in
+ <a href="http://www.ietf.org/rfc/rfc2045.txt"> RFC 2045</a>
+ </td>
+ <td>Production</td>
+ </tr>
+ <tr>
+ <td>
+ <a href="binary/Hex.html">
+ org.apache.commons.codec.binary.Hex</a>
+ </td>
+ <td>
+ Converts an array of bytes into an array of characters
+ representing the hexidecimal values of each byte in order
+ </td>
+ <td>Production</td>
+ </tr>
+ </tbody>
+ </table>
+ <h4>
+ <a name="Language Encoders">Language Encoders</a>
+ </h4>
+ <p>
+ Codec contains a number of commonly used language and phonetic
+ encoders
+ </p>
+ <table border="1" width="100%" cellspacing="2" cellpadding="3">
+ <tbody>
+ <tr>
+ <td>
+ <a href="#">org.apache.commons.codec.language.Soundex</a>
+ </td>
+ <td>Implementation of the Soundex algorithm.</td>
+ <td>Production</td>
+ </tr>
+ <tr>
+ <td>
+ <a href="#">org.apache.commons.codec.language.Metaphone</a>
+ </td>
+ <td>Implementation of the Metaphone algorithm.</td>
+ <td>Production</td>
+ </tr>
+ </tbody>
+ </table>
+ <h4><a name="Network_Encoders">Network Encoders</a></h4>
+ <h4> </h4>
+ <p> Codec contains network related encoders </p>
+ <table border="1" width="100%" cellspacing="2" cellpadding="3">
+ <tbody>
+ <tr>
+ <td>
+ <a href="#">org.apache.commons.codec.net.URLCodec</a>
+ </td>
+ <td>Implements the 'www-form-urlencoded' encoding scheme.</td>
+ <td>Production</td>
+ </tr>
+ </tbody>
+ </table>
+ <br>
+ </body>
+</html>
diff --git a/src/org/apache/commons/logging/Log.java b/src/org/apache/commons/logging/Log.java
new file mode 100644
index 0000000..9203f3f
--- /dev/null
+++ b/src/org/apache/commons/logging/Log.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging;
+
+/**
+ * <p>A simple logging interface abstracting logging APIs. In order to be
+ * instantiated successfully by {@link LogFactory}, classes that implement
+ * this interface must have a constructor that takes a single String
+ * parameter representing the "name" of this Log.</p>
+ *
+ * <p> The six logging levels used by <code>Log</code> are (in order):
+ * <ol>
+ * <li>trace (the least serious)</li>
+ * <li>debug</li>
+ * <li>info</li>
+ * <li>warn</li>
+ * <li>error</li>
+ * <li>fatal (the most serious)</li>
+ * </ol>
+ * The mapping of these log levels to the concepts used by the underlying
+ * logging system is implementation dependent.
+ * The implemention should ensure, though, that this ordering behaves
+ * as expected.</p>
+ *
+ * <p>Performance is often a logging concern.
+ * By examining the appropriate property,
+ * a component can avoid expensive operations (producing information
+ * to be logged).</p>
+ *
+ * <p> For example,
+ * <code><pre>
+ * if (log.isDebugEnabled()) {
+ * ... do something expensive ...
+ * log.debug(theResult);
+ * }
+ * </pre></code>
+ * </p>
+ *
+ * <p>Configuration of the underlying logging system will generally be done
+ * external to the Logging APIs, through whatever mechanism is supported by
+ * that system.</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author Rod Waldhoff
+ * @version $Id: Log.java 381838 2006-02-28 23:57:11Z skitching $
+ */
+public interface Log {
+
+
+ // ----------------------------------------------------- Logging Properties
+
+
+ /**
+ * <p> Is debug logging currently enabled? </p>
+ *
+ * <p> Call this method to prevent having to perform expensive operations
+ * (for example, <code>String</code> concatenation)
+ * when the log level is more than debug. </p>
+ *
+ * @return true if debug is enabled in the underlying logger.
+ */
+ public boolean isDebugEnabled();
+
+
+ /**
+ * <p> Is error logging currently enabled? </p>
+ *
+ * <p> Call this method to prevent having to perform expensive operations
+ * (for example, <code>String</code> concatenation)
+ * when the log level is more than error. </p>
+ *
+ * @return true if error is enabled in the underlying logger.
+ */
+ public boolean isErrorEnabled();
+
+
+ /**
+ * <p> Is fatal logging currently enabled? </p>
+ *
+ * <p> Call this method to prevent having to perform expensive operations
+ * (for example, <code>String</code> concatenation)
+ * when the log level is more than fatal. </p>
+ *
+ * @return true if fatal is enabled in the underlying logger.
+ */
+ public boolean isFatalEnabled();
+
+
+ /**
+ * <p> Is info logging currently enabled? </p>
+ *
+ * <p> Call this method to prevent having to perform expensive operations
+ * (for example, <code>String</code> concatenation)
+ * when the log level is more than info. </p>
+ *
+ * @return true if info is enabled in the underlying logger.
+ */
+ public boolean isInfoEnabled();
+
+
+ /**
+ * <p> Is trace logging currently enabled? </p>
+ *
+ * <p> Call this method to prevent having to perform expensive operations
+ * (for example, <code>String</code> concatenation)
+ * when the log level is more than trace. </p>
+ *
+ * @return true if trace is enabled in the underlying logger.
+ */
+ public boolean isTraceEnabled();
+
+
+ /**
+ * <p> Is warn logging currently enabled? </p>
+ *
+ * <p> Call this method to prevent having to perform expensive operations
+ * (for example, <code>String</code> concatenation)
+ * when the log level is more than warn. </p>
+ *
+ * @return true if warn is enabled in the underlying logger.
+ */
+ public boolean isWarnEnabled();
+
+
+ // -------------------------------------------------------- Logging Methods
+
+
+ /**
+ * <p> Log a message with trace log level. </p>
+ *
+ * @param message log this message
+ */
+ public void trace(Object message);
+
+
+ /**
+ * <p> Log an error with trace log level. </p>
+ *
+ * @param message log this message
+ * @param t log this cause
+ */
+ public void trace(Object message, Throwable t);
+
+
+ /**
+ * <p> Log a message with debug log level. </p>
+ *
+ * @param message log this message
+ */
+ public void debug(Object message);
+
+
+ /**
+ * <p> Log an error with debug log level. </p>
+ *
+ * @param message log this message
+ * @param t log this cause
+ */
+ public void debug(Object message, Throwable t);
+
+
+ /**
+ * <p> Log a message with info log level. </p>
+ *
+ * @param message log this message
+ */
+ public void info(Object message);
+
+
+ /**
+ * <p> Log an error with info log level. </p>
+ *
+ * @param message log this message
+ * @param t log this cause
+ */
+ public void info(Object message, Throwable t);
+
+
+ /**
+ * <p> Log a message with warn log level. </p>
+ *
+ * @param message log this message
+ */
+ public void warn(Object message);
+
+
+ /**
+ * <p> Log an error with warn log level. </p>
+ *
+ * @param message log this message
+ * @param t log this cause
+ */
+ public void warn(Object message, Throwable t);
+
+
+ /**
+ * <p> Log a message with error log level. </p>
+ *
+ * @param message log this message
+ */
+ public void error(Object message);
+
+
+ /**
+ * <p> Log an error with error log level. </p>
+ *
+ * @param message log this message
+ * @param t log this cause
+ */
+ public void error(Object message, Throwable t);
+
+
+ /**
+ * <p> Log a message with fatal log level. </p>
+ *
+ * @param message log this message
+ */
+ public void fatal(Object message);
+
+
+ /**
+ * <p> Log an error with fatal log level. </p>
+ *
+ * @param message log this message
+ * @param t log this cause
+ */
+ public void fatal(Object message, Throwable t);
+
+
+}
diff --git a/src/org/apache/commons/logging/LogConfigurationException.java b/src/org/apache/commons/logging/LogConfigurationException.java
new file mode 100644
index 0000000..b34387b
--- /dev/null
+++ b/src/org/apache/commons/logging/LogConfigurationException.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging;
+
+
+/**
+ * <p>An exception that is thrown only if a suitable <code>LogFactory</code>
+ * or <code>Log</code> instance cannot be created by the corresponding
+ * factory methods.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 155426 $ $Date: 2005-02-26 13:10:49 +0000 (Sat, 26 Feb 2005) $
+ */
+
+public class LogConfigurationException extends RuntimeException {
+
+
+ /**
+ * Construct a new exception with <code>null</code> as its detail message.
+ */
+ public LogConfigurationException() {
+
+ super();
+
+ }
+
+
+ /**
+ * Construct a new exception with the specified detail message.
+ *
+ * @param message The detail message
+ */
+ public LogConfigurationException(String message) {
+
+ super(message);
+
+ }
+
+
+ /**
+ * Construct a new exception with the specified cause and a derived
+ * detail message.
+ *
+ * @param cause The underlying cause
+ */
+ public LogConfigurationException(Throwable cause) {
+
+ this((cause == null) ? null : cause.toString(), cause);
+
+ }
+
+
+ /**
+ * Construct a new exception with the specified detail message and cause.
+ *
+ * @param message The detail message
+ * @param cause The underlying cause
+ */
+ public LogConfigurationException(String message, Throwable cause) {
+
+ super(message + " (Caused by " + cause + ")");
+ this.cause = cause; // Two-argument version requires JDK 1.4 or later
+
+ }
+
+
+ /**
+ * The underlying cause of this exception.
+ */
+ protected Throwable cause = null;
+
+
+ /**
+ * Return the underlying cause of this exception (if any).
+ */
+ public Throwable getCause() {
+
+ return (this.cause);
+
+ }
+
+
+}
diff --git a/src/org/apache/commons/logging/LogFactory.java b/src/org/apache/commons/logging/LogFactory.java
new file mode 100644
index 0000000..107d0f7
--- /dev/null
+++ b/src/org/apache/commons/logging/LogFactory.java
@@ -0,0 +1,1740 @@
+/*
+ * Copyright 2001-2006 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging;
+
+
+import java.io.BufferedReader;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+
+/**
+ * <p>Factory for creating {@link Log} instances, with discovery and
+ * configuration features similar to that employed by standard Java APIs
+ * such as JAXP.</p>
+ *
+ * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
+ * based on the SAXParserFactory and DocumentBuilderFactory implementations
+ * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
+ *
+ * @author Craig R. McClanahan
+ * @author Costin Manolache
+ * @author Richard A. Sitze
+ * @version $Revision: 399431 $ $Date: 2006-05-03 21:58:34 +0100 (Wed, 03 May 2006) $
+ */
+
+public abstract class LogFactory {
+
+
+ // ----------------------------------------------------- Manifest Constants
+
+ /**
+ * The name (<code>priority</code>) of the key in the config file used to
+ * specify the priority of that particular config file. The associated value
+ * is a floating-point number; higher values take priority over lower values.
+ */
+ public static final String PRIORITY_KEY = "priority";
+
+ /**
+ * The name (<code>use_tccl</code>) of the key in the config file used
+ * to specify whether logging classes should be loaded via the thread
+ * context class loader (TCCL), or not. By default, the TCCL is used.
+ */
+ public static final String TCCL_KEY = "use_tccl";
+
+ /**
+ * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property
+ * used to identify the LogFactory implementation
+ * class name. This can be used as a system property, or as an entry in a
+ * configuration properties file.
+ */
+ public static final String FACTORY_PROPERTY =
+ "org.apache.commons.logging.LogFactory";
+
+ /**
+ * The fully qualified class name of the fallback <code>LogFactory</code>
+ * implementation class to use, if no other can be found.
+ */
+ public static final String FACTORY_DEFAULT =
+ "org.apache.commons.logging.impl.LogFactoryImpl";
+
+ /**
+ * The name (<code>commons-logging.properties</code>) of the properties file to search for.
+ */
+ public static final String FACTORY_PROPERTIES =
+ "commons-logging.properties";
+
+ /**
+ * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
+ * 'Service Provider' specification</a>.
+ *
+ */
+ protected static final String SERVICE_ID =
+ "META-INF/services/org.apache.commons.logging.LogFactory";
+
+ /**
+ * The name (<code>org.apache.commons.logging.diagnostics.dest</code>)
+ * of the property used to enable internal commons-logging
+ * diagnostic output, in order to get information on what logging
+ * implementations are being discovered, what classloaders they
+ * are loaded through, etc.
+ * <p>
+ * If a system property of this name is set then the value is
+ * assumed to be the name of a file. The special strings
+ * STDOUT or STDERR (case-sensitive) indicate output to
+ * System.out and System.err respectively.
+ * <p>
+ * Diagnostic logging should be used only to debug problematic
+ * configurations and should not be set in normal production use.
+ */
+ public static final String DIAGNOSTICS_DEST_PROPERTY =
+ "org.apache.commons.logging.diagnostics.dest";
+
+ /**
+ * When null (the usual case), no diagnostic output will be
+ * generated by LogFactory or LogFactoryImpl. When non-null,
+ * interesting events will be written to the specified object.
+ */
+ private static PrintStream diagnosticsStream = null;
+
+ /**
+ * A string that gets prefixed to every message output by the
+ * logDiagnostic method, so that users can clearly see which
+ * LogFactory class is generating the output.
+ */
+ private static String diagnosticPrefix;
+
+ /**
+ * <p>Setting this system property
+ * (<code>org.apache.commons.logging.LogFactory.HashtableImpl</code>)
+ * value allows the <code>Hashtable</code> used to store
+ * classloaders to be substituted by an alternative implementation.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> <code>LogFactory</code> will print:
+ * <code><pre>
+ * [ERROR] LogFactory: Load of custom hashtable failed</em>
+ * </pre></code>
+ * to system error and then continue using a standard Hashtable.
+ * </p>
+ * <p>
+ * <strong>Usage:</strong> Set this property when Java is invoked
+ * and <code>LogFactory</code> will attempt to load a new instance
+ * of the given implementation class.
+ * For example, running the following ant scriplet:
+ * <code><pre>
+ * &lt;java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"&gt;
+ * ...
+ * &lt;sysproperty
+ * key="org.apache.commons.logging.LogFactory.HashtableImpl"
+ * value="org.apache.commons.logging.AltHashtable"/&gt;
+ * &lt;/java&gt;
+ * </pre></code>
+ * will mean that <code>LogFactory</code> will load an instance of
+ * <code>org.apache.commons.logging.AltHashtable</code>.
+ * </p>
+ * <p>
+ * A typical use case is to allow a custom
+ * Hashtable implementation using weak references to be substituted.
+ * This will allow classloaders to be garbage collected without
+ * the need to release them (on 1.3+ JVMs only, of course ;)
+ * </p>
+ */
+ public static final String HASHTABLE_IMPLEMENTATION_PROPERTY =
+ "org.apache.commons.logging.LogFactory.HashtableImpl";
+ /** Name used to load the weak hashtable implementation by names */
+ private static final String WEAK_HASHTABLE_CLASSNAME =
+ "org.apache.commons.logging.impl.WeakHashtable";
+
+ /**
+ * A reference to the classloader that loaded this class. This is the
+ * same as LogFactory.class.getClassLoader(). However computing this
+ * value isn't quite as simple as that, as we potentially need to use
+ * AccessControllers etc. It's more efficient to compute it once and
+ * cache it here.
+ */
+ private static ClassLoader thisClassLoader;
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Protected constructor that is not available for public use.
+ */
+ protected LogFactory() {
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Return the configuration attribute with the specified name (if any),
+ * or <code>null</code> if there is no such attribute.
+ *
+ * @param name Name of the attribute to return
+ */
+ public abstract Object getAttribute(String name);
+
+
+ /**
+ * Return an array containing the names of all currently defined
+ * configuration attributes. If there are no such attributes, a zero
+ * length array is returned.
+ */
+ public abstract String[] getAttributeNames();
+
+
+ /**
+ * Convenience method to derive a name from the specified class and
+ * call <code>getInstance(String)</code> with it.
+ *
+ * @param clazz Class for which a suitable Log name will be derived
+ *
+ * @exception LogConfigurationException if a suitable <code>Log</code>
+ * instance cannot be returned
+ */
+ public abstract Log getInstance(Class clazz)
+ throws LogConfigurationException;
+
+
+ /**
+ * <p>Construct (if necessary) and return a <code>Log</code> instance,
+ * using the factory's current set of configuration attributes.</p>
+ *
+ * <p><strong>NOTE</strong> - Depending upon the implementation of
+ * the <code>LogFactory</code> you are using, the <code>Log</code>
+ * instance you are returned may or may not be local to the current
+ * application, and may or may not be returned again on a subsequent
+ * call with the same name argument.</p>
+ *
+ * @param name Logical name of the <code>Log</code> instance to be
+ * returned (the meaning of this name is only known to the underlying
+ * logging implementation that is being wrapped)
+ *
+ * @exception LogConfigurationException if a suitable <code>Log</code>
+ * instance cannot be returned
+ */
+ public abstract Log getInstance(String name)
+ throws LogConfigurationException;
+
+
+ /**
+ * Release any internal references to previously created {@link Log}
+ * instances returned by this factory. This is useful in environments
+ * like servlet containers, which implement application reloading by
+ * throwing away a ClassLoader. Dangling references to objects in that
+ * class loader would prevent garbage collection.
+ */
+ public abstract void release();
+
+
+ /**
+ * Remove any configuration attribute associated with the specified name.
+ * If there is no such attribute, no action is taken.
+ *
+ * @param name Name of the attribute to remove
+ */
+ public abstract void removeAttribute(String name);
+
+
+ /**
+ * Set the configuration attribute with the specified name. Calling
+ * this with a <code>null</code> value is equivalent to calling
+ * <code>removeAttribute(name)</code>.
+ *
+ * @param name Name of the attribute to set
+ * @param value Value of the attribute to set, or <code>null</code>
+ * to remove any setting for this attribute
+ */
+ public abstract void setAttribute(String name, Object value);
+
+
+ // ------------------------------------------------------- Static Variables
+
+
+ /**
+ * The previously constructed <code>LogFactory</code> instances, keyed by
+ * the <code>ClassLoader</code> with which it was created.
+ */
+ protected static Hashtable factories = null;
+
+ /**
+ * Prevously constructed <code>LogFactory</code> instance as in the
+ * <code>factories</code> map, but for the case where
+ * <code>getClassLoader</code> returns <code>null</code>.
+ * This can happen when:
+ * <ul>
+ * <li>using JDK1.1 and the calling code is loaded via the system
+ * classloader (very common)</li>
+ * <li>using JDK1.2+ and the calling code is loaded via the boot
+ * classloader (only likely for embedded systems work).</li>
+ * </ul>
+ * Note that <code>factories</code> is a <i>Hashtable</i> (not a HashMap),
+ * and hashtables don't allow null as a key.
+ */
+ protected static LogFactory nullClassLoaderFactory = null;
+
+ /**
+ * Create the hashtable which will be used to store a map of
+ * (context-classloader -> logfactory-object). Version 1.2+ of Java
+ * supports "weak references", allowing a custom Hashtable class
+ * to be used which uses only weak references to its keys. Using weak
+ * references can fix memory leaks on webapp unload in some cases (though
+ * not all). Version 1.1 of Java does not support weak references, so we
+ * must dynamically determine which we are using. And just for fun, this
+ * code also supports the ability for a system property to specify an
+ * arbitrary Hashtable implementation name.
+ * <p>
+ * Note that the correct way to ensure no memory leaks occur is to ensure
+ * that LogFactory.release(contextClassLoader) is called whenever a
+ * webapp is undeployed.
+ */
+ private static final Hashtable createFactoryStore() {
+ Hashtable result = null;
+ String storeImplementationClass
+ = System.getProperty(HASHTABLE_IMPLEMENTATION_PROPERTY);
+ if (storeImplementationClass == null) {
+ storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
+ }
+ try {
+ Class implementationClass = Class.forName(storeImplementationClass);
+ result = (Hashtable) implementationClass.newInstance();
+
+ } catch (Throwable t) {
+ // ignore
+ if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
+ // if the user's trying to set up a custom implementation, give a clue
+ if (isDiagnosticsEnabled()) {
+ // use internal logging to issue the warning
+ logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
+ } else {
+ // we *really* want this output, even if diagnostics weren't
+ // explicitly enabled by the user.
+ System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");
+ }
+ }
+ }
+ if (result == null) {
+ result = new Hashtable();
+ }
+ return result;
+ }
+
+
+ // --------------------------------------------------------- Static Methods
+
+ /**
+ * <p>Construct (if necessary) and return a <code>LogFactory</code>
+ * instance, using the following ordered lookup procedure to determine
+ * the name of the implementation class to be loaded.</p>
+ * <ul>
+ * <li>The <code>org.apache.commons.logging.LogFactory</code> system
+ * property.</li>
+ * <li>The JDK 1.3 Service Discovery mechanism</li>
+ * <li>Use the properties file <code>commons-logging.properties</code>
+ * file, if found in the class path of this class. The configuration
+ * file is in standard <code>java.util.Properties</code> format and
+ * contains the fully qualified name of the implementation class
+ * with the key being the system property defined above.</li>
+ * <li>Fall back to a default implementation class
+ * (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
+ * </ul>
+ *
+ * <p><em>NOTE</em> - If the properties file method of identifying the
+ * <code>LogFactory</code> implementation class is utilized, all of the
+ * properties defined in this file will be set as configuration attributes
+ * on the corresponding <code>LogFactory</code> instance.</p>
+ *
+ * <p><em>NOTE</em> - In a multithreaded environment it is possible
+ * that two different instances will be returned for the same
+ * classloader environment.
+ * </p>
+ *
+ * @exception LogConfigurationException if the implementation class is not
+ * available or cannot be instantiated.
+ */
+ public static LogFactory getFactory() throws LogConfigurationException {
+ // Identify the class loader we will be using
+ ClassLoader contextClassLoader = getContextClassLoader();
+
+ if (contextClassLoader == null) {
+ // This is an odd enough situation to report about. This
+ // output will be a nuisance on JDK1.1, as the system
+ // classloader is null in that environment.
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Context classloader is null.");
+ }
+ }
+
+ // Return any previously registered factory for this class loader
+ LogFactory factory = getCachedFactory(contextClassLoader);
+ if (factory != null) {
+ return factory;
+ }
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] LogFactory implementation requested for the first time for context classloader "
+ + objectId(contextClassLoader));
+ logHierarchy("[LOOKUP] ", contextClassLoader);
+ }
+
+ // Load properties file.
+ //
+ // If the properties file exists, then its contents are used as
+ // "attributes" on the LogFactory implementation class. One particular
+ // property may also control which LogFactory concrete subclass is
+ // used, but only if other discovery mechanisms fail..
+ //
+ // As the properties file (if it exists) will be used one way or
+ // another in the end we may as well look for it first.
+
+ Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
+
+ // Determine whether we will be using the thread context class loader to
+ // load logging classes or not by checking the loaded properties file (if any).
+ ClassLoader baseClassLoader = contextClassLoader;
+ if (props != null) {
+ String useTCCLStr = props.getProperty(TCCL_KEY);
+ if (useTCCLStr != null) {
+ // The Boolean.valueOf(useTCCLStr).booleanValue() formulation
+ // is required for Java 1.2 compatability.
+ if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
+ // Don't use current context classloader when locating any
+ // LogFactory or Log classes, just use the class that loaded
+ // this abstract class. When this class is deployed in a shared
+ // classpath of a container, it means webapps cannot deploy their
+ // own logging implementations. It also means that it is up to the
+ // implementation whether to load library-specific config files
+ // from the TCCL or not.
+ baseClassLoader = thisClassLoader;
+ }
+ }
+ }
+
+ // Determine which concrete LogFactory subclass to use.
+ // First, try a global system property
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY
+ + "] to define the LogFactory subclass to use...");
+ }
+
+ try {
+ String factoryClass = System.getProperty(FACTORY_PROPERTY);
+ if (factoryClass != null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass
+ + "' as specified by system property " + FACTORY_PROPERTY);
+ }
+
+ factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
+ } else {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] No system property [" + FACTORY_PROPERTY
+ + "] defined.");
+ }
+ }
+ } catch (SecurityException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] A security exception occurred while trying to create an"
+ + " instance of the custom factory class"
+ + ": [" + e.getMessage().trim()
+ + "]. Trying alternative implementations...");
+ }
+ ; // ignore
+ } catch(RuntimeException e) {
+ // This is not consistent with the behaviour when a bad LogFactory class is
+ // specified in a services file.
+ //
+ // One possible exception that can occur here is a ClassCastException when
+ // the specified class wasn't castable to this LogFactory type.
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] An exception occurred while trying to create an"
+ + " instance of the custom factory class"
+ + ": [" + e.getMessage().trim()
+ + "] as specified by a system property.");
+ }
+ throw e;
+ }
+
+
+ // Second, try to find a service by using the JDK1.3 class
+ // discovery mechanism, which involves putting a file with the name
+ // of an interface class in the META-INF/services directory, where the
+ // contents of the file is a single line specifying a concrete class
+ // that implements the desired interface.
+
+ if (factory == null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Looking for a resource file of name [" + SERVICE_ID
+ + "] to define the LogFactory subclass to use...");
+ }
+ try {
+ InputStream is = getResourceAsStream(contextClassLoader,
+ SERVICE_ID);
+
+ if( is != null ) {
+ // This code is needed by EBCDIC and other strange systems.
+ // It's a fix for bugs reported in xerces
+ BufferedReader rd;
+ try {
+ rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+ } catch (java.io.UnsupportedEncodingException e) {
+ rd = new BufferedReader(new InputStreamReader(is));
+ }
+
+ String factoryClassName = rd.readLine();
+ rd.close();
+
+ if (factoryClassName != null &&
+ ! "".equals(factoryClassName)) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Creating an instance of LogFactory class " + factoryClassName
+ + " as specified by file '" + SERVICE_ID
+ + "' which was present in the path of the context"
+ + " classloader.");
+ }
+ factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
+ }
+ } else {
+ // is == null
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] No resource file with name '" + SERVICE_ID
+ + "' found.");
+ }
+ }
+ } catch( Exception ex ) {
+ // note: if the specified LogFactory class wasn't compatible with LogFactory
+ // for some reason, a ClassCastException will be caught here, and attempts will
+ // continue to find a compatible class.
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] A security exception occurred while trying to create an"
+ + " instance of the custom factory class"
+ + ": [" + ex.getMessage().trim()
+ + "]. Trying alternative implementations...");
+ }
+ ; // ignore
+ }
+ }
+
+
+ // Third try looking into the properties file read earlier (if found)
+
+ if (factory == null) {
+ if (props != null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Looking in properties file for entry with key '"
+ + FACTORY_PROPERTY
+ + "' to define the LogFactory subclass to use...");
+ }
+ String factoryClass = props.getProperty(FACTORY_PROPERTY);
+ if (factoryClass != null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Properties file specifies LogFactory subclass '"
+ + factoryClass + "'");
+ }
+ factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
+
+ // TODO: think about whether we need to handle exceptions from newFactory
+ } else {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
+ }
+ }
+ } else {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] No properties file available to determine"
+ + " LogFactory subclass from..");
+ }
+ }
+ }
+
+
+ // Fourth, try the fallback implementation class
+
+ if (factory == null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT
+ + "' via the same classloader that loaded this LogFactory"
+ + " class (ie not looking in the context classloader).");
+ }
+
+ // Note: unlike the above code which can try to load custom LogFactory
+ // implementations via the TCCL, we don't try to load the default LogFactory
+ // implementation via the context classloader because:
+ // * that can cause problems (see comments in newFactory method)
+ // * no-one should be customising the code of the default class
+ // Yes, we do give up the ability for the child to ship a newer
+ // version of the LogFactoryImpl class and have it used dynamically
+ // by an old LogFactory class in the parent, but that isn't
+ // necessarily a good idea anyway.
+ factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
+ }
+
+ if (factory != null) {
+ /**
+ * Always cache using context class loader.
+ */
+ cacheFactory(contextClassLoader, factory);
+
+ if( props!=null ) {
+ Enumeration names = props.propertyNames();
+ while (names.hasMoreElements()) {
+ String name = (String) names.nextElement();
+ String value = props.getProperty(name);
+ factory.setAttribute(name, value);
+ }
+ }
+ }
+
+ return factory;
+ }
+
+
+ /**
+ * Convenience method to return a named logger, without the application
+ * having to care about factories.
+ *
+ * @param clazz Class from which a log name will be derived
+ *
+ * @exception LogConfigurationException if a suitable <code>Log</code>
+ * instance cannot be returned
+ */
+ public static Log getLog(Class clazz)
+ throws LogConfigurationException {
+
+ // BEGIN android-added
+ return getLog(clazz.getName());
+ // END android-added
+ // BEGIN android-deleted
+ //return (getFactory().getInstance(clazz));
+ // END android-deleted
+
+ }
+
+
+ /**
+ * Convenience method to return a named logger, without the application
+ * having to care about factories.
+ *
+ * @param name Logical name of the <code>Log</code> instance to be
+ * returned (the meaning of this name is only known to the underlying
+ * logging implementation that is being wrapped)
+ *
+ * @exception LogConfigurationException if a suitable <code>Log</code>
+ * instance cannot be returned
+ */
+ public static Log getLog(String name)
+ throws LogConfigurationException {
+
+ // BEGIN android-added
+ return new org.apache.commons.logging.impl.Jdk14Logger(name);
+ // END android-added
+ // BEGIN android-deleted
+ //return (getFactory().getInstance(name));
+ // END android-deleted
+
+ }
+
+
+ /**
+ * Release any internal references to previously created {@link LogFactory}
+ * instances that have been associated with the specified class loader
+ * (if any), after calling the instance method <code>release()</code> on
+ * each of them.
+ *
+ * @param classLoader ClassLoader for which to release the LogFactory
+ */
+ public static void release(ClassLoader classLoader) {
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Releasing factory for classloader " + objectId(classLoader));
+ }
+ synchronized (factories) {
+ if (classLoader == null) {
+ if (nullClassLoaderFactory != null) {
+ nullClassLoaderFactory.release();
+ nullClassLoaderFactory = null;
+ }
+ } else {
+ LogFactory factory = (LogFactory) factories.get(classLoader);
+ if (factory != null) {
+ factory.release();
+ factories.remove(classLoader);
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * Release any internal references to previously created {@link LogFactory}
+ * instances, after calling the instance method <code>release()</code> on
+ * each of them. This is useful in environments like servlet containers,
+ * which implement application reloading by throwing away a ClassLoader.
+ * Dangling references to objects in that class loader would prevent
+ * garbage collection.
+ */
+ public static void releaseAll() {
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Releasing factory for all classloaders.");
+ }
+ synchronized (factories) {
+ Enumeration elements = factories.elements();
+ while (elements.hasMoreElements()) {
+ LogFactory element = (LogFactory) elements.nextElement();
+ element.release();
+ }
+ factories.clear();
+
+ if (nullClassLoaderFactory != null) {
+ nullClassLoaderFactory.release();
+ nullClassLoaderFactory = null;
+ }
+ }
+
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+ /**
+ * Safely get access to the classloader for the specified class.
+ * <p>
+ * Theoretically, calling getClassLoader can throw a security exception,
+ * and so should be done under an AccessController in order to provide
+ * maximum flexibility. However in practice people don't appear to use
+ * security policies that forbid getClassLoader calls. So for the moment
+ * all code is written to call this method rather than Class.getClassLoader,
+ * so that we could put AccessController stuff in this method without any
+ * disruption later if we need to.
+ * <p>
+ * Even when using an AccessController, however, this method can still
+ * throw SecurityException. Commons-logging basically relies on the
+ * ability to access classloaders, ie a policy that forbids all
+ * classloader access will also prevent commons-logging from working:
+ * currently this method will throw an exception preventing the entire app
+ * from starting up. Maybe it would be good to detect this situation and
+ * just disable all commons-logging? Not high priority though - as stated
+ * above, security policies that prevent classloader access aren't common.
+ *
+ * @since 1.1
+ */
+ protected static ClassLoader getClassLoader(Class clazz) {
+ try {
+ return clazz.getClassLoader();
+ } catch(SecurityException ex) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Unable to get classloader for class '" + clazz
+ + "' due to security restrictions - " + ex.getMessage());
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Calls LogFactory.directGetContextClassLoader under the control of an
+ * AccessController class. This means that java code running under a
+ * security manager that forbids access to ClassLoaders will still work
+ * if this class is given appropriate privileges, even when the caller
+ * doesn't have such privileges. Without using an AccessController, the
+ * the entire call stack must have the privilege before the call is
+ * allowed.
+ *
+ * @return the context classloader associated with the current thread,
+ * or null if security doesn't allow it.
+ *
+ * @throws LogConfigurationException if there was some weird error while
+ * attempting to get the context classloader.
+ *
+ * @throws SecurityException if the current java security policy doesn't
+ * allow this class to access the context classloader.
+ */
+ protected static ClassLoader getContextClassLoader()
+ throws LogConfigurationException {
+
+ return (ClassLoader)AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ return directGetContextClassLoader();
+ }
+ });
+ }
+
+ /**
+ * Return the thread context class loader if available; otherwise return
+ * null.
+ * <p>
+ * Most/all code should call getContextClassLoader rather than calling
+ * this method directly.
+ * <p>
+ * The thread context class loader is available for JDK 1.2
+ * or later, if certain security conditions are met.
+ * <p>
+ * Note that no internal logging is done within this method because
+ * this method is called every time LogFactory.getLogger() is called,
+ * and we don't want too much output generated here.
+ *
+ * @exception LogConfigurationException if a suitable class loader
+ * cannot be identified.
+ *
+ * @exception SecurityException if the java security policy forbids
+ * access to the context classloader from one of the classes in the
+ * current call stack.
+ * @since 1.1
+ */
+ protected static ClassLoader directGetContextClassLoader()
+ throws LogConfigurationException
+ {
+ ClassLoader classLoader = null;
+
+ try {
+ // Are we running on a JDK 1.2 or later system?
+ Method method = Thread.class.getMethod("getContextClassLoader",
+ (Class[]) null);
+
+ // Get the thread context class loader (if there is one)
+ try {
+ classLoader = (ClassLoader)method.invoke(Thread.currentThread(),
+ (Object[]) null);
+ } catch (IllegalAccessException e) {
+ throw new LogConfigurationException
+ ("Unexpected IllegalAccessException", e);
+ } catch (InvocationTargetException e) {
+ /**
+ * InvocationTargetException is thrown by 'invoke' when
+ * the method being invoked (getContextClassLoader) throws
+ * an exception.
+ *
+ * getContextClassLoader() throws SecurityException when
+ * the context class loader isn't an ancestor of the
+ * calling class's class loader, or if security
+ * permissions are restricted.
+ *
+ * In the first case (not related), we want to ignore and
+ * keep going. We cannot help but also ignore the second
+ * with the logic below, but other calls elsewhere (to
+ * obtain a class loader) will trigger this exception where
+ * we can make a distinction.
+ */
+ if (e.getTargetException() instanceof SecurityException) {
+ ; // ignore
+ } else {
+ // Capture 'e.getTargetException()' exception for details
+ // alternate: log 'e.getTargetException()', and pass back 'e'.
+ throw new LogConfigurationException
+ ("Unexpected InvocationTargetException", e.getTargetException());
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ // Assume we are running on JDK 1.1
+ classLoader = getClassLoader(LogFactory.class);
+
+ // We deliberately don't log a message here to outputStream;
+ // this message would be output for every call to LogFactory.getLog()
+ // when running on JDK1.1
+ //
+ // if (outputStream != null) {
+ // outputStream.println(
+ // "Method Thread.getContextClassLoader does not exist;"
+ // + " assuming this is JDK 1.1, and that the context"
+ // + " classloader is the same as the class that loaded"
+ // + " the concrete LogFactory class.");
+ // }
+
+ }
+
+ // Return the selected class loader
+ return classLoader;
+ }
+
+ /**
+ * Check cached factories (keyed by contextClassLoader)
+ *
+ * @param contextClassLoader is the context classloader associated
+ * with the current thread. This allows separate LogFactory objects
+ * per component within a container, provided each component has
+ * a distinct context classloader set. This parameter may be null
+ * in JDK1.1, and in embedded systems where jcl-using code is
+ * placed in the bootclasspath.
+ *
+ * @return the factory associated with the specified classloader if
+ * one has previously been created, or null if this is the first time
+ * we have seen this particular classloader.
+ */
+ private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
+ {
+ LogFactory factory = null;
+
+ if (contextClassLoader == null) {
+ // We have to handle this specially, as factories is a Hashtable
+ // and those don't accept null as a key value.
+ //
+ // nb: nullClassLoaderFactory might be null. That's ok.
+ factory = nullClassLoaderFactory;
+ } else {
+ factory = (LogFactory) factories.get(contextClassLoader);
+ }
+
+ return factory;
+ }
+
+ /**
+ * Remember this factory, so later calls to LogFactory.getCachedFactory
+ * can return the previously created object (together with all its
+ * cached Log objects).
+ *
+ * @param classLoader should be the current context classloader. Note that
+ * this can be null under some circumstances; this is ok.
+ *
+ * @param factory should be the factory to cache. This should never be null.
+ */
+ private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
+ {
+ // Ideally we would assert(factory != null) here. However reporting
+ // errors from within a logging implementation is a little tricky!
+
+ if (factory != null) {
+ if (classLoader == null) {
+ nullClassLoaderFactory = factory;
+ } else {
+ factories.put(classLoader, factory);
+ }
+ }
+ }
+
+ /**
+ * Return a new instance of the specified <code>LogFactory</code>
+ * implementation class, loaded by the specified class loader.
+ * If that fails, try the class loader used to load this
+ * (abstract) LogFactory.
+ * <p>
+ * <h2>ClassLoader conflicts</h2>
+ * Note that there can be problems if the specified ClassLoader is not the
+ * same as the classloader that loaded this class, ie when loading a
+ * concrete LogFactory subclass via a context classloader.
+ * <p>
+ * The problem is the same one that can occur when loading a concrete Log
+ * subclass via a context classloader.
+ * <p>
+ * The problem occurs when code running in the context classloader calls
+ * class X which was loaded via a parent classloader, and class X then calls
+ * LogFactory.getFactory (either directly or via LogFactory.getLog). Because
+ * class X was loaded via the parent, it binds to LogFactory loaded via
+ * the parent. When the code in this method finds some LogFactoryYYYY
+ * class in the child (context) classloader, and there also happens to be a
+ * LogFactory class defined in the child classloader, then LogFactoryYYYY
+ * will be bound to LogFactory@childloader. It cannot be cast to
+ * LogFactory@parentloader, ie this method cannot return the object as
+ * the desired type. Note that it doesn't matter if the LogFactory class
+ * in the child classloader is identical to the LogFactory class in the
+ * parent classloader, they are not compatible.
+ * <p>
+ * The solution taken here is to simply print out an error message when
+ * this occurs then throw an exception. The deployer of the application
+ * must ensure they remove all occurrences of the LogFactory class from
+ * the child classloader in order to resolve the issue. Note that they
+ * do not have to move the custom LogFactory subclass; that is ok as
+ * long as the only LogFactory class it can find to bind to is in the
+ * parent classloader.
+ * <p>
+ * @param factoryClass Fully qualified name of the <code>LogFactory</code>
+ * implementation class
+ * @param classLoader ClassLoader from which to load this class
+ * @param contextClassLoader is the context that this new factory will
+ * manage logging for.
+ *
+ * @exception LogConfigurationException if a suitable instance
+ * cannot be created
+ * @since 1.1
+ */
+ protected static LogFactory newFactory(final String factoryClass,
+ final ClassLoader classLoader,
+ final ClassLoader contextClassLoader)
+ throws LogConfigurationException
+ {
+ // Note that any unchecked exceptions thrown by the createFactory
+ // method will propagate out of this method; in particular a
+ // ClassCastException can be thrown.
+ Object result = AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ return createFactory(factoryClass, classLoader);
+ }
+ });
+
+ if (result instanceof LogConfigurationException) {
+ LogConfigurationException ex = (LogConfigurationException) result;
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "An error occurred while loading the factory class:"
+ + ex.getMessage());
+ }
+ throw ex;
+ }
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Created object " + objectId(result)
+ + " to manage classloader " + objectId(contextClassLoader));
+ }
+ return (LogFactory)result;
+ }
+
+ /**
+ * Method provided for backwards compatibility; see newFactory version that
+ * takes 3 parameters.
+ * <p>
+ * This method would only ever be called in some rather odd situation.
+ * Note that this method is static, so overriding in a subclass doesn't
+ * have any effect unless this method is called from a method in that
+ * subclass. However this method only makes sense to use from the
+ * getFactory method, and as that is almost always invoked via
+ * LogFactory.getFactory, any custom definition in a subclass would be
+ * pointless. Only a class with a custom getFactory method, then invoked
+ * directly via CustomFactoryImpl.getFactory or similar would ever call
+ * this. Anyway, it's here just in case, though the "managed class loader"
+ * value output to the diagnostics will not report the correct value.
+ */
+ protected static LogFactory newFactory(final String factoryClass,
+ final ClassLoader classLoader) {
+ return newFactory(factoryClass, classLoader, null);
+ }
+
+ /**
+ * Implements the operations described in the javadoc for newFactory.
+ *
+ * @param factoryClass
+ *
+ * @param classLoader used to load the specified factory class. This is
+ * expected to be either the TCCL or the classloader which loaded this
+ * class. Note that the classloader which loaded this class might be
+ * "null" (ie the bootloader) for embedded systems.
+ *
+ * @return either a LogFactory object or a LogConfigurationException object.
+ * @since 1.1
+ */
+ protected static Object createFactory(String factoryClass, ClassLoader classLoader) {
+
+ // This will be used to diagnose bad configurations
+ // and allow a useful message to be sent to the user
+ Class logFactoryClass = null;
+ try {
+ if (classLoader != null) {
+ try {
+ // First the given class loader param (thread class loader)
+
+ // Warning: must typecast here & allow exception
+ // to be generated/caught & recast properly.
+ logFactoryClass = classLoader.loadClass(factoryClass);
+ if (LogFactory.class.isAssignableFrom(logFactoryClass)) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Loaded class " + logFactoryClass.getName()
+ + " from classloader " + objectId(classLoader));
+ }
+ } else {
+ //
+ // This indicates a problem with the ClassLoader tree.
+ // An incompatible ClassLoader was used to load the
+ // implementation.
+ // As the same classes
+ // must be available in multiple class loaders,
+ // it is very likely that multiple JCL jars are present.
+ // The most likely fix for this
+ // problem is to remove the extra JCL jars from the
+ // ClassLoader hierarchy.
+ //
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Factory class " + logFactoryClass.getName()
+ + " loaded from classloader " + objectId(logFactoryClass.getClassLoader())
+ + " does not extend '" + LogFactory.class.getName()
+ + "' as loaded by this classloader.");
+ logHierarchy("[BAD CL TREE] ", classLoader);
+ }
+ }
+
+ return (LogFactory) logFactoryClass.newInstance();
+
+ } catch (ClassNotFoundException ex) {
+ if (classLoader == thisClassLoader) {
+ // Nothing more to try, onwards.
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Unable to locate any class called '" + factoryClass
+ + "' via classloader " + objectId(classLoader));
+ }
+ throw ex;
+ }
+ // ignore exception, continue
+ } catch (NoClassDefFoundError e) {
+ if (classLoader == thisClassLoader) {
+ // Nothing more to try, onwards.
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Class '" + factoryClass + "' cannot be loaded"
+ + " via classloader " + objectId(classLoader)
+ + " - it depends on some other class that cannot"
+ + " be found.");
+ }
+ throw e;
+ }
+ // ignore exception, continue
+ } catch(ClassCastException e) {
+ if (classLoader == thisClassLoader) {
+ // There's no point in falling through to the code below that
+ // tries again with thisClassLoader, because we've just tried
+ // loading with that loader (not the TCCL). Just throw an
+ // appropriate exception here.
+
+ final boolean implementsLogFactory = implementsLogFactory(logFactoryClass);
+
+ //
+ // Construct a good message: users may not actual expect that a custom implementation
+ // has been specified. Several well known containers use this mechanism to adapt JCL
+ // to their native logging system.
+ //
+ String msg =
+ "The application has specified that a custom LogFactory implementation should be used but " +
+ "Class '" + factoryClass + "' cannot be converted to '"
+ + LogFactory.class.getName() + "'. ";
+ if (implementsLogFactory) {
+ msg = msg + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. " +
+ "Background can be found in http://jakarta.apache.org/commons/logging/tech.html. " +
+ "If you have not explicitly specified a custom LogFactory then it is likely that " +
+ "the container has set one without your knowledge. " +
+ "In this case, consider using the commons-logging-adapters.jar file or " +
+ "specifying the standard LogFactory from the command line. ";
+ } else {
+ msg = msg + "Please check the custom implementation. ";
+ }
+ msg = msg + "Help can be found @http://jakarta.apache.org/commons/logging/troubleshooting.html.";
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(msg);
+ }
+
+ ClassCastException ex = new ClassCastException(msg);
+ throw ex;
+ }
+
+ // Ignore exception, continue. Presumably the classloader was the
+ // TCCL; the code below will try to load the class via thisClassLoader.
+ // This will handle the case where the original calling class is in
+ // a shared classpath but the TCCL has a copy of LogFactory and the
+ // specified LogFactory implementation; we will fall back to using the
+ // LogFactory implementation from the same classloader as this class.
+ //
+ // Issue: this doesn't handle the reverse case, where this LogFactory
+ // is in the webapp, and the specified LogFactory implementation is
+ // in a shared classpath. In that case:
+ // (a) the class really does implement LogFactory (bad log msg above)
+ // (b) the fallback code will result in exactly the same problem.
+ }
+ }
+
+ /* At this point, either classLoader == null, OR
+ * classLoader was unable to load factoryClass.
+ *
+ * In either case, we call Class.forName, which is equivalent
+ * to LogFactory.class.getClassLoader().load(name), ie we ignore
+ * the classloader parameter the caller passed, and fall back
+ * to trying the classloader associated with this class. See the
+ * javadoc for the newFactory method for more info on the
+ * consequences of this.
+ *
+ * Notes:
+ * * LogFactory.class.getClassLoader() may return 'null'
+ * if LogFactory is loaded by the bootstrap classloader.
+ */
+ // Warning: must typecast here & allow exception
+ // to be generated/caught & recast properly.
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Unable to load factory class via classloader "
+ + objectId(classLoader)
+ + " - trying the classloader associated with this LogFactory.");
+ }
+ logFactoryClass = Class.forName(factoryClass);
+ return (LogFactory) logFactoryClass.newInstance();
+ } catch (Exception e) {
+ // Check to see if we've got a bad configuration
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Unable to create LogFactory instance.");
+ }
+ if (logFactoryClass != null
+ && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
+
+ return new LogConfigurationException(
+ "The chosen LogFactory implementation does not extend LogFactory."
+ + " Please check your configuration.",
+ e);
+ }
+ return new LogConfigurationException(e);
+ }
+ }
+
+ /**
+ * Determines whether the given class actually implements <code>LogFactory</code>.
+ * Diagnostic information is also logged.
+ * <p>
+ * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
+ * of incompatibility. The test used is whether the class is assignable from
+ * the <code>LogFactory</code> class loaded by the class's classloader.
+ * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
+ * @return true if the <code>logFactoryClass</code> does extend
+ * <code>LogFactory</code> when that class is loaded via the same
+ * classloader that loaded the <code>logFactoryClass</code>.
+ */
+ private static boolean implementsLogFactory(Class logFactoryClass) {
+ boolean implementsLogFactory = false;
+ if (logFactoryClass != null) {
+ try {
+ ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
+ if (logFactoryClassLoader == null) {
+ logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
+ } else {
+ logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
+ Class factoryFromCustomLoader
+ = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
+ implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
+ if (implementsLogFactory) {
+ logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
+ + " implements LogFactory but was loaded by an incompatible classloader.");
+ } else {
+ logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
+ + " does not implement LogFactory.");
+ }
+ }
+ } catch (SecurityException e) {
+ //
+ // The application is running within a hostile security environment.
+ // This will make it very hard to diagnose issues with JCL.
+ // Consider running less securely whilst debugging this issue.
+ //
+ logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether " +
+ "the compatibility was caused by a classloader conflict: "
+ + e.getMessage());
+ } catch (LinkageError e) {
+ //
+ // This should be an unusual circumstance.
+ // LinkageError's usually indicate that a dependent class has incompatibly changed.
+ // Another possibility may be an exception thrown by an initializer.
+ // Time for a clean rebuild?
+ //
+ logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether " +
+ "the compatibility was caused by a classloader conflict: "
+ + e.getMessage());
+ } catch (ClassNotFoundException e) {
+ //
+ // LogFactory cannot be loaded by the classloader which loaded the custom factory implementation.
+ // The custom implementation is not viable until this is corrected.
+ // Ensure that the JCL jar and the custom class are available from the same classloader.
+ // Running with diagnostics on should give information about the classloaders used
+ // to load the custom factory.
+ //
+ logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded the " +
+ "custom LogFactory implementation. Is the custom factory in the right classloader?");
+ }
+ }
+ return implementsLogFactory;
+ }
+
+ /**
+ * Applets may run in an environment where accessing resources of a loader is
+ * a secure operation, but where the commons-logging library has explicitly
+ * been granted permission for that operation. In this case, we need to
+ * run the operation using an AccessController.
+ */
+ private static InputStream getResourceAsStream(final ClassLoader loader,
+ final String name)
+ {
+ return (InputStream)AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ if (loader != null) {
+ return loader.getResourceAsStream(name);
+ } else {
+ return ClassLoader.getSystemResourceAsStream(name);
+ }
+ }
+ });
+ }
+
+ /**
+ * Given a filename, return an enumeration of URLs pointing to
+ * all the occurrences of that filename in the classpath.
+ * <p>
+ * This is just like ClassLoader.getResources except that the
+ * operation is done under an AccessController so that this method will
+ * succeed when this jarfile is privileged but the caller is not.
+ * This method must therefore remain private to avoid security issues.
+ * <p>
+ * If no instances are found, an Enumeration is returned whose
+ * hasMoreElements method returns false (ie an "empty" enumeration).
+ * If resources could not be listed for some reason, null is returned.
+ */
+ private static Enumeration getResources(final ClassLoader loader,
+ final String name)
+ {
+ PrivilegedAction action =
+ new PrivilegedAction() {
+ public Object run() {
+ try {
+ if (loader != null) {
+ return loader.getResources(name);
+ } else {
+ return ClassLoader.getSystemResources(name);
+ }
+ } catch(IOException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Exception while trying to find configuration file "
+ + name + ":" + e.getMessage());
+ }
+ return null;
+ } catch(NoSuchMethodError e) {
+ // we must be running on a 1.1 JVM which doesn't support
+ // ClassLoader.getSystemResources; just return null in
+ // this case.
+ return null;
+ }
+ }
+ };
+ Object result = AccessController.doPrivileged(action);
+ return (Enumeration) result;
+ }
+
+ /**
+ * Given a URL that refers to a .properties file, load that file.
+ * This is done under an AccessController so that this method will
+ * succeed when this jarfile is privileged but the caller is not.
+ * This method must therefore remain private to avoid security issues.
+ * <p>
+ * Null is returned if the URL cannot be opened.
+ */
+ private static Properties getProperties(final URL url) {
+ PrivilegedAction action =
+ new PrivilegedAction() {
+ public Object run() {
+ try {
+ InputStream stream = url.openStream();
+ if (stream != null) {
+ Properties props = new Properties();
+ props.load(stream);
+ stream.close();
+ return props;
+ }
+ } catch(IOException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Unable to read URL " + url);
+ }
+ }
+
+ return null;
+ }
+ };
+ return (Properties) AccessController.doPrivileged(action);
+ }
+
+ /**
+ * Locate a user-provided configuration file.
+ * <p>
+ * The classpath of the specified classLoader (usually the context classloader)
+ * is searched for properties files of the specified name. If none is found,
+ * null is returned. If more than one is found, then the file with the greatest
+ * value for its PRIORITY property is returned. If multiple files have the
+ * same PRIORITY value then the first in the classpath is returned.
+ * <p>
+ * This differs from the 1.0.x releases; those always use the first one found.
+ * However as the priority is a new field, this change is backwards compatible.
+ * <p>
+ * The purpose of the priority field is to allow a webserver administrator to
+ * override logging settings in all webapps by placing a commons-logging.properties
+ * file in a shared classpath location with a priority > 0; this overrides any
+ * commons-logging.properties files without priorities which are in the
+ * webapps. Webapps can also use explicit priorities to override a configuration
+ * file in the shared classpath if needed.
+ */
+ private static final Properties getConfigurationFile(
+ ClassLoader classLoader, String fileName) {
+
+ Properties props = null;
+ double priority = 0.0;
+ URL propsUrl = null;
+ try {
+ Enumeration urls = getResources(classLoader, fileName);
+
+ if (urls == null) {
+ return null;
+ }
+
+ while (urls.hasMoreElements()) {
+ URL url = (URL) urls.nextElement();
+
+ Properties newProps = getProperties(url);
+ if (newProps != null) {
+ if (props == null) {
+ propsUrl = url;
+ props = newProps;
+ String priorityStr = props.getProperty(PRIORITY_KEY);
+ priority = 0.0;
+ if (priorityStr != null) {
+ priority = Double.parseDouble(priorityStr);
+ }
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Properties file found at '" + url + "'"
+ + " with priority " + priority);
+ }
+ } else {
+ String newPriorityStr = newProps.getProperty(PRIORITY_KEY);
+ double newPriority = 0.0;
+ if (newPriorityStr != null) {
+ newPriority = Double.parseDouble(newPriorityStr);
+ }
+
+ if (newPriority > priority) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Properties file at '" + url + "'"
+ + " with priority " + newPriority
+ + " overrides file at '" + propsUrl + "'"
+ + " with priority " + priority);
+ }
+
+ propsUrl = url;
+ props = newProps;
+ priority = newPriority;
+ } else {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[LOOKUP] Properties file at '" + url + "'"
+ + " with priority " + newPriority
+ + " does not override file at '" + propsUrl + "'"
+ + " with priority " + priority);
+ }
+ }
+ }
+
+ }
+ }
+ } catch (SecurityException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("SecurityException thrown while trying to find/read config files.");
+ }
+ }
+
+ if (isDiagnosticsEnabled()) {
+ if (props == null) {
+ logDiagnostic(
+ "[LOOKUP] No properties file of name '" + fileName
+ + "' found.");
+ } else {
+ logDiagnostic(
+ "[LOOKUP] Properties file of name '" + fileName
+ + "' found at '" + propsUrl + '"');
+ }
+ }
+
+ return props;
+ }
+
+ /**
+ * Determines whether the user wants internal diagnostic output. If so,
+ * returns an appropriate writer object. Users can enable diagnostic
+ * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
+ * a filename, or the special values STDOUT or STDERR.
+ */
+ private static void initDiagnostics() {
+ String dest;
+ try {
+ dest = System.getProperty(DIAGNOSTICS_DEST_PROPERTY);
+ if (dest == null) {
+ return;
+ }
+ } catch(SecurityException ex) {
+ // We must be running in some very secure environment.
+ // We just have to assume output is not wanted..
+ return;
+ }
+
+ if (dest.equals("STDOUT")) {
+ diagnosticsStream = System.out;
+ } else if (dest.equals("STDERR")) {
+ diagnosticsStream = System.err;
+ } else {
+ try {
+ // open the file in append mode
+ FileOutputStream fos = new FileOutputStream(dest, true);
+ diagnosticsStream = new PrintStream(fos);
+ } catch(IOException ex) {
+ // We should report this to the user - but how?
+ return;
+ }
+ }
+
+ // In order to avoid confusion where multiple instances of JCL are
+ // being used via different classloaders within the same app, we
+ // ensure each logged message has a prefix of form
+ // [LogFactory from classloader OID]
+ //
+ // Note that this prefix should be kept consistent with that
+ // in LogFactoryImpl. However here we don't need to output info
+ // about the actual *instance* of LogFactory, as all methods that
+ // output diagnostics from this class are static.
+ String classLoaderName;
+ try {
+ ClassLoader classLoader = thisClassLoader;
+ if (thisClassLoader == null) {
+ classLoaderName = "BOOTLOADER";
+ } else {
+ classLoaderName = objectId(classLoader);
+ }
+ } catch(SecurityException e) {
+ classLoaderName = "UNKNOWN";
+ }
+ diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
+ }
+
+ /**
+ * Indicates true if the user has enabled internal logging.
+ * <p>
+ * By the way, sorry for the incorrect grammar, but calling this method
+ * areDiagnosticsEnabled just isn't java beans style.
+ *
+ * @return true if calls to logDiagnostic will have any effect.
+ * @since 1.1
+ */
+ protected static boolean isDiagnosticsEnabled() {
+ return diagnosticsStream != null;
+ }
+
+ /**
+ * Write the specified message to the internal logging destination.
+ * <p>
+ * Note that this method is private; concrete subclasses of this class
+ * should not call it because the diagnosticPrefix string this
+ * method puts in front of all its messages is LogFactory@....,
+ * while subclasses should put SomeSubClass@...
+ * <p>
+ * Subclasses should instead compute their own prefix, then call
+ * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
+ * fine for subclasses.
+ * <p>
+ * Note that it is safe to call this method before initDiagnostics
+ * is called; any output will just be ignored (as isDiagnosticsEnabled
+ * will return false).
+ *
+ * @param msg is the diagnostic message to be output.
+ */
+ private static final void logDiagnostic(String msg) {
+ if (diagnosticsStream != null) {
+ diagnosticsStream.print(diagnosticPrefix);
+ diagnosticsStream.println(msg);
+ diagnosticsStream.flush();
+ }
+ }
+
+ /**
+ * Write the specified message to the internal logging destination.
+ *
+ * @param msg is the diagnostic message to be output.
+ * @since 1.1
+ */
+ protected static final void logRawDiagnostic(String msg) {
+ if (diagnosticsStream != null) {
+ diagnosticsStream.println(msg);
+ diagnosticsStream.flush();
+ }
+ }
+
+ /**
+ * Generate useful diagnostics regarding the classloader tree for
+ * the specified class.
+ * <p>
+ * As an example, if the specified class was loaded via a webapp's
+ * classloader, then you may get the following output:
+ * <pre>
+ * Class com.acme.Foo was loaded via classloader 11111
+ * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT
+ * </pre>
+ * <p>
+ * This method returns immediately if isDiagnosticsEnabled()
+ * returns false.
+ *
+ * @param clazz is the class whose classloader + tree are to be
+ * output.
+ */
+ private static void logClassLoaderEnvironment(Class clazz) {
+ if (!isDiagnosticsEnabled()) {
+ return;
+ }
+
+ try {
+ logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
+ logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
+ } catch(SecurityException ex) {
+ logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
+ }
+
+ String className = clazz.getName();
+ ClassLoader classLoader;
+
+ try {
+ classLoader = getClassLoader(clazz);
+ } catch(SecurityException ex) {
+ // not much useful diagnostics we can print here!
+ logDiagnostic(
+ "[ENV] Security forbids determining the classloader for " + className);
+ return;
+ }
+
+ logDiagnostic(
+ "[ENV] Class " + className + " was loaded via classloader "
+ + objectId(classLoader));
+ logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader);
+ }
+
+ /**
+ * Logs diagnostic messages about the given classloader
+ * and it's hierarchy. The prefix is prepended to the message
+ * and is intended to make it easier to understand the logs.
+ * @param prefix
+ * @param classLoader
+ */
+ private static void logHierarchy(String prefix, ClassLoader classLoader) {
+ if (!isDiagnosticsEnabled()) {
+ return;
+ }
+ ClassLoader systemClassLoader;
+ if (classLoader != null) {
+ final String classLoaderString = classLoader.toString();
+ logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'");
+ }
+
+ try {
+ systemClassLoader = ClassLoader.getSystemClassLoader();
+ } catch(SecurityException ex) {
+ logDiagnostic(
+ prefix + "Security forbids determining the system classloader.");
+ return;
+ }
+ if (classLoader != null) {
+ StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:");
+ for(;;) {
+ buf.append(objectId(classLoader));
+ if (classLoader == systemClassLoader) {
+ buf.append(" (SYSTEM) ");
+ }
+
+ try {
+ classLoader = classLoader.getParent();
+ } catch(SecurityException ex) {
+ buf.append(" --> SECRET");
+ break;
+ }
+
+ buf.append(" --> ");
+ if (classLoader == null) {
+ buf.append("BOOT");
+ break;
+ }
+ }
+ logDiagnostic(buf.toString());
+ }
+ }
+
+ /**
+ * Returns a string that uniquely identifies the specified object, including
+ * its class.
+ * <p>
+ * The returned string is of form "classname@hashcode", ie is the same as
+ * the return value of the Object.toString() method, but works even when
+ * the specified object's class has overidden the toString method.
+ *
+ * @param o may be null.
+ * @return a string of form classname@hashcode, or "null" if param o is null.
+ * @since 1.1
+ */
+ public static String objectId(Object o) {
+ if (o == null) {
+ return "null";
+ } else {
+ return o.getClass().getName() + "@" + System.identityHashCode(o);
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Static initialiser block to perform initialisation at class load time.
+ //
+ // We can't do this in the class constructor, as there are many
+ // static methods on this class that can be called before any
+ // LogFactory instances are created, and they depend upon this
+ // stuff having been set up.
+ //
+ // Note that this block must come after any variable declarations used
+ // by any methods called from this block, as we want any static initialiser
+ // associated with the variable to run first. If static initialisers for
+ // variables run after this code, then (a) their value might be needed
+ // by methods called from here, and (b) they might *override* any value
+ // computed here!
+ //
+ // So the wisest thing to do is just to place this code at the very end
+ // of the class file.
+ // ----------------------------------------------------------------------
+
+ static {
+ // note: it's safe to call methods before initDiagnostics.
+ thisClassLoader = getClassLoader(LogFactory.class);
+ initDiagnostics();
+ logClassLoaderEnvironment(LogFactory.class);
+ factories = createFactoryStore();
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("BOOTSTRAP COMPLETED");
+ }
+ }
+}
diff --git a/src/org/apache/commons/logging/LogSource.java b/src/org/apache/commons/logging/LogSource.java
new file mode 100644
index 0000000..e3c0603
--- /dev/null
+++ b/src/org/apache/commons/logging/LogSource.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging;
+
+
+import java.lang.reflect.Constructor;
+import java.util.Hashtable;
+
+import org.apache.commons.logging.impl.NoOpLog;
+
+
+/**
+ * <p>Factory for creating {@link Log} instances. Applications should call
+ * the <code>makeNewLogInstance()</code> method to instantiate new instances
+ * of the configured {@link Log} implementation class.</p>
+ *
+ * <p>By default, calling <code>getInstance()</code> will use the following
+ * algorithm:</p>
+ * <ul>
+ * <li>If Log4J is available, return an instance of
+ * <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
+ * <li>If JDK 1.4 or later is available, return an instance of
+ * <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
+ * <li>Otherwise, return an instance of
+ * <code>org.apache.commons.logging.impl.NoOpLog</code>.</li>
+ * </ul>
+ *
+ * <p>You can change the default behavior in one of two ways:</p>
+ * <ul>
+ * <li>On the startup command line, set the system property
+ * <code>org.apache.commons.logging.log</code> to the name of the
+ * <code>org.apache.commons.logging.Log</code> implementation class
+ * you want to use.</li>
+ * <li>At runtime, call <code>LogSource.setLogImplementation()</code>.</li>
+ * </ul>
+ *
+ * @deprecated Use {@link LogFactory} instead - The default factory
+ * implementation performs exactly the same algorithm as this class did
+ *
+ * @author Rod Waldhoff
+ * @version $Id: LogSource.java 155426 2005-02-26 13:10:49Z dirkv $
+ */
+public class LogSource {
+
+ // ------------------------------------------------------- Class Attributes
+
+ static protected Hashtable logs = new Hashtable();
+
+ /** Is log4j available (in the current classpath) */
+ static protected boolean log4jIsAvailable = false;
+
+ /** Is JDK 1.4 logging available */
+ static protected boolean jdk14IsAvailable = false;
+
+ /** Constructor for current log class */
+ static protected Constructor logImplctor = null;
+
+
+ // ----------------------------------------------------- Class Initializers
+
+ static {
+
+ // Is Log4J Available?
+ try {
+ if (null != Class.forName("org.apache.log4j.Logger")) {
+ log4jIsAvailable = true;
+ } else {
+ log4jIsAvailable = false;
+ }
+ } catch (Throwable t) {
+ log4jIsAvailable = false;
+ }
+
+ // Is JDK 1.4 Logging Available?
+ try {
+ if ((null != Class.forName("java.util.logging.Logger")) &&
+ (null != Class.forName("org.apache.commons.logging.impl.Jdk14Logger"))) {
+ jdk14IsAvailable = true;
+ } else {
+ jdk14IsAvailable = false;
+ }
+ } catch (Throwable t) {
+ jdk14IsAvailable = false;
+ }
+
+ // Set the default Log implementation
+ String name = null;
+ try {
+ name = System.getProperty("org.apache.commons.logging.log");
+ if (name == null) {
+ name = System.getProperty("org.apache.commons.logging.Log");
+ }
+ } catch (Throwable t) {
+ }
+ if (name != null) {
+ try {
+ setLogImplementation(name);
+ } catch (Throwable t) {
+ try {
+ setLogImplementation
+ ("org.apache.commons.logging.impl.NoOpLog");
+ } catch (Throwable u) {
+ ;
+ }
+ }
+ } else {
+ try {
+ if (log4jIsAvailable) {
+ setLogImplementation
+ ("org.apache.commons.logging.impl.Log4JLogger");
+ } else if (jdk14IsAvailable) {
+ setLogImplementation
+ ("org.apache.commons.logging.impl.Jdk14Logger");
+ } else {
+ setLogImplementation
+ ("org.apache.commons.logging.impl.NoOpLog");
+ }
+ } catch (Throwable t) {
+ try {
+ setLogImplementation
+ ("org.apache.commons.logging.impl.NoOpLog");
+ } catch (Throwable u) {
+ ;
+ }
+ }
+ }
+
+ }
+
+
+ // ------------------------------------------------------------ Constructor
+
+
+ /** Don't allow others to create instances */
+ private LogSource() {
+ }
+
+
+ // ---------------------------------------------------------- Class Methods
+
+
+ /**
+ * Set the log implementation/log implementation factory
+ * by the name of the class. The given class
+ * must implement {@link Log}, and provide a constructor that
+ * takes a single {@link String} argument (containing the name
+ * of the log).
+ */
+ static public void setLogImplementation(String classname) throws
+ LinkageError, ExceptionInInitializerError,
+ NoSuchMethodException, SecurityException,
+ ClassNotFoundException {
+ try {
+ Class logclass = Class.forName(classname);
+ Class[] argtypes = new Class[1];
+ argtypes[0] = "".getClass();
+ logImplctor = logclass.getConstructor(argtypes);
+ } catch (Throwable t) {
+ logImplctor = null;
+ }
+ }
+
+
+ /**
+ * Set the log implementation/log implementation factory
+ * by class. The given class must implement {@link Log},
+ * and provide a constructor that takes a single {@link String}
+ * argument (containing the name of the log).
+ */
+ static public void setLogImplementation(Class logclass) throws
+ LinkageError, ExceptionInInitializerError,
+ NoSuchMethodException, SecurityException {
+ Class[] argtypes = new Class[1];
+ argtypes[0] = "".getClass();
+ logImplctor = logclass.getConstructor(argtypes);
+ }
+
+
+ /** Get a <code>Log</code> instance by class name */
+ static public Log getInstance(String name) {
+ Log log = (Log) (logs.get(name));
+ if (null == log) {
+ log = makeNewLogInstance(name);
+ logs.put(name, log);
+ }
+ return log;
+ }
+
+
+ /** Get a <code>Log</code> instance by class */
+ static public Log getInstance(Class clazz) {
+ return getInstance(clazz.getName());
+ }
+
+
+ /**
+ * Create a new {@link Log} implementation, based
+ * on the given <i>name</i>.
+ * <p>
+ * The specific {@link Log} implementation returned
+ * is determined by the value of the
+ * <tt>org.apache.commons.logging.log</tt> property.
+ * The value of <tt>org.apache.commons.logging.log</tt> may be set to
+ * the fully specified name of a class that implements
+ * the {@link Log} interface. This class must also
+ * have a public constructor that takes a single
+ * {@link String} argument (containing the <i>name</i>
+ * of the {@link Log} to be constructed.
+ * <p>
+ * When <tt>org.apache.commons.logging.log</tt> is not set,
+ * or when no corresponding class can be found,
+ * this method will return a Log4JLogger
+ * if the log4j Logger class is
+ * available in the {@link LogSource}'s classpath, or a
+ * Jdk14Logger if we are on a JDK 1.4 or later system, or
+ * NoOpLog if neither of the above conditions is true.
+ *
+ * @param name the log name (or category)
+ */
+ static public Log makeNewLogInstance(String name) {
+
+ Log log = null;
+ try {
+ Object[] args = new Object[1];
+ args[0] = name;
+ log = (Log) (logImplctor.newInstance(args));
+ } catch (Throwable t) {
+ log = null;
+ }
+ if (null == log) {
+ log = new NoOpLog(name);
+ }
+ return log;
+
+ }
+
+
+ /**
+ * Returns a {@link String} array containing the names of
+ * all logs known to me.
+ */
+ static public String[] getLogNames() {
+ return (String[]) (logs.keySet().toArray(new String[logs.size()]));
+ }
+
+
+}
diff --git a/src/org/apache/commons/logging/impl/Jdk14Logger.java b/src/org/apache/commons/logging/impl/Jdk14Logger.java
new file mode 100644
index 0000000..d4f840c
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/Jdk14Logger.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging.impl;
+
+
+import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.logging.Log;
+
+
+/**
+ * <p>Implementation of the <code>org.apache.commons.logging.Log</code>
+ * interface that wraps the standard JDK logging mechanisms that were
+ * introduced in the Merlin release (JDK 1.4).</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
+ * @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
+ * @version $Revision: 370652 $ $Date: 2006-01-19 22:23:48 +0000 (Thu, 19 Jan 2006) $
+ */
+
+public class Jdk14Logger implements Log, Serializable {
+
+ /**
+ * This member variable simply ensures that any attempt to initialise
+ * this class in a pre-1.4 JVM will result in an ExceptionInInitializerError.
+ * It must not be private, as an optimising compiler could detect that it
+ * is not used and optimise it away.
+ */
+ protected static final Level dummyLevel = Level.FINE;
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Construct a named instance of this Logger.
+ *
+ * @param name Name of the logger to be constructed
+ */
+ public Jdk14Logger(String name) {
+
+ this.name = name;
+ logger = getLogger();
+
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The underlying Logger implementation we are using.
+ */
+ protected transient Logger logger = null;
+
+
+ /**
+ * The name of the logger we are wrapping.
+ */
+ protected String name = null;
+
+
+ // --------------------------------------------------------- Public Methods
+
+ private void log( Level level, String msg, Throwable ex ) {
+
+ Logger logger = getLogger();
+ if (logger.isLoggable(level)) {
+ // Hack (?) to get the stack trace.
+ Throwable dummyException=new Throwable();
+ StackTraceElement locations[]=dummyException.getStackTrace();
+ // Caller will be the third element
+ String cname="unknown";
+ String method="unknown";
+ if( locations!=null && locations.length >2 ) {
+ StackTraceElement caller=locations[2];
+ cname=caller.getClassName();
+ method=caller.getMethodName();
+ }
+ if( ex==null ) {
+ logger.logp( level, cname, method, msg );
+ } else {
+ logger.logp( level, cname, method, msg, ex );
+ }
+ }
+
+ }
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.FINE</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#debug(Object)
+ */
+ public void debug(Object message) {
+ log(Level.FINE, String.valueOf(message), null);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.FINE</code>.
+ *
+ * @param message to log
+ * @param exception log this cause
+ * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+ */
+ public void debug(Object message, Throwable exception) {
+ log(Level.FINE, String.valueOf(message), exception);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#error(Object)
+ */
+ public void error(Object message) {
+ log(Level.SEVERE, String.valueOf(message), null);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+ *
+ * @param message to log
+ * @param exception log this cause
+ * @see org.apache.commons.logging.Log#error(Object, Throwable)
+ */
+ public void error(Object message, Throwable exception) {
+ log(Level.SEVERE, String.valueOf(message), exception);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#fatal(Object)
+ */
+ public void fatal(Object message) {
+ log(Level.SEVERE, String.valueOf(message), null);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+ *
+ * @param message to log
+ * @param exception log this cause
+ * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+ */
+ public void fatal(Object message, Throwable exception) {
+ log(Level.SEVERE, String.valueOf(message), exception);
+ }
+
+
+ /**
+ * Return the native Logger instance we are using.
+ */
+ public Logger getLogger() {
+ if (logger == null) {
+ logger = Logger.getLogger(name);
+ }
+ return (logger);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.INFO</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#info(Object)
+ */
+ public void info(Object message) {
+ log(Level.INFO, String.valueOf(message), null);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.INFO</code>.
+ *
+ * @param message to log
+ * @param exception log this cause
+ * @see org.apache.commons.logging.Log#info(Object, Throwable)
+ */
+ public void info(Object message, Throwable exception) {
+ log(Level.INFO, String.valueOf(message), exception);
+ }
+
+
+ /**
+ * Is debug logging currently enabled?
+ */
+ public boolean isDebugEnabled() {
+ return (getLogger().isLoggable(Level.FINE));
+ }
+
+
+ /**
+ * Is error logging currently enabled?
+ */
+ public boolean isErrorEnabled() {
+ return (getLogger().isLoggable(Level.SEVERE));
+ }
+
+
+ /**
+ * Is fatal logging currently enabled?
+ */
+ public boolean isFatalEnabled() {
+ return (getLogger().isLoggable(Level.SEVERE));
+ }
+
+
+ /**
+ * Is info logging currently enabled?
+ */
+ public boolean isInfoEnabled() {
+ return (getLogger().isLoggable(Level.INFO));
+ }
+
+
+ /**
+ * Is trace logging currently enabled?
+ */
+ public boolean isTraceEnabled() {
+ return (getLogger().isLoggable(Level.FINEST));
+ }
+
+
+ /**
+ * Is warn logging currently enabled?
+ */
+ public boolean isWarnEnabled() {
+ return (getLogger().isLoggable(Level.WARNING));
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#trace(Object)
+ */
+ public void trace(Object message) {
+ log(Level.FINEST, String.valueOf(message), null);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+ *
+ * @param message to log
+ * @param exception log this cause
+ * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+ */
+ public void trace(Object message, Throwable exception) {
+ log(Level.FINEST, String.valueOf(message), exception);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#warn(Object)
+ */
+ public void warn(Object message) {
+ log(Level.WARNING, String.valueOf(message), null);
+ }
+
+
+ /**
+ * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+ *
+ * @param message to log
+ * @param exception log this cause
+ * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+ */
+ public void warn(Object message, Throwable exception) {
+ log(Level.WARNING, String.valueOf(message), exception);
+ }
+
+
+}
diff --git a/src/org/apache/commons/logging/impl/LogFactoryImpl.java b/src/org/apache/commons/logging/impl/LogFactoryImpl.java
new file mode 100644
index 0000000..8937b2f
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/LogFactoryImpl.java
@@ -0,0 +1,1400 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging.impl;
+
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * <p>Concrete subclass of {@link LogFactory} that implements the
+ * following algorithm to dynamically select a logging implementation
+ * class to instantiate a wrapper for.</p>
+ * <ul>
+ * <li>Use a factory configuration attribute named
+ * <code>org.apache.commons.logging.Log</code> to identify the
+ * requested implementation class.</li>
+ * <li>Use the <code>org.apache.commons.logging.Log</code> system property
+ * to identify the requested implementation class.</li>
+ * <li>If <em>Log4J</em> is available, return an instance of
+ * <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
+ * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
+ * <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
+ * <li>Otherwise, return an instance of
+ * <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
+ * </ul>
+ *
+ * <p>If the selected {@link Log} implementation class has a
+ * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
+ * parameter, this method will be called on each newly created instance
+ * to identify the associated factory. This makes factory configuration
+ * attributes available to the Log instance, if it so desires.</p>
+ *
+ * <p>This factory will remember previously created <code>Log</code> instances
+ * for the same name, and will return them on repeated requests to the
+ * <code>getInstance()</code> method.</p>
+ *
+ * @author Rod Waldhoff
+ * @author Craig R. McClanahan
+ * @author Richard A. Sitze
+ * @author Brian Stansberry
+ * @version $Revision: 399224 $ $Date: 2006-05-03 10:25:54 +0100 (Wed, 03 May 2006) $
+ */
+
+public class LogFactoryImpl extends LogFactory {
+
+
+ /** Log4JLogger class name */
+ private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger";
+ /** Jdk14Logger class name */
+ private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger";
+ /** Jdk13LumberjackLogger class name */
+ private static final String LOGGING_IMPL_LUMBERJACK_LOGGER = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
+ /** SimpleLog class name */
+ private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog";
+
+ private static final String PKG_IMPL="org.apache.commons.logging.impl.";
+ private static final int PKG_LEN = PKG_IMPL.length();
+
+ // ----------------------------------------------------------- Constructors
+
+
+
+ /**
+ * Public no-arguments constructor required by the lookup mechanism.
+ */
+ public LogFactoryImpl() {
+ super();
+ initDiagnostics(); // method on this object
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Instance created.");
+ }
+ }
+
+
+ // ----------------------------------------------------- Manifest Constants
+
+
+ /**
+ * The name (<code>org.apache.commons.logging.Log</code>) of the system
+ * property identifying our {@link Log} implementation class.
+ */
+ public static final String LOG_PROPERTY =
+ "org.apache.commons.logging.Log";
+
+
+ /**
+ * The deprecated system property used for backwards compatibility with
+ * old versions of JCL.
+ */
+ protected static final String LOG_PROPERTY_OLD =
+ "org.apache.commons.logging.log";
+
+ /**
+ * The name (<code>org.apache.commons.logging.Log.allowFlawedContext</code>)
+ * of the system property which can be set true/false to
+ * determine system behaviour when a bad context-classloader is encountered.
+ * When set to false, a LogConfigurationException is thrown if
+ * LogFactoryImpl is loaded via a child classloader of the TCCL (this
+ * should never happen in sane systems).
+ *
+ * Default behaviour: true (tolerates bad context classloaders)
+ *
+ * See also method setAttribute.
+ */
+ public static final String ALLOW_FLAWED_CONTEXT_PROPERTY =
+ "org.apache.commons.logging.Log.allowFlawedContext";
+
+ /**
+ * The name (<code>org.apache.commons.logging.Log.allowFlawedDiscovery</code>)
+ * of the system property which can be set true/false to
+ * determine system behaviour when a bad logging adapter class is
+ * encountered during logging discovery. When set to false, an
+ * exception will be thrown and the app will fail to start. When set
+ * to true, discovery will continue (though the user might end up
+ * with a different logging implementation than they expected).
+ *
+ * Default behaviour: true (tolerates bad logging adapters)
+ *
+ * See also method setAttribute.
+ */
+ public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY =
+ "org.apache.commons.logging.Log.allowFlawedDiscovery";
+
+ /**
+ * The name (<code>org.apache.commons.logging.Log.allowFlawedHierarchy</code>)
+ * of the system property which can be set true/false to
+ * determine system behaviour when a logging adapter class is
+ * encountered which has bound to the wrong Log class implementation.
+ * When set to false, an exception will be thrown and the app will fail
+ * to start. When set to true, discovery will continue (though the user
+ * might end up with a different logging implementation than they expected).
+ *
+ * Default behaviour: true (tolerates bad Log class hierarchy)
+ *
+ * See also method setAttribute.
+ */
+ public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY =
+ "org.apache.commons.logging.Log.allowFlawedHierarchy";
+
+
+ /**
+ * The names of classes that will be tried (in order) as logging
+ * adapters. Each class is expected to implement the Log interface,
+ * and to throw NoClassDefFound or ExceptionInInitializerError when
+ * loaded if the underlying logging library is not available. Any
+ * other error indicates that the underlying logging library is available
+ * but broken/unusable for some reason.
+ */
+ private static final String[] classesToDiscover = {
+ LOGGING_IMPL_LOG4J_LOGGER,
+ "org.apache.commons.logging.impl.Jdk14Logger",
+ "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
+ "org.apache.commons.logging.impl.SimpleLog"
+ };
+
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * Determines whether logging classes should be loaded using the thread-context
+ * classloader, or via the classloader that loaded this LogFactoryImpl class.
+ */
+ private boolean useTCCL = true;
+
+ /**
+ * The string prefixed to every message output by the logDiagnostic method.
+ */
+ private String diagnosticPrefix;
+
+
+ /**
+ * Configuration attributes.
+ */
+ protected Hashtable attributes = new Hashtable();
+
+
+ /**
+ * The {@link org.apache.commons.logging.Log} instances that have
+ * already been created, keyed by logger name.
+ */
+ protected Hashtable instances = new Hashtable();
+
+
+ /**
+ * Name of the class implementing the Log interface.
+ */
+ private String logClassName;
+
+
+ /**
+ * The one-argument constructor of the
+ * {@link org.apache.commons.logging.Log}
+ * implementation class that will be used to create new instances.
+ * This value is initialized by <code>getLogConstructor()</code>,
+ * and then returned repeatedly.
+ */
+ protected Constructor logConstructor = null;
+
+
+ /**
+ * The signature of the Constructor to be used.
+ */
+ protected Class logConstructorSignature[] =
+ { java.lang.String.class };
+
+
+ /**
+ * The one-argument <code>setLogFactory</code> method of the selected
+ * {@link org.apache.commons.logging.Log} method, if it exists.
+ */
+ protected Method logMethod = null;
+
+
+ /**
+ * The signature of the <code>setLogFactory</code> method to be used.
+ */
+ protected Class logMethodSignature[] =
+ { LogFactory.class };
+
+ /**
+ * See getBaseClassLoader and initConfiguration.
+ */
+ private boolean allowFlawedContext;
+
+ /**
+ * See handleFlawedDiscovery and initConfiguration.
+ */
+ private boolean allowFlawedDiscovery;
+
+ /**
+ * See handleFlawedHierarchy and initConfiguration.
+ */
+ private boolean allowFlawedHierarchy;
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Return the configuration attribute with the specified name (if any),
+ * or <code>null</code> if there is no such attribute.
+ *
+ * @param name Name of the attribute to return
+ */
+ public Object getAttribute(String name) {
+
+ return (attributes.get(name));
+
+ }
+
+
+ /**
+ * Return an array containing the names of all currently defined
+ * configuration attributes. If there are no such attributes, a zero
+ * length array is returned.
+ */
+ public String[] getAttributeNames() {
+
+ Vector names = new Vector();
+ Enumeration keys = attributes.keys();
+ while (keys.hasMoreElements()) {
+ names.addElement((String) keys.nextElement());
+ }
+ String results[] = new String[names.size()];
+ for (int i = 0; i < results.length; i++) {
+ results[i] = (String) names.elementAt(i);
+ }
+ return (results);
+
+ }
+
+
+ /**
+ * Convenience method to derive a name from the specified class and
+ * call <code>getInstance(String)</code> with it.
+ *
+ * @param clazz Class for which a suitable Log name will be derived
+ *
+ * @exception LogConfigurationException if a suitable <code>Log</code>
+ * instance cannot be returned
+ */
+ public Log getInstance(Class clazz) throws LogConfigurationException {
+
+ return (getInstance(clazz.getName()));
+
+ }
+
+
+ /**
+ * <p>Construct (if necessary) and return a <code>Log</code> instance,
+ * using the factory's current set of configuration attributes.</p>
+ *
+ * <p><strong>NOTE</strong> - Depending upon the implementation of
+ * the <code>LogFactory</code> you are using, the <code>Log</code>
+ * instance you are returned may or may not be local to the current
+ * application, and may or may not be returned again on a subsequent
+ * call with the same name argument.</p>
+ *
+ * @param name Logical name of the <code>Log</code> instance to be
+ * returned (the meaning of this name is only known to the underlying
+ * logging implementation that is being wrapped)
+ *
+ * @exception LogConfigurationException if a suitable <code>Log</code>
+ * instance cannot be returned
+ */
+ public Log getInstance(String name) throws LogConfigurationException {
+
+ Log instance = (Log) instances.get(name);
+ if (instance == null) {
+ instance = newInstance(name);
+ instances.put(name, instance);
+ }
+ return (instance);
+
+ }
+
+
+ /**
+ * Release any internal references to previously created
+ * {@link org.apache.commons.logging.Log}
+ * instances returned by this factory. This is useful in environments
+ * like servlet containers, which implement application reloading by
+ * throwing away a ClassLoader. Dangling references to objects in that
+ * class loader would prevent garbage collection.
+ */
+ public void release() {
+
+ logDiagnostic("Releasing all known loggers");
+ instances.clear();
+ }
+
+
+ /**
+ * Remove any configuration attribute associated with the specified name.
+ * If there is no such attribute, no action is taken.
+ *
+ * @param name Name of the attribute to remove
+ */
+ public void removeAttribute(String name) {
+
+ attributes.remove(name);
+
+ }
+
+
+ /**
+ * Set the configuration attribute with the specified name. Calling
+ * this with a <code>null</code> value is equivalent to calling
+ * <code>removeAttribute(name)</code>.
+ * <p>
+ * This method can be used to set logging configuration programmatically
+ * rather than via system properties. It can also be used in code running
+ * within a container (such as a webapp) to configure behaviour on a
+ * per-component level instead of globally as system properties would do.
+ * To use this method instead of a system property, call
+ * <pre>
+ * LogFactory.getFactory().setAttribute(...)
+ * </pre>
+ * This must be done before the first Log object is created; configuration
+ * changes after that point will be ignored.
+ * <p>
+ * This method is also called automatically if LogFactory detects a
+ * commons-logging.properties file; every entry in that file is set
+ * automatically as an attribute here.
+ *
+ * @param name Name of the attribute to set
+ * @param value Value of the attribute to set, or <code>null</code>
+ * to remove any setting for this attribute
+ */
+ public void setAttribute(String name, Object value) {
+
+ if (logConstructor != null) {
+ logDiagnostic("setAttribute: call too late; configuration already performed.");
+ }
+
+ if (value == null) {
+ attributes.remove(name);
+ } else {
+ attributes.put(name, value);
+ }
+
+ if (name.equals(TCCL_KEY)) {
+ useTCCL = Boolean.valueOf(value.toString()).booleanValue();
+ }
+
+ }
+
+
+ // ------------------------------------------------------
+ // Static Methods
+ //
+ // These methods only defined as workarounds for a java 1.2 bug;
+ // theoretically none of these are needed.
+ // ------------------------------------------------------
+
+ /**
+ * Gets the context classloader.
+ * This method is a workaround for a java 1.2 compiler bug.
+ * @since 1.1
+ */
+ protected static ClassLoader getContextClassLoader() throws LogConfigurationException {
+ return LogFactory.getContextClassLoader();
+ }
+
+
+ /**
+ * Workaround for bug in Java1.2; in theory this method is not needed.
+ * See LogFactory.isDiagnosticsEnabled.
+ */
+ protected static boolean isDiagnosticsEnabled() {
+ return LogFactory.isDiagnosticsEnabled();
+ }
+
+
+ /**
+ * Workaround for bug in Java1.2; in theory this method is not needed.
+ * See LogFactory.getClassLoader.
+ * @since 1.1
+ */
+ protected static ClassLoader getClassLoader(Class clazz) {
+ return LogFactory.getClassLoader(clazz);
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+ /**
+ * Calculate and cache a string that uniquely identifies this instance,
+ * including which classloader the object was loaded from.
+ * <p>
+ * This string will later be prefixed to each "internal logging" message
+ * emitted, so that users can clearly see any unexpected behaviour.
+ * <p>
+ * Note that this method does not detect whether internal logging is
+ * enabled or not, nor where to output stuff if it is; that is all
+ * handled by the parent LogFactory class. This method just computes
+ * its own unique prefix for log messages.
+ */
+ private void initDiagnostics() {
+ // It would be nice to include an identifier of the context classloader
+ // that this LogFactoryImpl object is responsible for. However that
+ // isn't possible as that information isn't available. It is possible
+ // to figure this out by looking at the logging from LogFactory to
+ // see the context & impl ids from when this object was instantiated,
+ // in order to link the impl id output as this object's prefix back to
+ // the context it is intended to manage.
+ // Note that this prefix should be kept consistent with that
+ // in LogFactory.
+ Class clazz = this.getClass();
+ ClassLoader classLoader = getClassLoader(clazz);
+ String classLoaderName;
+ try {
+ if (classLoader == null) {
+ classLoaderName = "BOOTLOADER";
+ } else {
+ classLoaderName = objectId(classLoader);
+ }
+ } catch(SecurityException e) {
+ classLoaderName = "UNKNOWN";
+ }
+ diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] ";
+ }
+
+
+ /**
+ * Output a diagnostic message to a user-specified destination (if the
+ * user has enabled diagnostic logging).
+ *
+ * @param msg diagnostic message
+ * @since 1.1
+ */
+ protected void logDiagnostic(String msg) {
+ if (isDiagnosticsEnabled()) {
+ logRawDiagnostic(diagnosticPrefix + msg);
+ }
+ }
+
+ /**
+ * Return the fully qualified Java classname of the {@link Log}
+ * implementation we will be using.
+ *
+ * @deprecated Never invoked by this class; subclasses should not assume
+ * it will be.
+ */
+ protected String getLogClassName() {
+
+ if (logClassName == null) {
+ discoverLogImplementation(getClass().getName());
+ }
+
+ return logClassName;
+ }
+
+
+ /**
+ * <p>Return the <code>Constructor</code> that can be called to instantiate
+ * new {@link org.apache.commons.logging.Log} instances.</p>
+ *
+ * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
+ * calling this method from more than one thread are ignored, because
+ * the same <code>Constructor</code> instance will ultimately be derived
+ * in all circumstances.</p>
+ *
+ * @exception LogConfigurationException if a suitable constructor
+ * cannot be returned
+ *
+ * @deprecated Never invoked by this class; subclasses should not assume
+ * it will be.
+ */
+ protected Constructor getLogConstructor()
+ throws LogConfigurationException {
+
+ // Return the previously identified Constructor (if any)
+ if (logConstructor == null) {
+ discoverLogImplementation(getClass().getName());
+ }
+
+ return logConstructor;
+ }
+
+
+ /**
+ * Is <em>JDK 1.3 with Lumberjack</em> logging available?
+ *
+ * @deprecated Never invoked by this class; subclasses should not assume
+ * it will be.
+ */
+ protected boolean isJdk13LumberjackAvailable() {
+ return isLogLibraryAvailable(
+ "Jdk13Lumberjack",
+ "org.apache.commons.logging.impl.Jdk13LumberjackLogger");
+ }
+
+
+ /**
+ * <p>Return <code>true</code> if <em>JDK 1.4 or later</em> logging
+ * is available. Also checks that the <code>Throwable</code> class
+ * supports <code>getStackTrace()</code>, which is required by
+ * Jdk14Logger.</p>
+ *
+ * @deprecated Never invoked by this class; subclasses should not assume
+ * it will be.
+ */
+ protected boolean isJdk14Available() {
+ return isLogLibraryAvailable(
+ "Jdk14",
+ "org.apache.commons.logging.impl.Jdk14Logger");
+ }
+
+
+ /**
+ * Is a <em>Log4J</em> implementation available?
+ *
+ * @deprecated Never invoked by this class; subclasses should not assume
+ * it will be.
+ */
+ protected boolean isLog4JAvailable() {
+ return isLogLibraryAvailable(
+ "Log4J",
+ LOGGING_IMPL_LOG4J_LOGGER);
+ }
+
+
+ /**
+ * Create and return a new {@link org.apache.commons.logging.Log}
+ * instance for the specified name.
+ *
+ * @param name Name of the new logger
+ *
+ * @exception LogConfigurationException if a new instance cannot
+ * be created
+ */
+ protected Log newInstance(String name) throws LogConfigurationException {
+
+ Log instance = null;
+ try {
+ if (logConstructor == null) {
+ instance = discoverLogImplementation(name);
+ }
+ else {
+ Object params[] = { name };
+ instance = (Log) logConstructor.newInstance(params);
+ }
+
+ if (logMethod != null) {
+ Object params[] = { this };
+ logMethod.invoke(instance, params);
+ }
+
+ return (instance);
+
+ } catch (LogConfigurationException lce) {
+
+ // this type of exception means there was a problem in discovery
+ // and we've already output diagnostics about the issue, etc.;
+ // just pass it on
+ throw (LogConfigurationException) lce;
+
+ } catch (InvocationTargetException e) {
+ // A problem occurred invoking the Constructor or Method
+ // previously discovered
+ Throwable c = e.getTargetException();
+ if (c != null) {
+ throw new LogConfigurationException(c);
+ } else {
+ throw new LogConfigurationException(e);
+ }
+ } catch (Throwable t) {
+ // A problem occurred invoking the Constructor or Method
+ // previously discovered
+ throw new LogConfigurationException(t);
+ }
+ }
+
+
+ // ------------------------------------------------------ Private Methods
+
+ /**
+ * Utility method to check whether a particular logging library is
+ * present and available for use. Note that this does <i>not</i>
+ * affect the future behaviour of this class.
+ */
+ private boolean isLogLibraryAvailable(String name, String classname) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Checking for '" + name + "'.");
+ }
+ try {
+ Log log = createLogFromClass(
+ classname,
+ this.getClass().getName(), // dummy category
+ false);
+
+ if (log == null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Did not find '" + name + "'.");
+ }
+ return false;
+ } else {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Found '" + name + "'.");
+ }
+ return true;
+ }
+ } catch(LogConfigurationException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Logging system '" + name + "' is available but not useable.");
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Attempt to find an attribute (see method setAttribute) or a
+ * system property with the provided name and return its value.
+ * <p>
+ * The attributes associated with this object are checked before
+ * system properties in case someone has explicitly called setAttribute,
+ * or a configuration property has been set in a commons-logging.properties
+ * file.
+ *
+ * @return the value associated with the property, or null.
+ */
+ private String getConfigurationValue(String property) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("[ENV] Trying to get configuration for item " + property);
+ }
+
+ Object valueObj = getAttribute(property);
+ if (valueObj != null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property);
+ }
+ return valueObj.toString();
+ }
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("[ENV] No LogFactory attribute found for " + property);
+ }
+
+ try {
+ String value = System.getProperty(property);
+ if (value != null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("[ENV] Found system property [" + value + "] for " + property);
+ }
+ return value;
+ }
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("[ENV] No system property found for property " + property);
+ }
+ } catch (SecurityException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("[ENV] Security prevented reading system property " + property);
+ }
+ }
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("[ENV] No configuration defined for item " + property);
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the setting for the user-configurable behaviour specified by key.
+ * If nothing has explicitly been set, then return dflt.
+ */
+ private boolean getBooleanConfiguration(String key, boolean dflt) {
+ String val = getConfigurationValue(key);
+ if (val == null)
+ return dflt;
+ return Boolean.valueOf(val).booleanValue();
+ }
+
+ /**
+ * Initialize a number of variables that control the behaviour of this
+ * class and that can be tweaked by the user. This is done when the first
+ * logger is created, not in the constructor of this class, because we
+ * need to give the user a chance to call method setAttribute in order to
+ * configure this object.
+ */
+ private void initConfiguration() {
+ allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true);
+ allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true);
+ allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true);
+ }
+
+
+ /**
+ * Attempts to create a Log instance for the given category name.
+ * Follows the discovery process described in the class javadoc.
+ *
+ * @param logCategory the name of the log category
+ *
+ * @throws LogConfigurationException if an error in discovery occurs,
+ * or if no adapter at all can be instantiated
+ */
+ private Log discoverLogImplementation(String logCategory)
+ throws LogConfigurationException
+ {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Discovering a Log implementation...");
+ }
+
+ initConfiguration();
+
+ Log result = null;
+
+ // See if the user specified the Log implementation to use
+ String specifiedLogClassName = findUserSpecifiedLogClassName();
+
+ if (specifiedLogClassName != null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Attempting to load user-specified log class '" +
+ specifiedLogClassName + "'...");
+ }
+
+ result = createLogFromClass(specifiedLogClassName,
+ logCategory,
+ true);
+ if (result == null) {
+ StringBuffer messageBuffer = new StringBuffer("User-specified log class '");
+ messageBuffer.append(specifiedLogClassName);
+ messageBuffer.append("' cannot be found or is not useable.");
+
+ // Mistyping or misspelling names is a common fault.
+ // Construct a good error message, if we can
+ if (specifiedLogClassName != null) {
+ informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
+ informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
+ informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
+ informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);
+ }
+ throw new LogConfigurationException(messageBuffer.toString());
+ }
+
+ return result;
+ }
+
+ // No user specified log; try to discover what's on the classpath
+ //
+ // Note that we deliberately loop here over classesToDiscover and
+ // expect method createLogFromClass to loop over the possible source
+ // classloaders. The effect is:
+ // for each discoverable log adapter
+ // for each possible classloader
+ // see if it works
+ //
+ // It appears reasonable at first glance to do the opposite:
+ // for each possible classloader
+ // for each discoverable log adapter
+ // see if it works
+ //
+ // The latter certainly has advantages for user-installable logging
+ // libraries such as log4j; in a webapp for example this code should
+ // first check whether the user has provided any of the possible
+ // logging libraries before looking in the parent classloader.
+ // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4,
+ // and SimpleLog will always work in any JVM. So the loop would never
+ // ever look for logging libraries in the parent classpath. Yet many
+ // users would expect that putting log4j there would cause it to be
+ // detected (and this is the historical JCL behaviour). So we go with
+ // the first approach. A user that has bundled a specific logging lib
+ // in a webapp should use a commons-logging.properties file or a
+ // service file in META-INF to force use of that logging lib anyway,
+ // rather than relying on discovery.
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "No user-specified Log implementation; performing discovery" +
+ " using the standard supported logging implementations...");
+ }
+ for(int i=0; (i<classesToDiscover.length) && (result == null); ++i) {
+ result = createLogFromClass(classesToDiscover[i], logCategory, true);
+ }
+
+ if (result == null) {
+ throw new LogConfigurationException
+ ("No suitable Log implementation");
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Appends message if the given name is similar to the candidate.
+ * @param messageBuffer <code>StringBuffer</code> the message should be appended to,
+ * not null
+ * @param name the (trimmed) name to be test against the candidate, not null
+ * @param candidate the candidate name (not null)
+ */
+ private void informUponSimilarName(final StringBuffer messageBuffer, final String name,
+ final String candidate) {
+ if (name.equals(candidate)) {
+ // Don't suggest a name that is exactly the same as the one the
+ // user tried...
+ return;
+ }
+
+ // If the user provides a name that is in the right package, and gets
+ // the first 5 characters of the adapter class right (ignoring case),
+ // then suggest the candidate adapter class name.
+ if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) {
+ messageBuffer.append(" Did you mean '");
+ messageBuffer.append(candidate);
+ messageBuffer.append("'?");
+ }
+ }
+
+
+ /**
+ * Checks system properties and the attribute map for
+ * a Log implementation specified by the user under the
+ * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}.
+ *
+ * @return classname specified by the user, or <code>null</code>
+ */
+ private String findUserSpecifiedLogClassName()
+ {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'");
+ }
+ String specifiedClass = (String) getAttribute(LOG_PROPERTY);
+
+ if (specifiedClass == null) { // @deprecated
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Trying to get log class from attribute '" +
+ LOG_PROPERTY_OLD + "'");
+ }
+ specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);
+ }
+
+ if (specifiedClass == null) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Trying to get log class from system property '" +
+ LOG_PROPERTY + "'");
+ }
+ try {
+ specifiedClass = System.getProperty(LOG_PROPERTY);
+ } catch (SecurityException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("No access allowed to system property '" +
+ LOG_PROPERTY + "' - " + e.getMessage());
+ }
+ }
+ }
+
+ if (specifiedClass == null) { // @deprecated
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Trying to get log class from system property '" +
+ LOG_PROPERTY_OLD + "'");
+ }
+ try {
+ specifiedClass = System.getProperty(LOG_PROPERTY_OLD);
+ } catch (SecurityException e) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("No access allowed to system property '" +
+ LOG_PROPERTY_OLD + "' - " + e.getMessage());
+ }
+ }
+ }
+
+ // Remove any whitespace; it's never valid in a classname so its
+ // presence just means a user mistake. As we know what they meant,
+ // we may as well strip the spaces.
+ if (specifiedClass != null) {
+ specifiedClass = specifiedClass.trim();
+ }
+
+ return specifiedClass;
+ }
+
+
+ /**
+ * Attempts to load the given class, find a suitable constructor,
+ * and instantiate an instance of Log.
+ *
+ * @param logAdapterClassName classname of the Log implementation
+ *
+ * @param logCategory argument to pass to the Log implementation's
+ * constructor
+ *
+ * @param affectState <code>true</code> if this object's state should
+ * be affected by this method call, <code>false</code> otherwise.
+ *
+ * @return an instance of the given class, or null if the logging
+ * library associated with the specified adapter is not available.
+ *
+ * @throws LogConfigurationException if there was a serious error with
+ * configuration and the handleFlawedDiscovery method decided this
+ * problem was fatal.
+ */
+ private Log createLogFromClass(String logAdapterClassName,
+ String logCategory,
+ boolean affectState)
+ throws LogConfigurationException {
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");
+ }
+
+ Object[] params = { logCategory };
+ Log logAdapter = null;
+ Constructor constructor = null;
+
+ Class logAdapterClass = null;
+ ClassLoader currentCL = getBaseClassLoader();
+
+ for(;;) {
+ // Loop through the classloader hierarchy trying to find
+ // a viable classloader.
+ logDiagnostic(
+ "Trying to load '"
+ + logAdapterClassName
+ + "' from classloader "
+ + objectId(currentCL));
+ try {
+ if (isDiagnosticsEnabled()) {
+ // Show the location of the first occurrence of the .class file
+ // in the classpath. This is the location that ClassLoader.loadClass
+ // will load the class from -- unless the classloader is doing
+ // something weird.
+ URL url;
+ String resourceName = logAdapterClassName.replace('.', '/') + ".class";
+ if (currentCL != null) {
+ url = currentCL.getResource(resourceName );
+ } else {
+ url = ClassLoader.getSystemResource(resourceName + ".class");
+ }
+
+ if (url == null) {
+ logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");
+ } else {
+ logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");
+ }
+ }
+
+ Class c = null;
+ try {
+ c = Class.forName(logAdapterClassName, true, currentCL);
+ } catch (ClassNotFoundException originalClassNotFoundException) {
+ // The current classloader was unable to find the log adapter
+ // in this or any ancestor classloader. There's no point in
+ // trying higher up in the hierarchy in this case..
+ String msg = "" + originalClassNotFoundException.getMessage();
+ logDiagnostic(
+ "The log adapter '"
+ + logAdapterClassName
+ + "' is not available via classloader "
+ + objectId(currentCL)
+ + ": "
+ + msg.trim());
+ try {
+ // Try the class classloader.
+ // This may work in cases where the TCCL
+ // does not contain the code executed or JCL.
+ // This behaviour indicates that the application
+ // classloading strategy is not consistent with the
+ // Java 1.2 classloading guidelines but JCL can
+ // and so should handle this case.
+ c = Class.forName(logAdapterClassName);
+ } catch (ClassNotFoundException secondaryClassNotFoundException) {
+ // no point continuing: this adapter isn't available
+ msg = "" + secondaryClassNotFoundException.getMessage();
+ logDiagnostic(
+ "The log adapter '"
+ + logAdapterClassName
+ + "' is not available via the LogFactoryImpl class classloader: "
+ + msg.trim());
+ break;
+ }
+ }
+
+ constructor = c.getConstructor(logConstructorSignature);
+ Object o = constructor.newInstance(params);
+
+ // Note that we do this test after trying to create an instance
+ // [rather than testing Log.class.isAssignableFrom(c)] so that
+ // we don't complain about Log hierarchy problems when the
+ // adapter couldn't be instantiated anyway.
+ if (o instanceof Log) {
+ logAdapterClass = c;
+ logAdapter = (Log) o;
+ break;
+ }
+
+ // Oops, we have a potential problem here. An adapter class
+ // has been found and its underlying lib is present too, but
+ // there are multiple Log interface classes available making it
+ // impossible to cast to the type the caller wanted. We
+ // certainly can't use this logger, but we need to know whether
+ // to keep on discovering or terminate now.
+ //
+ // The handleFlawedHierarchy method will throw
+ // LogConfigurationException if it regards this problem as
+ // fatal, and just return if not.
+ handleFlawedHierarchy(currentCL, c);
+ } catch (NoClassDefFoundError e) {
+ // We were able to load the adapter but it had references to
+ // other classes that could not be found. This simply means that
+ // the underlying logger library is not present in this or any
+ // ancestor classloader. There's no point in trying higher up
+ // in the hierarchy in this case..
+ String msg = "" + e.getMessage();
+ logDiagnostic(
+ "The log adapter '"
+ + logAdapterClassName
+ + "' is missing dependencies when loaded via classloader "
+ + objectId(currentCL)
+ + ": "
+ + msg.trim());
+ break;
+ } catch (ExceptionInInitializerError e) {
+ // A static initializer block or the initializer code associated
+ // with a static variable on the log adapter class has thrown
+ // an exception.
+ //
+ // We treat this as meaning the adapter's underlying logging
+ // library could not be found.
+ String msg = "" + e.getMessage();
+ logDiagnostic(
+ "The log adapter '"
+ + logAdapterClassName
+ + "' is unable to initialize itself when loaded via classloader "
+ + objectId(currentCL)
+ + ": "
+ + msg.trim());
+ break;
+ } catch(LogConfigurationException e) {
+ // call to handleFlawedHierarchy above must have thrown
+ // a LogConfigurationException, so just throw it on
+ throw e;
+ } catch(Throwable t) {
+ // handleFlawedDiscovery will determine whether this is a fatal
+ // problem or not. If it is fatal, then a LogConfigurationException
+ // will be thrown.
+ handleFlawedDiscovery(logAdapterClassName, currentCL, t);
+ }
+
+ if (currentCL == null) {
+ break;
+ }
+
+ // try the parent classloader
+ currentCL = currentCL.getParent();
+ }
+
+ if ((logAdapter != null) && affectState) {
+ // We've succeeded, so set instance fields
+ this.logClassName = logAdapterClassName;
+ this.logConstructor = constructor;
+
+ // Identify the <code>setLogFactory</code> method (if there is one)
+ try {
+ this.logMethod = logAdapterClass.getMethod("setLogFactory",
+ logMethodSignature);
+ logDiagnostic("Found method setLogFactory(LogFactory) in '"
+ + logAdapterClassName + "'");
+ } catch (Throwable t) {
+ this.logMethod = null;
+ logDiagnostic(
+ "[INFO] '" + logAdapterClassName
+ + "' from classloader " + objectId(currentCL)
+ + " does not declare optional method "
+ + "setLogFactory(LogFactory)");
+ }
+
+ logDiagnostic(
+ "Log adapter '" + logAdapterClassName
+ + "' from classloader " + objectId(logAdapterClass.getClassLoader())
+ + " has been selected for use.");
+ }
+
+ return logAdapter;
+ }
+
+
+ /**
+ * Return the classloader from which we should try to load the logging
+ * adapter classes.
+ * <p>
+ * This method usually returns the context classloader. However if it
+ * is discovered that the classloader which loaded this class is a child
+ * of the context classloader <i>and</i> the allowFlawedContext option
+ * has been set then the classloader which loaded this class is returned
+ * instead.
+ * <p>
+ * The only time when the classloader which loaded this class is a
+ * descendant (rather than the same as or an ancestor of the context
+ * classloader) is when an app has created custom classloaders but
+ * failed to correctly set the context classloader. This is a bug in
+ * the calling application; however we provide the option for JCL to
+ * simply generate a warning rather than fail outright.
+ *
+ */
+ private ClassLoader getBaseClassLoader() throws LogConfigurationException {
+ ClassLoader thisClassLoader = getClassLoader(LogFactoryImpl.class);
+
+ if (useTCCL == false) {
+ return thisClassLoader;
+ }
+
+ ClassLoader contextClassLoader = getContextClassLoader();
+
+ ClassLoader baseClassLoader = getLowestClassLoader(
+ contextClassLoader, thisClassLoader);
+
+ if (baseClassLoader == null) {
+ // The two classloaders are not part of a parent child relationship.
+ // In some classloading setups (e.g. JBoss with its
+ // UnifiedLoaderRepository) this can still work, so if user hasn't
+ // forbidden it, just return the contextClassLoader.
+ if (allowFlawedContext) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "[WARNING] the context classloader is not part of a"
+ + " parent-child relationship with the classloader that"
+ + " loaded LogFactoryImpl.");
+ }
+ // If contextClassLoader were null, getLowestClassLoader() would
+ // have returned thisClassLoader. The fact we are here means
+ // contextClassLoader is not null, so we can just return it.
+ return contextClassLoader;
+ }
+ else {
+ throw new LogConfigurationException(
+ "Bad classloader hierarchy; LogFactoryImpl was loaded via"
+ + " a classloader that is not related to the current context"
+ + " classloader.");
+ }
+ }
+
+ if (baseClassLoader != contextClassLoader) {
+ // We really should just use the contextClassLoader as the starting
+ // point for scanning for log adapter classes. However it is expected
+ // that there are a number of broken systems out there which create
+ // custom classloaders but fail to set the context classloader so
+ // we handle those flawed systems anyway.
+ if (allowFlawedContext) {
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(
+ "Warning: the context classloader is an ancestor of the"
+ + " classloader that loaded LogFactoryImpl; it should be"
+ + " the same or a descendant. The application using"
+ + " commons-logging should ensure the context classloader"
+ + " is used correctly.");
+ }
+ } else {
+ throw new LogConfigurationException(
+ "Bad classloader hierarchy; LogFactoryImpl was loaded via"
+ + " a classloader that is not related to the current context"
+ + " classloader.");
+ }
+ }
+
+ return baseClassLoader;
+ }
+
+ /**
+ * Given two related classloaders, return the one which is a child of
+ * the other.
+ * <p>
+ * @param c1 is a classloader (including the null classloader)
+ * @param c2 is a classloader (including the null classloader)
+ *
+ * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor,
+ * and null if neither is an ancestor of the other.
+ */
+ private ClassLoader getLowestClassLoader(ClassLoader c1, ClassLoader c2) {
+ // TODO: use AccessController when dealing with classloaders here
+
+ if (c1 == null)
+ return c2;
+
+ if (c2 == null)
+ return c1;
+
+ ClassLoader current;
+
+ // scan c1's ancestors to find c2
+ current = c1;
+ while (current != null) {
+ if (current == c2)
+ return c1;
+ current = current.getParent();
+ }
+
+ // scan c2's ancestors to find c1
+ current = c2;
+ while (current != null) {
+ if (current == c1)
+ return c2;
+ current = current.getParent();
+ }
+
+ return null;
+ }
+
+ /**
+ * Generates an internal diagnostic logging of the discovery failure and
+ * then throws a <code>LogConfigurationException</code> that wraps
+ * the passed <code>Throwable</code>.
+ *
+ * @param logAdapterClassName is the class name of the Log implementation
+ * that could not be instantiated. Cannot be <code>null</code>.
+ *
+ * @param classLoader is the classloader that we were trying to load the
+ * logAdapterClassName from when the exception occurred.
+ *
+ * @param discoveryFlaw is the Throwable created by the classloader
+ *
+ * @throws LogConfigurationException ALWAYS
+ */
+ private void handleFlawedDiscovery(String logAdapterClassName,
+ ClassLoader classLoader,
+ Throwable discoveryFlaw) {
+
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic("Could not instantiate Log '"
+ + logAdapterClassName + "' -- "
+ + discoveryFlaw.getClass().getName() + ": "
+ + discoveryFlaw.getLocalizedMessage());
+ }
+
+ if (!allowFlawedDiscovery) {
+ throw new LogConfigurationException(discoveryFlaw);
+ }
+ }
+
+
+ /**
+ * Report a problem loading the log adapter, then either return
+ * (if the situation is considered recoverable) or throw a
+ * LogConfigurationException.
+ * <p>
+ * There are two possible reasons why we successfully loaded the
+ * specified log adapter class then failed to cast it to a Log object:
+ * <ol>
+ * <li>the specific class just doesn't implement the Log interface
+ * (user screwed up), or
+ * <li> the specified class has bound to a Log class loaded by some other
+ * classloader; Log@classloaderX cannot be cast to Log@classloaderY.
+ * </ol>
+ * <p>
+ * Here we try to figure out which case has occurred so we can give the
+ * user some reasonable feedback.
+ *
+ * @param badClassLoader is the classloader we loaded the problem class from,
+ * ie it is equivalent to badClass.getClassLoader().
+ *
+ * @param badClass is a Class object with the desired name, but which
+ * does not implement Log correctly.
+ *
+ * @throws LogConfigurationException when the situation
+ * should not be recovered from.
+ */
+ private void handleFlawedHierarchy(ClassLoader badClassLoader, Class badClass)
+ throws LogConfigurationException {
+
+ boolean implementsLog = false;
+ String logInterfaceName = Log.class.getName();
+ Class interfaces[] = badClass.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ if (logInterfaceName.equals(interfaces[i].getName())) {
+ implementsLog = true;
+ break;
+ }
+ }
+
+ if (implementsLog) {
+ // the class does implement an interface called Log, but
+ // it is in the wrong classloader
+ if (isDiagnosticsEnabled()) {
+ try {
+ ClassLoader logInterfaceClassLoader = getClassLoader(Log.class);
+ logDiagnostic(
+ "Class '" + badClass.getName()
+ + "' was found in classloader "
+ + objectId(badClassLoader)
+ + ". It is bound to a Log interface which is not"
+ + " the one loaded from classloader "
+ + objectId(logInterfaceClassLoader));
+ } catch (Throwable t) {
+ logDiagnostic(
+ "Error while trying to output diagnostics about"
+ + " bad class '" + badClass + "'");
+ }
+ }
+
+ if (!allowFlawedHierarchy) {
+ StringBuffer msg = new StringBuffer();
+ msg.append("Terminating logging for this context ");
+ msg.append("due to bad log hierarchy. ");
+ msg.append("You have more than one version of '");
+ msg.append(Log.class.getName());
+ msg.append("' visible.");
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(msg.toString());
+ }
+ throw new LogConfigurationException(msg.toString());
+ }
+
+ if (isDiagnosticsEnabled()) {
+ StringBuffer msg = new StringBuffer();
+ msg.append("Warning: bad log hierarchy. ");
+ msg.append("You have more than one version of '");
+ msg.append(Log.class.getName());
+ msg.append("' visible.");
+ logDiagnostic(msg.toString());
+ }
+ } else {
+ // this is just a bad adapter class
+ if (!allowFlawedDiscovery) {
+ StringBuffer msg = new StringBuffer();
+ msg.append("Terminating logging for this context. ");
+ msg.append("Log class '");
+ msg.append(badClass.getName());
+ msg.append("' does not implement the Log interface.");
+ if (isDiagnosticsEnabled()) {
+ logDiagnostic(msg.toString());
+ }
+
+ throw new LogConfigurationException(msg.toString());
+ }
+
+ if (isDiagnosticsEnabled()) {
+ StringBuffer msg = new StringBuffer();
+ msg.append("[WARNING] Log class '");
+ msg.append(badClass.getName());
+ msg.append("' does not implement the Log interface.");
+ logDiagnostic(msg.toString());
+ }
+ }
+ }
+}
diff --git a/src/org/apache/commons/logging/impl/NoOpLog.java b/src/org/apache/commons/logging/impl/NoOpLog.java
new file mode 100644
index 0000000..b698813
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/NoOpLog.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging.impl;
+
+
+import java.io.Serializable;
+import org.apache.commons.logging.Log;
+
+
+/**
+ * <p>Trivial implementation of Log that throws away all messages. No
+ * configurable system properties are supported.</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author Rod Waldhoff
+ * @version $Id: NoOpLog.java 155426 2005-02-26 13:10:49Z dirkv $
+ */
+public class NoOpLog implements Log, Serializable {
+
+ /** Convenience constructor */
+ public NoOpLog() { }
+ /** Base constructor */
+ public NoOpLog(String name) { }
+ /** Do nothing */
+ public void trace(Object message) { }
+ /** Do nothing */
+ public void trace(Object message, Throwable t) { }
+ /** Do nothing */
+ public void debug(Object message) { }
+ /** Do nothing */
+ public void debug(Object message, Throwable t) { }
+ /** Do nothing */
+ public void info(Object message) { }
+ /** Do nothing */
+ public void info(Object message, Throwable t) { }
+ /** Do nothing */
+ public void warn(Object message) { }
+ /** Do nothing */
+ public void warn(Object message, Throwable t) { }
+ /** Do nothing */
+ public void error(Object message) { }
+ /** Do nothing */
+ public void error(Object message, Throwable t) { }
+ /** Do nothing */
+ public void fatal(Object message) { }
+ /** Do nothing */
+ public void fatal(Object message, Throwable t) { }
+
+ /**
+ * Debug is never enabled.
+ *
+ * @return false
+ */
+ public final boolean isDebugEnabled() { return false; }
+
+ /**
+ * Error is never enabled.
+ *
+ * @return false
+ */
+ public final boolean isErrorEnabled() { return false; }
+
+ /**
+ * Fatal is never enabled.
+ *
+ * @return false
+ */
+ public final boolean isFatalEnabled() { return false; }
+
+ /**
+ * Info is never enabled.
+ *
+ * @return false
+ */
+ public final boolean isInfoEnabled() { return false; }
+
+ /**
+ * Trace is never enabled.
+ *
+ * @return false
+ */
+ public final boolean isTraceEnabled() { return false; }
+
+ /**
+ * Warn is never enabled.
+ *
+ * @return false
+ */
+ public final boolean isWarnEnabled() { return false; }
+
+}
diff --git a/src/org/apache/commons/logging/impl/SimpleLog.java b/src/org/apache/commons/logging/impl/SimpleLog.java
new file mode 100644
index 0000000..6b643d3
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/SimpleLog.java
@@ -0,0 +1,709 @@
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging.impl;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+
+/**
+ * <p>Simple implementation of Log that sends all enabled log messages,
+ * for all defined loggers, to System.err. The following system properties
+ * are supported to configure the behavior of this logger:</p>
+ * <ul>
+ * <li><code>org.apache.commons.logging.simplelog.defaultlog</code> -
+ * Default logging detail level for all instances of SimpleLog.
+ * Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
+ * If not specified, defaults to "info". </li>
+ * <li><code>org.apache.commons.logging.simplelog.log.xxxxx</code> -
+ * Logging detail level for a SimpleLog instance named "xxxxx".
+ * Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
+ * If not specified, the default logging detail level is used.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showlogname</code> -
+ * Set to <code>true</code> if you want the Log instance name to be
+ * included in output messages. Defaults to <code>false</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showShortLogname</code> -
+ * Set to <code>true</code> if you want the last component of the name to be
+ * included in output messages. Defaults to <code>true</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showdatetime</code> -
+ * Set to <code>true</code> if you want the current date and time
+ * to be included in output messages. Default is <code>false</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.dateTimeFormat</code> -
+ * The date and time format to be used in the output messages.
+ * The pattern describing the date and time format is the same that is
+ * used in <code>java.text.SimpleDateFormat</code>. If the format is not
+ * specified or is invalid, the default format is used.
+ * The default format is <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li>
+ * </ul>
+ *
+ * <p>In addition to looking for system properties with the names specified
+ * above, this implementation also checks for a class loader resource named
+ * <code>"simplelog.properties"</code>, and includes any matching definitions
+ * from this resource (if it exists).</p>
+ *
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author Rod Waldhoff
+ * @author Robert Burrell Donkin
+ *
+ * @version $Id: SimpleLog.java 399221 2006-05-03 09:20:24Z dennisl $
+ */
+public class SimpleLog implements Log, Serializable {
+
+
+ // ------------------------------------------------------- Class Attributes
+
+ /** All system properties used by <code>SimpleLog</code> start with this */
+ static protected final String systemPrefix =
+ "org.apache.commons.logging.simplelog.";
+
+ /** Properties loaded from simplelog.properties */
+ static protected final Properties simpleLogProps = new Properties();
+
+ /** The default format to use when formating dates */
+ static protected final String DEFAULT_DATE_TIME_FORMAT =
+ "yyyy/MM/dd HH:mm:ss:SSS zzz";
+
+ /** Include the instance name in the log message? */
+ static protected boolean showLogName = false;
+ /** Include the short name ( last component ) of the logger in the log
+ * message. Defaults to true - otherwise we'll be lost in a flood of
+ * messages without knowing who sends them.
+ */
+ static protected boolean showShortName = true;
+ /** Include the current time in the log message */
+ static protected boolean showDateTime = false;
+ /** The date and time format to use in the log message */
+ static protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
+ /** Used to format times */
+ static protected DateFormat dateFormatter = null;
+
+ // ---------------------------------------------------- Log Level Constants
+
+
+ /** "Trace" level logging. */
+ public static final int LOG_LEVEL_TRACE = 1;
+ /** "Debug" level logging. */
+ public static final int LOG_LEVEL_DEBUG = 2;
+ /** "Info" level logging. */
+ public static final int LOG_LEVEL_INFO = 3;
+ /** "Warn" level logging. */
+ public static final int LOG_LEVEL_WARN = 4;
+ /** "Error" level logging. */
+ public static final int LOG_LEVEL_ERROR = 5;
+ /** "Fatal" level logging. */
+ public static final int LOG_LEVEL_FATAL = 6;
+
+ /** Enable all logging levels */
+ public static final int LOG_LEVEL_ALL = (LOG_LEVEL_TRACE - 1);
+
+ /** Enable no logging levels */
+ public static final int LOG_LEVEL_OFF = (LOG_LEVEL_FATAL + 1);
+
+ // ------------------------------------------------------------ Initializer
+
+ private static String getStringProperty(String name) {
+ String prop = null;
+ try {
+ prop = System.getProperty(name);
+ } catch (SecurityException e) {
+ ; // Ignore
+ }
+ return (prop == null) ? simpleLogProps.getProperty(name) : prop;
+ }
+
+ private static String getStringProperty(String name, String dephault) {
+ String prop = getStringProperty(name);
+ return (prop == null) ? dephault : prop;
+ }
+
+ private static boolean getBooleanProperty(String name, boolean dephault) {
+ String prop = getStringProperty(name);
+ return (prop == null) ? dephault : "true".equalsIgnoreCase(prop);
+ }
+
+ // Initialize class attributes.
+ // Load properties file, if found.
+ // Override with system properties.
+ static {
+ // Add props from the resource simplelog.properties
+ InputStream in = getResourceAsStream("simplelog.properties");
+ if(null != in) {
+ try {
+ simpleLogProps.load(in);
+ in.close();
+ } catch(java.io.IOException e) {
+ // ignored
+ }
+ }
+
+ showLogName = getBooleanProperty( systemPrefix + "showlogname", showLogName);
+ showShortName = getBooleanProperty( systemPrefix + "showShortLogname", showShortName);
+ showDateTime = getBooleanProperty( systemPrefix + "showdatetime", showDateTime);
+
+ if(showDateTime) {
+ dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",
+ dateTimeFormat);
+ try {
+ dateFormatter = new SimpleDateFormat(dateTimeFormat);
+ } catch(IllegalArgumentException e) {
+ // If the format pattern is invalid - use the default format
+ dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
+ dateFormatter = new SimpleDateFormat(dateTimeFormat);
+ }
+ }
+ }
+
+
+ // ------------------------------------------------------------- Attributes
+
+ /** The name of this simple log instance */
+ protected String logName = null;
+ /** The current log level */
+ protected int currentLogLevel;
+ /** The short name of this simple log instance */
+ private String shortLogName = null;
+
+
+ // ------------------------------------------------------------ Constructor
+
+ /**
+ * Construct a simple log with given name.
+ *
+ * @param name log name
+ */
+ public SimpleLog(String name) {
+
+ logName = name;
+
+ // Set initial log level
+ // Used to be: set default log level to ERROR
+ // IMHO it should be lower, but at least info ( costin ).
+ setLevel(SimpleLog.LOG_LEVEL_INFO);
+
+ // Set log level from properties
+ String lvl = getStringProperty(systemPrefix + "log." + logName);
+ int i = String.valueOf(name).lastIndexOf(".");
+ while(null == lvl && i > -1) {
+ name = name.substring(0,i);
+ lvl = getStringProperty(systemPrefix + "log." + name);
+ i = String.valueOf(name).lastIndexOf(".");
+ }
+
+ if(null == lvl) {
+ lvl = getStringProperty(systemPrefix + "defaultlog");
+ }
+
+ if("all".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_ALL);
+ } else if("trace".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_TRACE);
+ } else if("debug".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_DEBUG);
+ } else if("info".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_INFO);
+ } else if("warn".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_WARN);
+ } else if("error".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_ERROR);
+ } else if("fatal".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_FATAL);
+ } else if("off".equalsIgnoreCase(lvl)) {
+ setLevel(SimpleLog.LOG_LEVEL_OFF);
+ }
+
+ }
+
+
+ // -------------------------------------------------------- Properties
+
+ /**
+ * <p> Set logging level. </p>
+ *
+ * @param currentLogLevel new logging level
+ */
+ public void setLevel(int currentLogLevel) {
+
+ this.currentLogLevel = currentLogLevel;
+
+ }
+
+
+ /**
+ * <p> Get logging level. </p>
+ */
+ public int getLevel() {
+
+ return currentLogLevel;
+ }
+
+
+ // -------------------------------------------------------- Logging Methods
+
+
+ /**
+ * <p> Do the actual logging.
+ * This method assembles the message
+ * and then calls <code>write()</code> to cause it to be written.</p>
+ *
+ * @param type One of the LOG_LEVEL_XXX constants defining the log level
+ * @param message The message itself (typically a String)
+ * @param t The exception whose stack trace should be logged
+ */
+ protected void log(int type, Object message, Throwable t) {
+ // Use a string buffer for better performance
+ StringBuffer buf = new StringBuffer();
+
+ // Append date-time if so configured
+ if(showDateTime) {
+ buf.append(dateFormatter.format(new Date()));
+ buf.append(" ");
+ }
+
+ // Append a readable representation of the log level
+ switch(type) {
+ case SimpleLog.LOG_LEVEL_TRACE: buf.append("[TRACE] "); break;
+ case SimpleLog.LOG_LEVEL_DEBUG: buf.append("[DEBUG] "); break;
+ case SimpleLog.LOG_LEVEL_INFO: buf.append("[INFO] "); break;
+ case SimpleLog.LOG_LEVEL_WARN: buf.append("[WARN] "); break;
+ case SimpleLog.LOG_LEVEL_ERROR: buf.append("[ERROR] "); break;
+ case SimpleLog.LOG_LEVEL_FATAL: buf.append("[FATAL] "); break;
+ }
+
+ // Append the name of the log instance if so configured
+ if( showShortName) {
+ if( shortLogName==null ) {
+ // Cut all but the last component of the name for both styles
+ shortLogName = logName.substring(logName.lastIndexOf(".") + 1);
+ shortLogName =
+ shortLogName.substring(shortLogName.lastIndexOf("/") + 1);
+ }
+ buf.append(String.valueOf(shortLogName)).append(" - ");
+ } else if(showLogName) {
+ buf.append(String.valueOf(logName)).append(" - ");
+ }
+
+ // Append the message
+ buf.append(String.valueOf(message));
+
+ // Append stack trace if not null
+ if(t != null) {
+ buf.append(" <");
+ buf.append(t.toString());
+ buf.append(">");
+
+ java.io.StringWriter sw= new java.io.StringWriter(1024);
+ java.io.PrintWriter pw= new java.io.PrintWriter(sw);
+ t.printStackTrace(pw);
+ pw.close();
+ buf.append(sw.toString());
+ }
+
+ // Print to the appropriate destination
+ write(buf);
+
+ }
+
+
+ /**
+ * <p>Write the content of the message accumulated in the specified
+ * <code>StringBuffer</code> to the appropriate output destination. The
+ * default implementation writes to <code>System.err</code>.</p>
+ *
+ * @param buffer A <code>StringBuffer</code> containing the accumulated
+ * text to be logged
+ */
+ protected void write(StringBuffer buffer) {
+
+ System.err.println(buffer.toString());
+
+ }
+
+
+ /**
+ * Is the given log level currently enabled?
+ *
+ * @param logLevel is this level enabled?
+ */
+ protected boolean isLevelEnabled(int logLevel) {
+ // log level are numerically ordered so can use simple numeric
+ // comparison
+ return (logLevel >= currentLogLevel);
+ }
+
+
+ // -------------------------------------------------------- Log Implementation
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#debug(Object)
+ */
+ public final void debug(Object message) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
+ log(SimpleLog.LOG_LEVEL_DEBUG, message, null);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG</code>.
+ *
+ * @param message to log
+ * @param t log this cause
+ * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+ */
+ public final void debug(Object message, Throwable t) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) {
+ log(SimpleLog.LOG_LEVEL_DEBUG, message, t);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#trace(Object)
+ */
+ public final void trace(Object message) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
+ log(SimpleLog.LOG_LEVEL_TRACE, message, null);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE</code>.
+ *
+ * @param message to log
+ * @param t log this cause
+ * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+ */
+ public final void trace(Object message, Throwable t) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) {
+ log(SimpleLog.LOG_LEVEL_TRACE, message, t);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#info(Object)
+ */
+ public final void info(Object message) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
+ log(SimpleLog.LOG_LEVEL_INFO,message,null);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO</code>.
+ *
+ * @param message to log
+ * @param t log this cause
+ * @see org.apache.commons.logging.Log#info(Object, Throwable)
+ */
+ public final void info(Object message, Throwable t) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) {
+ log(SimpleLog.LOG_LEVEL_INFO, message, t);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#warn(Object)
+ */
+ public final void warn(Object message) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
+ log(SimpleLog.LOG_LEVEL_WARN, message, null);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN</code>.
+ *
+ * @param message to log
+ * @param t log this cause
+ * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+ */
+ public final void warn(Object message, Throwable t) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) {
+ log(SimpleLog.LOG_LEVEL_WARN, message, t);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#error(Object)
+ */
+ public final void error(Object message) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
+ log(SimpleLog.LOG_LEVEL_ERROR, message, null);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR</code>.
+ *
+ * @param message to log
+ * @param t log this cause
+ * @see org.apache.commons.logging.Log#error(Object, Throwable)
+ */
+ public final void error(Object message, Throwable t) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) {
+ log(SimpleLog.LOG_LEVEL_ERROR, message, t);
+ }
+ }
+
+
+ /**
+ * Log a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>.
+ *
+ * @param message to log
+ * @see org.apache.commons.logging.Log#fatal(Object)
+ */
+ public final void fatal(Object message) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
+ log(SimpleLog.LOG_LEVEL_FATAL, message, null);
+ }
+ }
+
+
+ /**
+ * Logs a message with
+ * <code>org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL</code>.
+ *
+ * @param message to log
+ * @param t log this cause
+ * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+ */
+ public final void fatal(Object message, Throwable t) {
+
+ if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
+ log(SimpleLog.LOG_LEVEL_FATAL, message, t);
+ }
+ }
+
+
+ /**
+ * <p> Are debug messages currently enabled? </p>
+ *
+ * <p> This allows expensive operations such as <code>String</code>
+ * concatenation to be avoided when the message will be ignored by the
+ * logger. </p>
+ */
+ public final boolean isDebugEnabled() {
+
+ return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG);
+ }
+
+
+ /**
+ * <p> Are error messages currently enabled? </p>
+ *
+ * <p> This allows expensive operations such as <code>String</code>
+ * concatenation to be avoided when the message will be ignored by the
+ * logger. </p>
+ */
+ public final boolean isErrorEnabled() {
+
+ return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR);
+ }
+
+
+ /**
+ * <p> Are fatal messages currently enabled? </p>
+ *
+ * <p> This allows expensive operations such as <code>String</code>
+ * concatenation to be avoided when the message will be ignored by the
+ * logger. </p>
+ */
+ public final boolean isFatalEnabled() {
+
+ return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL);
+ }
+
+
+ /**
+ * <p> Are info messages currently enabled? </p>
+ *
+ * <p> This allows expensive operations such as <code>String</code>
+ * concatenation to be avoided when the message will be ignored by the
+ * logger. </p>
+ */
+ public final boolean isInfoEnabled() {
+
+ return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO);
+ }
+
+
+ /**
+ * <p> Are trace messages currently enabled? </p>
+ *
+ * <p> This allows expensive operations such as <code>String</code>
+ * concatenation to be avoided when the message will be ignored by the
+ * logger. </p>
+ */
+ public final boolean isTraceEnabled() {
+
+ return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE);
+ }
+
+
+ /**
+ * <p> Are warn messages currently enabled? </p>
+ *
+ * <p> This allows expensive operations such as <code>String</code>
+ * concatenation to be avoided when the message will be ignored by the
+ * logger. </p>
+ */
+ public final boolean isWarnEnabled() {
+
+ return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN);
+ }
+
+
+ /**
+ * Return the thread context class loader if available.
+ * Otherwise return null.
+ *
+ * The thread context class loader is available for JDK 1.2
+ * or later, if certain security conditions are met.
+ *
+ * @exception LogConfigurationException if a suitable class loader
+ * cannot be identified.
+ */
+ private static ClassLoader getContextClassLoader()
+ {
+ ClassLoader classLoader = null;
+
+ if (classLoader == null) {
+ try {
+ // Are we running on a JDK 1.2 or later system?
+ Method method = Thread.class.getMethod("getContextClassLoader",
+ (Class[]) null);
+
+ // Get the thread context class loader (if there is one)
+ try {
+ classLoader = (ClassLoader)method.invoke(Thread.currentThread(),
+ (Object[]) null);
+
+ } catch (IllegalAccessException e) {
+ ; // ignore
+ } catch (InvocationTargetException e) {
+ /**
+ * InvocationTargetException is thrown by 'invoke' when
+ * the method being invoked (getContextClassLoader) throws
+ * an exception.
+ *
+ * getContextClassLoader() throws SecurityException when
+ * the context class loader isn't an ancestor of the
+ * calling class's class loader, or if security
+ * permissions are restricted.
+ *
+ * In the first case (not related), we want to ignore and
+ * keep going. We cannot help but also ignore the second
+ * with the logic below, but other calls elsewhere (to
+ * obtain a class loader) will trigger this exception where
+ * we can make a distinction.
+ */
+ if (e.getTargetException() instanceof SecurityException) {
+ ; // ignore
+ } else {
+ // Capture 'e.getTargetException()' exception for details
+ // alternate: log 'e.getTargetException()', and pass back 'e'.
+ throw new LogConfigurationException
+ ("Unexpected InvocationTargetException", e.getTargetException());
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ // Assume we are running on JDK 1.1
+ ; // ignore
+ }
+ }
+
+ if (classLoader == null) {
+ classLoader = SimpleLog.class.getClassLoader();
+ }
+
+ // Return the selected class loader
+ return classLoader;
+ }
+
+ private static InputStream getResourceAsStream(final String name)
+ {
+ return (InputStream)AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ ClassLoader threadCL = getContextClassLoader();
+
+ if (threadCL != null) {
+ return threadCL.getResourceAsStream(name);
+ } else {
+ return ClassLoader.getSystemResourceAsStream(name);
+ }
+ }
+ });
+ }
+}
+
diff --git a/src/org/apache/commons/logging/impl/WeakHashtable.java b/src/org/apache/commons/logging/impl/WeakHashtable.java
new file mode 100644
index 0000000..e4749b6
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/WeakHashtable.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * 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.apache.commons.logging.impl;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.*;
+
+/**
+ * <p>Implementation of <code>Hashtable</code> that uses <code>WeakReference</code>'s
+ * to hold its keys thus allowing them to be reclaimed by the garbage collector.
+ * The associated values are retained using strong references.</p>
+ *
+ * <p>This class follows the symantics of <code>Hashtable</code> as closely as
+ * possible. It therefore does not accept null values or keys.</p>
+ *
+ * <p><strong>Note:</strong>
+ * This is <em>not</em> intended to be a general purpose hash table replacement.
+ * This implementation is also tuned towards a particular purpose: for use as a replacement
+ * for <code>Hashtable</code> in <code>LogFactory</code>. This application requires
+ * good liveliness for <code>get</code> and <code>put</code>. Various tradeoffs
+ * have been made with this in mind.
+ * </p>
+ * <p>
+ * <strong>Usage:</strong> typical use case is as a drop-in replacement
+ * for the <code>Hashtable</code> used in <code>LogFactory</code> for J2EE enviroments
+ * running 1.3+ JVMs. Use of this class <i>in most cases</i> (see below) will
+ * allow classloaders to be collected by the garbage collector without the need
+ * to call {@link org.apache.commons.logging.LogFactory#release(ClassLoader) LogFactory.release(ClassLoader)}.
+ * </p>
+ *
+ * <p><code>org.apache.commons.logging.LogFactory</code> checks whether this class
+ * can be supported by the current JVM, and if so then uses it to store
+ * references to the <code>LogFactory</code> implementationd it loads
+ * (rather than using a standard Hashtable instance).
+ * Having this class used instead of <code>Hashtable</code> solves
+ * certain issues related to dynamic reloading of applications in J2EE-style
+ * environments. However this class requires java 1.3 or later (due to its use
+ * of <code>java.lang.ref.WeakReference</code> and associates).
+ * And by the way, this extends <code>Hashtable</code> rather than <code>HashMap</code>
+ * for backwards compatibility reasons. See the documentation
+ * for method <code>LogFactory.createFactoryStore</code> for more details.</p>
+ *
+ * <p>The reason all this is necessary is due to a issue which
+ * arises during hot deploy in a J2EE-like containers.
+ * Each component running in the container owns one or more classloaders; when
+ * the component loads a LogFactory instance via the component classloader
+ * a reference to it gets stored in the static LogFactory.factories member,
+ * keyed by the component's classloader so different components don't
+ * stomp on each other. When the component is later unloaded, the container
+ * sets the component's classloader to null with the intent that all the
+ * component's classes get garbage-collected. However there's still a
+ * reference to the component's classloader from a key in the "global"
+ * <code>LogFactory</code>'s factories member! If <code>LogFactory.release()</code>
+ * is called whenever component is unloaded, the classloaders will be correctly
+ * garbage collected; this <i>should</i> be done by any container that
+ * bundles commons-logging by default. However, holding the classloader
+ * references weakly ensures that the classloader will be garbage collected
+ * without the container performing this step. </p>
+ *
+ * <p>
+ * <strong>Limitations:</strong>
+ * There is still one (unusual) scenario in which a component will not
+ * be correctly unloaded without an explicit release. Though weak references
+ * are used for its keys, it is necessary to use strong references for its values.
+ * </p>
+ *
+ * <p> If the abstract class <code>LogFactory</code> is
+ * loaded by the container classloader but a subclass of
+ * <code>LogFactory</code> [LogFactory1] is loaded by the component's
+ * classloader and an instance stored in the static map associated with the
+ * base LogFactory class, then there is a strong reference from the LogFactory
+ * class to the LogFactory1 instance (as normal) and a strong reference from
+ * the LogFactory1 instance to the component classloader via
+ * <code>getClass().getClassLoader()</code>. This chain of references will prevent
+ * collection of the child classloader.</p>
+ *
+ * <p>
+ * Such a situation occurs when the commons-logging.jar is
+ * loaded by a parent classloader (e.g. a server level classloader in a
+ * servlet container) and a custom <code>LogFactory</code> implementation is
+ * loaded by a child classloader (e.g. a web app classloader).</p>
+ *
+ * <p>To avoid this scenario, ensure
+ * that any custom LogFactory subclass is loaded by the same classloader as
+ * the base <code>LogFactory</code>. Creating custom LogFactory subclasses is,
+ * however, rare. The standard LogFactoryImpl class should be sufficient
+ * for most or all users.</p>
+ *
+ *
+ * @author Brian Stansberry
+ *
+ * @since 1.1
+ */
+public final class WeakHashtable extends Hashtable {
+
+ /**
+ * The maximum number of times put() or remove() can be called before
+ * the map will be purged of all cleared entries.
+ */
+ private static final int MAX_CHANGES_BEFORE_PURGE = 100;
+
+ /**
+ * The maximum number of times put() or remove() can be called before
+ * the map will be purged of one cleared entry.
+ */
+ private static final int PARTIAL_PURGE_COUNT = 10;
+
+ /* ReferenceQueue we check for gc'd keys */
+ private ReferenceQueue queue = new ReferenceQueue();
+ /* Counter used to control how often we purge gc'd entries */
+ private int changeCount = 0;
+
+ /**
+ * Constructs a WeakHashtable with the Hashtable default
+ * capacity and load factor.
+ */
+ public WeakHashtable() {}
+
+
+ /**
+ *@see Hashtable
+ */
+ public boolean containsKey(Object key) {
+ // purge should not be required
+ Referenced referenced = new Referenced(key);
+ return super.containsKey(referenced);
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public Enumeration elements() {
+ purge();
+ return super.elements();
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public Set entrySet() {
+ purge();
+ Set referencedEntries = super.entrySet();
+ Set unreferencedEntries = new HashSet();
+ for (Iterator it=referencedEntries.iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ Referenced referencedKey = (Referenced) entry.getKey();
+ Object key = referencedKey.getValue();
+ Object value = entry.getValue();
+ if (key != null) {
+ Entry dereferencedEntry = new Entry(key, value);
+ unreferencedEntries.add(dereferencedEntry);
+ }
+ }
+ return unreferencedEntries;
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public Object get(Object key) {
+ // for performance reasons, no purge
+ Referenced referenceKey = new Referenced(key);
+ return super.get(referenceKey);
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public Enumeration keys() {
+ purge();
+ final Enumeration enumer = super.keys();
+ return new Enumeration() {
+ public boolean hasMoreElements() {
+ return enumer.hasMoreElements();
+ }
+ public Object nextElement() {
+ Referenced nextReference = (Referenced) enumer.nextElement();
+ return nextReference.getValue();
+ }
+ };
+ }
+
+
+ /**
+ *@see Hashtable
+ */
+ public Set keySet() {
+ purge();
+ Set referencedKeys = super.keySet();
+ Set unreferencedKeys = new HashSet();
+ for (Iterator it=referencedKeys.iterator(); it.hasNext();) {
+ Referenced referenceKey = (Referenced) it.next();
+ Object keyValue = referenceKey.getValue();
+ if (keyValue != null) {
+ unreferencedKeys.add(keyValue);
+ }
+ }
+ return unreferencedKeys;
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public Object put(Object key, Object value) {
+ // check for nulls, ensuring symantics match superclass
+ if (key == null) {
+ throw new NullPointerException("Null keys are not allowed");
+ }
+ if (value == null) {
+ throw new NullPointerException("Null values are not allowed");
+ }
+
+ // for performance reasons, only purge every
+ // MAX_CHANGES_BEFORE_PURGE times
+ if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
+ purge();
+ changeCount = 0;
+ }
+ // do a partial purge more often
+ else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) {
+ purgeOne();
+ }
+
+ Object result = null;
+ Referenced keyRef = new Referenced(key, queue);
+ return super.put(keyRef, value);
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public void putAll(Map t) {
+ if (t != null) {
+ Set entrySet = t.entrySet();
+ for (Iterator it=entrySet.iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public Collection values() {
+ purge();
+ return super.values();
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public Object remove(Object key) {
+ // for performance reasons, only purge every
+ // MAX_CHANGES_BEFORE_PURGE times
+ if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
+ purge();
+ changeCount = 0;
+ }
+ // do a partial purge more often
+ else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) {
+ purgeOne();
+ }
+ return super.remove(new Referenced(key));
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public boolean isEmpty() {
+ purge();
+ return super.isEmpty();
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public int size() {
+ purge();
+ return super.size();
+ }
+
+ /**
+ *@see Hashtable
+ */
+ public String toString() {
+ purge();
+ return super.toString();
+ }
+
+ /**
+ * @see Hashtable
+ */
+ protected void rehash() {
+ // purge here to save the effort of rehashing dead entries
+ purge();
+ super.rehash();
+ }
+
+ /**
+ * Purges all entries whose wrapped keys
+ * have been garbage collected.
+ */
+ private void purge() {
+ synchronized (queue) {
+ WeakKey key;
+ while ((key = (WeakKey) queue.poll()) != null) {
+ super.remove(key.getReferenced());
+ }
+ }
+ }
+
+ /**
+ * Purges one entry whose wrapped key
+ * has been garbage collected.
+ */
+ private void purgeOne() {
+
+ synchronized (queue) {
+ WeakKey key = (WeakKey) queue.poll();
+ if (key != null) {
+ super.remove(key.getReferenced());
+ }
+ }
+ }
+
+ /** Entry implementation */
+ private final static class Entry implements Map.Entry {
+
+ private final Object key;
+ private final Object value;
+
+ private Entry(Object key, Object value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public boolean equals(Object o) {
+ boolean result = false;
+ if (o != null && o instanceof Map.Entry) {
+ Map.Entry entry = (Map.Entry) o;
+ result = (getKey()==null ?
+ entry.getKey() == null :
+ getKey().equals(entry.getKey()))
+ &&
+ (getValue()==null ?
+ entry.getValue() == null :
+ getValue().equals(entry.getValue()));
+ }
+ return result;
+ }
+
+ public int hashCode() {
+
+ return (getKey()==null ? 0 : getKey().hashCode()) ^
+ (getValue()==null ? 0 : getValue().hashCode());
+ }
+
+ public Object setValue(Object value) {
+ throw new UnsupportedOperationException("Entry.setValue is not supported.");
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+ }
+
+
+ /** Wrapper giving correct symantics for equals and hashcode */
+ private final static class Referenced {
+
+ private final WeakReference reference;
+ private final int hashCode;
+
+ /**
+ *
+ * @throws NullPointerException if referant is <code>null</code>
+ */
+ private Referenced(Object referant) {
+ reference = new WeakReference(referant);
+ // Calc a permanent hashCode so calls to Hashtable.remove()
+ // work if the WeakReference has been cleared
+ hashCode = referant.hashCode();
+ }
+
+ /**
+ *
+ * @throws NullPointerException if key is <code>null</code>
+ */
+ private Referenced(Object key, ReferenceQueue queue) {
+ reference = new WeakKey(key, queue, this);
+ // Calc a permanent hashCode so calls to Hashtable.remove()
+ // work if the WeakReference has been cleared
+ hashCode = key.hashCode();
+
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ private Object getValue() {
+ return reference.get();
+ }
+
+ public boolean equals(Object o) {
+ boolean result = false;
+ if (o instanceof Referenced) {
+ Referenced otherKey = (Referenced) o;
+ Object thisKeyValue = getValue();
+ Object otherKeyValue = otherKey.getValue();
+ if (thisKeyValue == null) {
+ result = (otherKeyValue == null);
+
+ // Since our hashcode was calculated from the original
+ // non-null referant, the above check breaks the
+ // hashcode/equals contract, as two cleared Referenced
+ // objects could test equal but have different hashcodes.
+ // We can reduce (not eliminate) the chance of this
+ // happening by comparing hashcodes.
+ if (result == true) {
+ result = (this.hashCode() == otherKey.hashCode());
+ }
+ // In any case, as our c'tor does not allow null referants
+ // and Hashtable does not do equality checks between
+ // existing keys, normal hashtable operations should never
+ // result in an equals comparison between null referants
+ }
+ else
+ {
+ result = thisKeyValue.equals(otherKeyValue);
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * WeakReference subclass that holds a hard reference to an
+ * associated <code>value</code> and also makes accessible
+ * the Referenced object holding it.
+ */
+ private final static class WeakKey extends WeakReference {
+
+ private final Referenced referenced;
+
+ private WeakKey(Object key,
+ ReferenceQueue queue,
+ Referenced referenced) {
+ super(key, queue);
+ this.referenced = referenced;
+ }
+
+ private Referenced getReferenced() {
+ return referenced;
+ }
+ }
+}
diff --git a/src/org/apache/commons/logging/impl/package.html b/src/org/apache/commons/logging/impl/package.html
new file mode 100644
index 0000000..eb26b76
--- /dev/null
+++ b/src/org/apache/commons/logging/impl/package.html
@@ -0,0 +1,21 @@
+<!--
+
+ Copyright 2001-2004 The Apache Software Foundation.
+
+ 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.
+
+-->
+
+<body>
+<p>Concrete implementations of commons-logging wrapper APIs.</p>
+</body>
diff --git a/src/org/apache/commons/logging/package.html b/src/org/apache/commons/logging/package.html
new file mode 100644
index 0000000..cfde4f0
--- /dev/null
+++ b/src/org/apache/commons/logging/package.html
@@ -0,0 +1,254 @@
+<!--
+
+ Copyright 2001-2004 The Apache Software Foundation.
+
+ 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.
+
+-->
+
+<body>
+<p>Simple wrapper API around multiple logging APIs.</p>
+
+
+<h3>Overview</h3>
+
+<p>This package provides an API for logging in server-based applications that
+can be used around a variety of different logging implementations, including
+prebuilt support for the following:</p>
+<ul>
+<li><a href="http://logging.apache.org/log4j/">Log4J</a> (version 1.2 or later)
+ from Apache's Jakarta project. Each named <a href="Log.html">Log</a>
+ instance is connected to a corresponding Log4J Logger.</li>
+<li><a href="http://java.sun.com/j2se/1.4/docs/guide/util/logging/index.html">
+ JDK Logging API</a>, included in JDK 1.4 or later systems. Each named
+ <a href="Log.html">Log</a> instance is connected to a corresponding
+ <code>java.util.logging.Logger</code> instance.</li>
+<li><a href="http://avalon.apache.org/logkit/">LogKit</a> from Apache's
+ Avalon project. Each named <a href="Log.html">Log</a> instance is
+ connected to a corresponding LogKit <code>Logger</code>.</li>
+<li><a href="impl/NoOpLog.html">NoOpLog</a> implementation that simply swallows
+ all log output, for all named <a href="Log.html">Log</a> instances.</li>
+<li><a href="impl/SimpleLog.html">SimpleLog</a> implementation that writes all
+ log output, for all named <a href="Log.html">Log</a> instances, to
+ System.err.</li>
+</ul>
+
+
+<h3>Quick Start Guide</h3>
+
+<p>For those impatient to just get on with it, the following example
+illustrates the typical declaration and use of a logger that is named (by
+convention) after the calling class:
+
+<pre>
+ import org.apache.commons.logging.Log;
+ import org.apache.commons.logging.LogFactory;
+
+ public class Foo {
+
+ private Log log = LogFactory.getLog(Foo.class);
+
+ public void foo() {
+ ...
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("About to do something to object " + name);
+ }
+ name.bar();
+ } catch (IllegalStateException e) {
+ log.error("Something bad happened to " + name, e);
+ }
+ ...
+ }
+</pre>
+
+<p>Unless you configure things differently, all log output will be written
+to System.err. Therefore, you really will want to review the remainder of
+this page in order to understand how to configure logging for your
+application.</p>
+
+
+<h3>Configuring the Commons Logging Package</h3>
+
+
+<h4>Choosing a <code>LogFactory</code> Implementation</h4>
+
+<p>From an application perspective, the first requirement is to retrieve an
+object reference to the <code>LogFactory</code> instance that will be used
+to create <code><a href="Log.html">Log</a></code> instances for this
+application. This is normally accomplished by calling the static
+<code>getFactory()</code> method. This method implements the following
+discovery algorithm to select the name of the <code>LogFactory</code>
+implementation class this application wants to use:</p>
+<ul>
+<li>Check for a system property named
+ <code>org.apache.commons.logging.LogFactory</code>.</li>
+<li>Use the JDK 1.3 JAR Services Discovery mechanism (see
+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html">
+ http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html</a> for
+ more information) to look for a resource named
+ <code>META-INF/services/org.apache.commons.logging.LogFactory</code>
+ whose first line is assumed to contain the desired class name.</li>
+<li>Look for a properties file named <code>commons-logging.properties</code>
+ visible in the application class path, with a property named
+ <code>org.apache.commons.logging.LogFactory</code> defining the
+ desired implementation class name.</li>
+<li>Fall back to a default implementation, which is described
+ further below.</li>
+</ul>
+
+<p>If a <code>commons-logging.properties</code> file is found, all of the
+properties defined there are also used to set configuration attributes on
+the instantiated <code>LogFactory</code> instance.</p>
+
+<p>Once an implementation class name is selected, the corresponding class is
+loaded from the current Thread context class loader (if there is one), or
+from the class loader that loaded the <code>LogFactory</code> class itself
+otherwise. This allows a copy of <code>commons-logging.jar</code> to be
+shared in a multiple class loader environment (such as a servlet container),
+but still allow each web application to provide its own <code>LogFactory</code>
+implementation, if it so desires. An instance of this class will then be
+created, and cached per class loader.
+
+
+<h4>The Default <code>LogFactory</code> Implementation</h4>
+
+<p>The Logging Package APIs include a default <code>LogFactory</code>
+implementation class (<a href="impl/LogFactoryImpl.html">
+org.apache.commons.logging.impl.LogFactoryImpl</a>) that is selected if no
+other implementation class name can be discovered. Its primary purpose is
+to create (as necessary) and return <a href="Log.html">Log</a> instances
+in response to calls to the <code>getInstance()</code> method. The default
+implementation uses the following rules:</p>
+<ul>
+<li>At most one <code>Log</code> instance of the same name will be created.
+ Subsequent <code>getInstance()</code> calls to the same
+ <code>LogFactory</code> instance, with the same name or <code>Class</code>
+ parameter, will return the same <code>Log</code> instance.</li>
+<li>When a new <code>Log</code> instance must be created, the default
+ <code>LogFactory</code> implementation uses the following discovery
+ process:
+ <ul>
+ <li>Look for a configuration attribute of this factory named
+ <code>org.apache.commons.logging.Log</code> (for backwards
+ compatibility to pre-1.0 versions of this API, an attribute
+ <code>org.apache.commons.logging.log</code> is also consulted).</li>
+ <li>Look for a system property named
+ <code>org.apache.commons.logging.Log</code> (for backwards
+ compatibility to pre-1.0 versions of this API, a system property
+ <code>org.apache.commons.logging.log</code> is also consulted).</li>
+ <li>If the Log4J logging system is available in the application
+ class path, use the corresponding wrapper class
+ (<a href="impl/Log4JLogger.html">Log4JLogger</a>).</li>
+ <li>If the application is executing on a JDK 1.4 system, use
+ the corresponding wrapper class
+ (<a href="impl/Jdk14Logger.html">Jdk14Logger</a>).</li>
+ <li>Fall back to the default simple logging implementation
+ (<a href="impl/SimpleLog.html">SimpleLog</a>).</li>
+ </ul></li>
+<li>Load the class of the specified name from the thread context class
+ loader (if any), or from the class loader that loaded the
+ <code>LogFactory</code> class otherwise.</li>
+<li>Instantiate an instance of the selected <code>Log</code>
+ implementation class, passing the specified name as the single
+ argument to its constructor.</li>
+</ul>
+
+<p>See the <a href="impl/SimpleLog.html">SimpleLog</a> JavaDocs for detailed
+configuration information for this default implementation.</p>
+
+
+<h4>Configuring the Underlying Logging System</h4>
+
+<p>The basic principle is that the user is totally responsible for the
+configuration of the underlying logging system.
+Commons-logging should not change the existing configuration.</p>
+
+<p>Each individual <a href="Log.html">Log</a> implementation may
+support its own configuration properties. These will be documented in the
+class descriptions for the corresponding implementation class.</p>
+
+<p>Finally, some <code>Log</code> implementations (such as the one for Log4J)
+require an external configuration file for the entire logging environment.
+This file should be prepared in a manner that is specific to the actual logging
+technology being used.</p>
+
+
+<h3>Using the Logging Package APIs</h3>
+
+<p>Use of the Logging Package APIs, from the perspective of an application
+component, consists of the following steps:</p>
+<ol>
+<li>Acquire a reference to an instance of
+ <a href="Log.html">org.apache.commons.logging.Log</a>, by calling the
+ factory method
+ <a href="LogFactory.html#getInstance(java.lang.String)">
+ LogFactory.getInstance(String name)</a>. Your application can contain
+ references to multiple loggers that are used for different
+ purposes. A typical scenario for a server application is to have each
+ major component of the server use its own Log instance.</li>
+<li>Cause messages to be logged (if the corresponding detail level is enabled)
+ by calling appropriate methods (<code>trace()</code>, <code>debug()</code>,
+ <code>info()</code>, <code>warn()</code>, <code>error</code>, and
+ <code>fatal()</code>).</li>
+</ol>
+
+<p>For convenience, <code>LogFactory</code> also offers a static method
+<code>getLog()</code> that combines the typical two-step pattern:</p>
+<pre>
+ Log log = LogFactory.getFactory().getInstance(Foo.class);
+</pre>
+<p>into a single method call:</p>
+<pre>
+ Log log = LogFactory.getLog(Foo.class);
+</pre>
+
+<p>For example, you might use the following technique to initialize and
+use a <a href="Log.html">Log</a> instance in an application component:</p>
+<pre>
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class MyComponent {
+
+ protected Log log =
+ LogFactory.getLog(MyComponent.class);
+
+ // Called once at startup time
+ public void start() {
+ ...
+ log.info("MyComponent started");
+ ...
+ }
+
+ // Called once at shutdown time
+ public void stop() {
+ ...
+ log.info("MyComponent stopped");
+ ...
+ }
+
+ // Called repeatedly to process a particular argument value
+ // which you want logged if debugging is enabled
+ public void process(String value) {
+ ...
+ // Do the string concatenation only if logging is enabled
+ if (log.isDebugEnabled())
+ log.debug("MyComponent processing " + value);
+ ...
+ }
+
+}
+</pre>
+
+</body>
diff --git a/src/org/apache/http/ConnectionClosedException.java b/src/org/apache/http/ConnectionClosedException.java
new file mode 100644
index 0000000..fa0e2db
--- /dev/null
+++ b/src/org/apache/http/ConnectionClosedException.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ConnectionClosedException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * Indicates that a connection has been closed.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ *
+ * @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/src/org/apache/http/ConnectionReuseStrategy.java b/src/org/apache/http/ConnectionReuseStrategy.java
new file mode 100644
index 0000000..635cc5c
--- /dev/null
+++ b/src/org/apache/http/ConnectionReuseStrategy.java
@@ -0,0 +1,75 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ConnectionReuseStrategy.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Interface for deciding whether a connection should be kept alive.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public interface ConnectionReuseStrategy {
+
+ /**
+ * Decides whether a connection can be kept open after a request.
+ * If this method returns <code>false</code>, the caller MUST
+ * close the connection to correctly implement the HTTP protocol.
+ * If it returns <code>true</code>, the caller SHOULD attempt to
+ * keep the connection open for reuse with another request.
+ * <br/>
+ * 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.
+ * <br/>
+ * If the connection is already closed, <code>false</code> 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 <code>true</code> if the connection is allowed to be reused, or
+ * <code>false</code> if it MUST NOT be reused
+ */
+ boolean keepAlive(HttpResponse response, HttpContext context);
+
+}
diff --git a/src/org/apache/http/FormattedHeader.java b/src/org/apache/http/FormattedHeader.java
new file mode 100644
index 0000000..04ea279
--- /dev/null
+++ b/src/org/apache/http/FormattedHeader.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/FormattedHeader.java $
+ * $Revision: 569781 $
+ * $Date: 2007-08-26 02:05:06 -0700 (Sun, 26 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.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.
+ *
+ *
+ * @version $Revision: 569781 $
+ */
+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/src/org/apache/http/Header.java b/src/org/apache/http/Header.java
new file mode 100644
index 0000000..4e04bec
--- /dev/null
+++ b/src/org/apache/http/Header.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/Header.java $
+ * $Revision: 569636 $
+ * $Date: 2007-08-25 00:34:47 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Represents an HTTP header field.
+ *
+ * <p>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.
+ *
+ *<pre>
+ * message-header = field-name ":" [ field-value ]
+ * field-name = token
+ * field-value = *( field-content | LWS )
+ * field-content = &lt;the OCTETs making up the field-value
+ * and consisting of either *TEXT or combinations
+ * of token, separators, and quoted-string&gt;
+ *</pre>
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @version $Revision: 569636 $
+ */
+public interface Header {
+
+ String getName();
+
+ String getValue();
+
+ HeaderElement[] getElements() throws ParseException;
+
+}
diff --git a/src/org/apache/http/HeaderElement.java b/src/org/apache/http/HeaderElement.java
new file mode 100644
index 0000000..ddc4a9e
--- /dev/null
+++ b/src/org/apache/http/HeaderElement.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HeaderElement.java $
+ * $Revision: 569828 $
+ * $Date: 2007-08-26 08:49:38 -0700 (Sun, 26 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * One element of an HTTP {@link Header header} value.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 569828 $ $Date: 2007-08-26 08:49:38 -0700 (Sun, 26 Aug 2007) $
+ *
+ * @since 4.0
+ */
+public interface HeaderElement {
+
+ String getName();
+
+ String getValue();
+
+ NameValuePair[] getParameters();
+
+ NameValuePair getParameterByName(String name);
+
+ int getParameterCount();
+
+ NameValuePair getParameter(int index);
+}
+
diff --git a/src/org/apache/http/HeaderElementIterator.java b/src/org/apache/http/HeaderElementIterator.java
new file mode 100644
index 0000000..14137f0
--- /dev/null
+++ b/src/org/apache/http/HeaderElementIterator.java
@@ -0,0 +1,61 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HeaderElementIterator.java $
+ * $Revision: 584542 $
+ * $Date: 2007-10-14 06:29:34 -0700 (Sun, 14 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.util.Iterator;
+
+/**
+ * A type-safe iterator for {@link HeaderElement HeaderElement} objects.
+ *
+ * @version $Revision: 584542 $
+ */
+public interface HeaderElementIterator extends Iterator {
+
+ /**
+ * Indicates whether there is another header element in this
+ * iteration.
+ *
+ * @return <code>true</code> if there is another header element,
+ * <code>false</code> 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/src/org/apache/http/HeaderIterator.java b/src/org/apache/http/HeaderIterator.java
new file mode 100644
index 0000000..688b611
--- /dev/null
+++ b/src/org/apache/http/HeaderIterator.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HeaderIterator.java $
+ * $Revision: 581981 $
+ * $Date: 2007-10-04 11:26:26 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+import java.util.Iterator;
+
+
+/**
+ * A type-safe iterator for {@link Header Header} objects.
+ *
+ * @version $Revision: 581981 $
+ */
+public interface HeaderIterator extends Iterator {
+
+ /**
+ * Indicates whether there is another header in this iteration.
+ *
+ * @return <code>true</code> if there is another header,
+ * <code>false</code> 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/src/org/apache/http/HttpClientConnection.java b/src/org/apache/http/HttpClientConnection.java
new file mode 100644
index 0000000..a38c8f3
--- /dev/null
+++ b/src/org/apache/http/HttpClientConnection.java
@@ -0,0 +1,112 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpClientConnection.java $
+ * $Revision: 542199 $
+ * $Date: 2007-05-28 04:23:46 -0700 (Mon, 28 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * An HTTP connection for use on the client side.
+ * It is used for sending requests and receiving responses.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 542199 $
+ *
+ * @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 <code>timeout</code> 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
+ * @throws IOException
+ */
+ void sendRequestHeader(HttpRequest request)
+ throws HttpException, IOException;
+
+ /**
+ * Sends the request entity over the connection.
+ * @param request the request whose entity to send.
+ * @throws HttpException
+ * @throws IOException
+ */
+ 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
+ * @throws IOException
+ */
+ 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
+ * @throws IOException
+ */
+ void receiveResponseEntity(HttpResponse response)
+ throws HttpException, IOException;
+
+ /**
+ * Writes out all pending buffered data over the open connection.
+ *
+ * @throws IOException
+ */
+ void flush() throws IOException;
+
+}
diff --git a/src/org/apache/http/HttpConnection.java b/src/org/apache/http/HttpConnection.java
new file mode 100644
index 0000000..a3311f8
--- /dev/null
+++ b/src/org/apache/http/HttpConnection.java
@@ -0,0 +1,110 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpConnection.java $
+ * $Revision: 548031 $
+ * $Date: 2007-06-17 04:28:38 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * A generic HTTP connection, useful on client and server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 548031 $
+ *
+ * @since 4.0
+ */
+public interface HttpConnection {
+
+ /**
+ * Closes this connection gracefully.
+ * This method will attempt to flush the transmitter's
+ * internal 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.
+ */
+ public void close() throws IOException;
+
+ /**
+ * Checks if this connection is open.
+ * @return true if it is open, false if it is closed.
+ */
+ public 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 <i>expensive</i> operation.
+ *
+ * @return <code>true</code> if attempts to use this connection are
+ * likely to succeed, or <code>false</code> if they are likely
+ * to fail and this connection should be closed
+ */
+ public 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,
+ * <code>0</code> if timeout is disabled or <code>-1</code> 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.
+ */
+ public void shutdown() throws IOException;
+
+ /**
+ * Returns a collection of connection metrcis
+ * @return HttpConnectionMetrics
+ */
+ HttpConnectionMetrics getMetrics();
+
+}
diff --git a/src/org/apache/http/HttpConnectionMetrics.java b/src/org/apache/http/HttpConnectionMetrics.java
new file mode 100644
index 0000000..289dd46
--- /dev/null
+++ b/src/org/apache/http/HttpConnectionMetrics.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpConnectionMetrics.java $
+ * $Revision: 548035 $
+ * $Date: 2007-06-17 05:17:03 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * The point of access to the statistics of an {@link HttpConnection}.
+ */
+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,
+ * <code>null</code> if the metric cannot not found.
+ */
+ Object getMetric(String metricName);
+
+ /**
+ * Resets the counts
+ *
+ */
+ void reset();
+
+}
diff --git a/src/org/apache/http/HttpEntity.java b/src/org/apache/http/HttpEntity.java
new file mode 100644
index 0000000..51ddafc
--- /dev/null
+++ b/src/org/apache/http/HttpEntity.java
@@ -0,0 +1,196 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpEntity.java $
+ * $Revision: 645824 $
+ * $Date: 2008-04-08 03:12:41 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+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.
+ * <p>
+ * In some places, the JavaDoc distinguishes three kinds of entities,
+ * depending on where their {@link #getContent content} originates:
+ * <ul>
+ * <li><b>streamed</b>: 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}.
+ * </li>
+ * <li><b>self-contained</b>: 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}.
+ * </li>
+ * <li><b>wrapping</b>: The content is obtained from another entity.
+ * </li>
+ * </ul>
+ * 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.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 645824 $
+ *
+ * @since 4.0
+ */
+public interface HttpEntity {
+
+ /**
+ * Tells if the entity is capable to produce 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.
+ * <br/>
+ * The behavior of wrapping entities is implementation dependent,
+ * but should respect the primary purpose.
+ *
+ * @return <code>true</code> if chunked encoding is preferred for this
+ * entity, or <code>false</code> 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
+ * <code>null</code> 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
+ * <code>null</code> if the content encoding is unknown
+ */
+ Header getContentEncoding();
+
+ /**
+ * Creates a new InputStream object of the entity.
+ * It is a programming error
+ * to return the same InputStream object more than once.
+ * Entities that are not {@link #isRepeatable repeatable}
+ * will throw an exception if this method is called multiple times.
+ *
+ * @return a new input stream that returns the entity data.
+ *
+ * @throws IOException if the stream could not be created
+ * @throws IllegalStateException
+ * if this entity is not repeatable and the stream
+ * has already been obtained previously
+ */
+ InputStream getContent() throws IOException, IllegalStateException;
+
+ /**
+ * Writes the entity content to the output stream.
+ *
+ * @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 should return <code>true</code> until the
+ * content has been consumed, <code>false</code> afterwards.
+ * Self-contained entities should return <code>false</code>.
+ * Wrapping entities should delegate this call to the wrapped entity.
+ * <br/>
+ * The content of a streamed entity is consumed when the stream
+ * returned by {@link #getContent getContent} has been read to EOF,
+ * or after {@link #consumeContent consumeContent} has been called.
+ * If a streamed entity can not detect whether the stream has been
+ * read to EOF, it should return <code>true</code> until
+ * {@link #consumeContent consumeContent} is called.
+ *
+ * @return <code>true</code> if the entity content is streamed and
+ * not yet consumed, <code>false</code> otherwise
+ */
+ boolean isStreaming(); // don't expect an exception here
+
+ /**
+ * TODO: The name of this method is misnomer. It will be renamed to
+ * #finish() in the next major release.
+ * <br/>
+ * 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.
+ * <br/>
+ * 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.
+ * This indicates that connection keep-alive is not possible.
+ */
+ void consumeContent() throws IOException;
+
+} // interface HttpEntity
diff --git a/src/org/apache/http/HttpEntityEnclosingRequest.java b/src/org/apache/http/HttpEntityEnclosingRequest.java
new file mode 100644
index 0000000..c47c32b
--- /dev/null
+++ b/src/org/apache/http/HttpEntityEnclosingRequest.java
@@ -0,0 +1,63 @@
+/*
+ * $Header: $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * A request with an entity.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ *
+ * @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();
+
+ /**
+ * Hands the entity to the request.
+ * @param entity the entity to send.
+ */
+ void setEntity(HttpEntity entity);
+
+ HttpEntity getEntity();
+
+}
diff --git a/src/org/apache/http/HttpException.java b/src/org/apache/http/HttpException.java
new file mode 100644
index 0000000..77aacb1
--- /dev/null
+++ b/src/org/apache/http/HttpException.java
@@ -0,0 +1,75 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.util.ExceptionUtils;
+
+/**
+ * Signals that an HTTP exception has occurred.
+ *
+ * @author Laura Werner
+ *
+ * @version $Revision: 618017 $ $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ */
+public class HttpException extends Exception {
+
+ private static final long serialVersionUID = -5437299376222011036L;
+
+ /**
+ * Creates a new HttpException with a <tt>null</tt> 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 <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public HttpException(final String message, final Throwable cause) {
+ super(message);
+ ExceptionUtils.initCause(this, cause);
+ }
+
+}
diff --git a/src/org/apache/http/HttpHost.java b/src/org/apache/http/HttpHost.java
new file mode 100644
index 0000000..869f5af
--- /dev/null
+++ b/src/org/apache/http/HttpHost.java
@@ -0,0 +1,218 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpHost.java $
+ * $Revision: 653058 $
+ * $Date: 2008-05-03 05:01:10 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.util.Locale;
+
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.LangUtils;
+
+/**
+ * Holds all of the variables needed to describe an HTTP connection to a host.
+ * This includes remote host name, port and scheme.
+ *
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Laura Werner
+ *
+ * @since 4.0
+ */
+public final class HttpHost implements Cloneable {
+
+ /** 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. */
+ protected final int port;
+
+ /** The scheme */
+ protected final String schemeName;
+
+
+ /**
+ * 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.
+ * <code>-1</code> indicates the scheme default port.
+ * @param scheme the name of the scheme.
+ * <code>null</code> indicates the
+ * {@link #DEFAULT_SCHEME_NAME default scheme}
+ */
+ public HttpHost(final String hostname, int port, final String scheme) {
+ super();
+ if (hostname == null) {
+ throw new IllegalArgumentException("Host name may not be null");
+ }
+ this.hostname = hostname;
+ this.lcHostname = 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 hostname the hostname (IP or DNS name)
+ * @param port the port number.
+ * <code>-1</code> indicates the scheme default port.
+ */
+ public HttpHost(final String hostname, 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);
+ }
+
+ /**
+ * Copy constructor for {@link HttpHost HttpHost}.
+ *
+ * @param httphost the HTTP host to copy details from
+ */
+ public HttpHost (final HttpHost httphost) {
+ this(httphost.hostname, httphost.port, httphost.schemeName);
+ }
+
+ /**
+ * 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 <code>-1</code> if not set
+ */
+ public int getPort() {
+ return this.port;
+ }
+
+ /**
+ * Returns the scheme name.
+ *
+ * @return the scheme name
+ */
+ public String getSchemeName() {
+ return this.schemeName;
+ }
+
+ /**
+ * Return the host URI, as a string.
+ *
+ * @return the host URI
+ */
+ public String toURI() {
+ CharArrayBuffer buffer = new CharArrayBuffer(32);
+ 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 <code>localhost:8080</code>
+ */
+ public String toHostString() {
+ CharArrayBuffer buffer = new CharArrayBuffer(32);
+ buffer.append(this.hostname);
+ if (this.port != -1) {
+ buffer.append(':');
+ buffer.append(Integer.toString(this.port));
+ }
+ return buffer.toString();
+ }
+
+
+ public String toString() {
+ return toURI();
+ }
+
+
+ public boolean equals(final Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (obj instanceof HttpHost) {
+ 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()
+ */
+ 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;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+}
diff --git a/src/org/apache/http/HttpInetConnection.java b/src/org/apache/http/HttpInetConnection.java
new file mode 100644
index 0000000..32ac04a
--- /dev/null
+++ b/src/org/apache/http/HttpInetConnection.java
@@ -0,0 +1,55 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpInetConnection.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.net.InetAddress;
+
+/**
+ * An HTTP connection over the Internet Protocol (IP).
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ *
+ * @since 4.0
+ */
+public interface HttpInetConnection extends HttpConnection {
+
+ InetAddress getLocalAddress();
+
+ int getLocalPort();
+
+ InetAddress getRemoteAddress();
+
+ int getRemotePort();
+
+}
diff --git a/src/org/apache/http/HttpMessage.java b/src/org/apache/http/HttpMessage.java
new file mode 100644
index 0000000..d24f0b4
--- /dev/null
+++ b/src/org/apache/http/HttpMessage.java
@@ -0,0 +1,191 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpMessage.java $
+ * $Revision: 610823 $
+ * $Date: 2008-01-10 07:53:53 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * A generic HTTP message.
+ * Holds what is common between requests and responses.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 610823 $
+ *
+ * @since 4.0
+ */
+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 <code>name</code>.
+ */
+ 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 <code>null</code> is
+ * returned.
+ *
+ * @param name the name of the header to return.
+ * @return the first header whose name property equals <code>name</code>
+ * or <code>null</code> 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 <code>null</code> is returned.
+ *
+ * @param name the name of the header to return.
+ * @return the last header whose name property equals <code>name</code>.
+ * or <code>null</code> 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
+ * <code>null</code> 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)}.
+ */
+ HttpParams getParams();
+
+ /**
+ * Provides parameters to be used for the processing of this message.
+ * @param params the parameters
+ */
+ void setParams(HttpParams params);
+
+}
diff --git a/src/org/apache/http/HttpRequest.java b/src/org/apache/http/HttpRequest.java
new file mode 100644
index 0000000..8558a97
--- /dev/null
+++ b/src/org/apache/http/HttpRequest.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpRequest.java $
+ * $Revision: 528428 $
+ * $Date: 2007-04-13 03:26:04 -0700 (Fri, 13 Apr 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * An HTTP request.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 528428 $
+ *
+ * @since 4.0
+ */
+public interface HttpRequest extends HttpMessage {
+
+ /**
+ * Returns the request line of this request.
+ * @return the request line.
+ */
+ RequestLine getRequestLine();
+
+}
diff --git a/src/org/apache/http/HttpRequestFactory.java b/src/org/apache/http/HttpRequestFactory.java
new file mode 100644
index 0000000..d385127
--- /dev/null
+++ b/src/org/apache/http/HttpRequestFactory.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpRequestFactory.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * A factory for {@link HttpRequest HttpRequest} objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ *
+ * @since 4.0
+ */
+public interface HttpRequestFactory {
+
+ HttpRequest newHttpRequest(RequestLine requestline)
+ throws MethodNotSupportedException;
+
+ HttpRequest newHttpRequest(String method, String uri)
+ throws MethodNotSupportedException;
+
+}
diff --git a/src/org/apache/http/HttpRequestInterceptor.java b/src/org/apache/http/HttpRequestInterceptor.java
new file mode 100644
index 0000000..db2194f
--- /dev/null
+++ b/src/org/apache/http/HttpRequestInterceptor.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpRequestInterceptor.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+import org.apache.http.protocol.HttpContext;
+
+
+/**
+ * Processes a request.
+ * Provides the ability to process a request before it is sent
+ * to the server or after it has received on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 618367 $
+ *
+ * @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 IOException in case of an IO problem
+ * @throws HttpException in case of a protocol or other problem
+ */
+ void process(HttpRequest request, HttpContext context)
+ throws HttpException, IOException;
+
+}
diff --git a/src/org/apache/http/HttpResponse.java b/src/org/apache/http/HttpResponse.java
new file mode 100644
index 0000000..f232f86
--- /dev/null
+++ b/src/org/apache/http/HttpResponse.java
@@ -0,0 +1,161 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpResponse.java $
+ * $Revision: 652956 $
+ * $Date: 2008-05-02 17:13:05 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+import java.util.Locale;
+
+
+/**
+ * An HTTP response.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 652956 $
+ *
+ * @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 <code>null</code> 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 <code>null</code> to omit
+ */
+ void setStatusLine(ProtocolVersion ver, int code, String reason);
+
+ /**
+ * Updates the status line of this response with a new status code.
+ * The status line can only be updated if it is available. It must
+ * have been set either explicitly or in a constructor.
+ * <br/>
+ * The reason phrase will be updated according to the new status code,
+ * based on the current {@link #getLocale locale}. It can be set
+ * explicitly using {@link #setReasonPhrase setReasonPhrase}.
+ *
+ * @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.
+ * The status line can only be updated if it is available. It must
+ * have been set either explicitly or in a constructor.
+ *
+ * @param reason the new reason phrase as a single-line string, or
+ * <code>null</code> 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
+ * <code>null</code> if there is none
+ */
+ HttpEntity getEntity();
+
+ /**
+ * Associates a response entity with this response.
+ *
+ * @param entity the entity to associate with this response, or
+ * <code>null</code> to unset
+ */
+ 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 <code>null</code>
+ */
+ Locale getLocale();
+
+ /**
+ * Changes the locale of this response.
+ * If there is a status line, it's reason phrase will be updated
+ * according to the status code and new locale.
+ *
+ * @param loc the new locale
+ *
+ * @see #getLocale getLocale
+ * @see #setStatusCode setStatusCode
+ */
+ void setLocale(Locale loc);
+}
diff --git a/src/org/apache/http/HttpResponseFactory.java b/src/org/apache/http/HttpResponseFactory.java
new file mode 100644
index 0000000..3ed06ff
--- /dev/null
+++ b/src/org/apache/http/HttpResponseFactory.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpResponseFactory.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.protocol.HttpContext;
+
+
+/**
+ * A factory for {@link HttpResponse HttpResponse} objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @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
+ * <code>null</code> 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
+ * <code>null</code> to use the default locale
+ *
+ * @return the new response with the argument status line
+ */
+ HttpResponse newHttpResponse(StatusLine statusline,
+ HttpContext context);
+
+}
diff --git a/src/org/apache/http/HttpResponseInterceptor.java b/src/org/apache/http/HttpResponseInterceptor.java
new file mode 100644
index 0000000..cae1526
--- /dev/null
+++ b/src/org/apache/http/HttpResponseInterceptor.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpResponseInterceptor.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Processes a response.
+ * Provides the ability to process a response before it is sent
+ * to the client or after it has been received on the client side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 618367 $
+ *
+ * @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 IOException in case of an IO problem
+ * @throws HttpException in case of a protocol or other problem
+ */
+ void process(HttpResponse response, HttpContext context)
+ throws HttpException, IOException;
+
+}
diff --git a/src/org/apache/http/HttpServerConnection.java b/src/org/apache/http/HttpServerConnection.java
new file mode 100644
index 0000000..8cea544
--- /dev/null
+++ b/src/org/apache/http/HttpServerConnection.java
@@ -0,0 +1,96 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpServerConnection.java $
+ * $Revision: 542199 $
+ * $Date: 2007-05-28 04:23:46 -0700 (Mon, 28 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * An HTTP connection for use on the server side.
+ * Requests are received, responses are sent.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 542199 $
+ *
+ * @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
+ * @throws IOException
+ */
+ 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
+ * @throws IOException
+ */
+ 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
+ * @throws IOException
+ */
+ 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
+ * @throws IOException
+ */
+ void sendResponseEntity(HttpResponse response)
+ throws HttpException, IOException;
+
+ /**
+ * Sends all pending buffered data over this connection.
+ * @throws IOException
+ */
+ void flush()
+ throws IOException;
+
+}
diff --git a/src/org/apache/http/HttpStatus.java b/src/org/apache/http/HttpStatus.java
new file mode 100644
index 0000000..f8f7f5d
--- /dev/null
+++ b/src/org/apache/http/HttpStatus.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpStatus.java $
+ * $Revision: 503381 $
+ * $Date: 2007-02-04 02:59:10 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * 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
+ * @author Unascribed
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ *
+ * @version $Revision: 503381 $
+ */
+public interface HttpStatus {
+
+ // --- 1xx Informational ---
+
+ /** <tt>100 Continue</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_CONTINUE = 100;
+ /** <tt>101 Switching Protocols</tt> (HTTP/1.1 - RFC 2616)*/
+ public static final int SC_SWITCHING_PROTOCOLS = 101;
+ /** <tt>102 Processing</tt> (WebDAV - RFC 2518) */
+ public static final int SC_PROCESSING = 102;
+
+ // --- 2xx Success ---
+
+ /** <tt>200 OK</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_OK = 200;
+ /** <tt>201 Created</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_CREATED = 201;
+ /** <tt>202 Accepted</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_ACCEPTED = 202;
+ /** <tt>203 Non Authoritative Information</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
+ /** <tt>204 No Content</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_NO_CONTENT = 204;
+ /** <tt>205 Reset Content</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_RESET_CONTENT = 205;
+ /** <tt>206 Partial Content</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_PARTIAL_CONTENT = 206;
+ /**
+ * <tt>207 Multi-Status</tt> (WebDAV - RFC 2518) or <tt>207 Partial Update
+ * OK</tt> (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?)
+ */
+ public static final int SC_MULTI_STATUS = 207;
+
+ // --- 3xx Redirection ---
+
+ /** <tt>300 Mutliple Choices</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_MULTIPLE_CHOICES = 300;
+ /** <tt>301 Moved Permanently</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_MOVED_PERMANENTLY = 301;
+ /** <tt>302 Moved Temporarily</tt> (Sometimes <tt>Found</tt>) (HTTP/1.0 - RFC 1945) */
+ public static final int SC_MOVED_TEMPORARILY = 302;
+ /** <tt>303 See Other</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_SEE_OTHER = 303;
+ /** <tt>304 Not Modified</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_NOT_MODIFIED = 304;
+ /** <tt>305 Use Proxy</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_USE_PROXY = 305;
+ /** <tt>307 Temporary Redirect</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_TEMPORARY_REDIRECT = 307;
+
+ // --- 4xx Client Error ---
+
+ /** <tt>400 Bad Request</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_BAD_REQUEST = 400;
+ /** <tt>401 Unauthorized</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_UNAUTHORIZED = 401;
+ /** <tt>402 Payment Required</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_PAYMENT_REQUIRED = 402;
+ /** <tt>403 Forbidden</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_FORBIDDEN = 403;
+ /** <tt>404 Not Found</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_NOT_FOUND = 404;
+ /** <tt>405 Method Not Allowed</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_METHOD_NOT_ALLOWED = 405;
+ /** <tt>406 Not Acceptable</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_NOT_ACCEPTABLE = 406;
+ /** <tt>407 Proxy Authentication Required</tt> (HTTP/1.1 - RFC 2616)*/
+ public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+ /** <tt>408 Request Timeout</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_REQUEST_TIMEOUT = 408;
+ /** <tt>409 Conflict</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_CONFLICT = 409;
+ /** <tt>410 Gone</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_GONE = 410;
+ /** <tt>411 Length Required</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_LENGTH_REQUIRED = 411;
+ /** <tt>412 Precondition Failed</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_PRECONDITION_FAILED = 412;
+ /** <tt>413 Request Entity Too Large</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_REQUEST_TOO_LONG = 413;
+ /** <tt>414 Request-URI Too Long</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_REQUEST_URI_TOO_LONG = 414;
+ /** <tt>415 Unsupported Media Type</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+ /** <tt>416 Requested Range Not Satisfiable</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+ /** <tt>417 Expectation Failed</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_EXPECTATION_FAILED = 417;
+
+ /**
+ * Static constant for a 418 error.
+ * <tt>418 Unprocessable Entity</tt> (WebDAV drafts?)
+ * or <tt>418 Reauthentication Required</tt> (HTTP/1.1 drafts?)
+ */
+ // not used
+ // public static final int SC_UNPROCESSABLE_ENTITY = 418;
+
+ /**
+ * Static constant for a 419 error.
+ * <tt>419 Insufficient Space on Resource</tt>
+ * (WebDAV - draft-ietf-webdav-protocol-05?)
+ * or <tt>419 Proxy Reauthentication Required</tt>
+ * (HTTP/1.1 drafts?)
+ */
+ public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
+ /**
+ * Static constant for a 420 error.
+ * <tt>420 Method Failure</tt>
+ * (WebDAV - draft-ietf-webdav-protocol-05?)
+ */
+ public static final int SC_METHOD_FAILURE = 420;
+ /** <tt>422 Unprocessable Entity</tt> (WebDAV - RFC 2518) */
+ public static final int SC_UNPROCESSABLE_ENTITY = 422;
+ /** <tt>423 Locked</tt> (WebDAV - RFC 2518) */
+ public static final int SC_LOCKED = 423;
+ /** <tt>424 Failed Dependency</tt> (WebDAV - RFC 2518) */
+ public static final int SC_FAILED_DEPENDENCY = 424;
+
+ // --- 5xx Server Error ---
+
+ /** <tt>500 Server Error</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_INTERNAL_SERVER_ERROR = 500;
+ /** <tt>501 Not Implemented</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_NOT_IMPLEMENTED = 501;
+ /** <tt>502 Bad Gateway</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_BAD_GATEWAY = 502;
+ /** <tt>503 Service Unavailable</tt> (HTTP/1.0 - RFC 1945) */
+ public static final int SC_SERVICE_UNAVAILABLE = 503;
+ /** <tt>504 Gateway Timeout</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_GATEWAY_TIMEOUT = 504;
+ /** <tt>505 HTTP Version Not Supported</tt> (HTTP/1.1 - RFC 2616) */
+ public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
+
+ /** <tt>507 Insufficient Storage</tt> (WebDAV - RFC 2518) */
+ public static final int SC_INSUFFICIENT_STORAGE = 507;
+
+}
diff --git a/src/org/apache/http/HttpVersion.java b/src/org/apache/http/HttpVersion.java
new file mode 100644
index 0000000..243f199
--- /dev/null
+++ b/src/org/apache/http/HttpVersion.java
@@ -0,0 +1,104 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/HttpVersion.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.Serializable;
+
+/**
+ * Represents an HTTP version, as specified in RFC 2616.
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 609106 $ $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ */
+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(int major, 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
+ */
+ public ProtocolVersion forVersion(int major, 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/src/org/apache/http/MalformedChunkCodingException.java b/src/org/apache/http/MalformedChunkCodingException.java
new file mode 100644
index 0000000..2267a2e
--- /dev/null
+++ b/src/org/apache/http/MalformedChunkCodingException.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/MalformedChunkCodingException.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * Signals a malformed chunked stream.
+ */
+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/src/org/apache/http/MethodNotSupportedException.java b/src/org/apache/http/MethodNotSupportedException.java
new file mode 100644
index 0000000..3ccf72d
--- /dev/null
+++ b/src/org/apache/http/MethodNotSupportedException.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/MethodNotSupportedException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+/**
+ * Indicates that an HTTP method is not supported.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ *
+ * @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 <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public MethodNotSupportedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/NameValuePair.java b/src/org/apache/http/NameValuePair.java
new file mode 100644
index 0000000..1ab861a
--- /dev/null
+++ b/src/org/apache/http/NameValuePair.java
@@ -0,0 +1,108 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/NameValuePair.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * A simple class encapsulating an attribute/value pair.
+ * <p>
+ * This class comforms to the generic grammar and formatting rules outlined in the
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2">Section 2.2</a>
+ * and
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>
+ * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
+ * </p>
+ * <h>2.2 Basic Rules</h>
+ * <p>
+ * The following rules are used throughout this specification to describe basic parsing constructs.
+ * The US-ASCII coded character set is defined by ANSI X3.4-1986.
+ * </p>
+ * <pre>
+ * OCTET = <any 8-bit sequence of data>
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ * UPALPHA = <any US-ASCII uppercase letter "A".."Z">
+ * LOALPHA = <any US-ASCII lowercase letter "a".."z">
+ * ALPHA = UPALPHA | LOALPHA
+ * DIGIT = <any US-ASCII digit "0".."9">
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * CR = <US-ASCII CR, carriage return (13)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ * SP = <US-ASCII SP, space (32)>
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ * <"> = <US-ASCII double-quote mark (34)>
+ * </pre>
+ * <p>
+ * Many HTTP/1.1 header field values consist of words separated by LWS or special
+ * characters. These special characters MUST be in a quoted string to be used within
+ * a parameter value (as defined in section 3.6).
+ * <p>
+ * <pre>
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ * </pre>
+ * <p>
+ * A string of text is parsed as a single word if it is quoted using double-quote marks.
+ * </p>
+ * <pre>
+ * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext = <any TEXT except <">>
+ * </pre>
+ * <p>
+ * The backslash character ("\") MAY be used as a single-character quoting mechanism only
+ * within quoted-string and comment constructs.
+ * </p>
+ * <pre>
+ * quoted-pair = "\" CHAR
+ * </pre>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ * Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter = attribute "=" value
+ * attribute = token
+ * value = token | quoted-string
+ * </pre>
+ *
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ *
+ */
+public interface NameValuePair {
+
+ String getName();
+
+ String getValue();
+
+}
diff --git a/src/org/apache/http/NoHttpResponseException.java b/src/org/apache/http/NoHttpResponseException.java
new file mode 100644
index 0000000..a02ef5a
--- /dev/null
+++ b/src/org/apache/http/NoHttpResponseException.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/NoHttpResponseException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.IOException;
+
+/**
+ * <p>
+ * Signals that the target server failed to respond with a valid HTTP response.
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ */
+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(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/org/apache/http/ParseException.java b/src/org/apache/http/ParseException.java
new file mode 100644
index 0000000..97083b2
--- /dev/null
+++ b/src/org/apache/http/ParseException.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ParseException.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Indicates 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 exceptions, 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 <code>null</code>
+ */
+ public ParseException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/org/apache/http/ProtocolException.java b/src/org/apache/http/ProtocolException.java
new file mode 100644
index 0000000..b4c34ee
--- /dev/null
+++ b/src/org/apache/http/ProtocolException.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ProtocolException.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Signals that an HTTP protocol violation has occurred.
+ * For example a malformed status line or headers, a missing message body, etc.
+ *
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ *
+ * @since 4.0
+ */
+public class ProtocolException extends HttpException {
+
+ private static final long serialVersionUID = -2143571074341228994L;
+
+ /**
+ * Creates a new ProtocolException with a <tt>null</tt> detail message.
+ */
+ public ProtocolException() {
+ super();
+ }
+
+ /**
+ * Creates a new ProtocolException with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public ProtocolException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new ProtocolException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public ProtocolException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/ProtocolVersion.java b/src/org/apache/http/ProtocolVersion.java
new file mode 100644
index 0000000..ced76a5
--- /dev/null
+++ b/src/org/apache/http/ProtocolVersion.java
@@ -0,0 +1,287 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ProtocolVersion.java $
+ * $Revision: 609106 $
+ * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.io.Serializable;
+import org.apache.http.util.CharArrayBuffer;
+
+
+/**
+ * Represents a protocol version, as specified in RFC 2616.
+ * RFC 2616 specifies only HTTP versions, like "HTTP/1.1" and "HTTP/1.0".
+ * RFC 3261 specifies a message format that is identical to HTTP except
+ * for the protocol name. It defines a protocol version "SIP/2.0".
+ * There are some nitty-gritty differences between the interpretation
+ * of versions in HTTP and SIP. In those cases, HTTP takes precedence.
+ * <p>
+ * 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.
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 609106 $
+ */
+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(String protocol, int major, int minor) {
+ if (protocol == null) {
+ throw new IllegalArgumentException
+ ("Protocol name must not be null.");
+ }
+ if (major < 0) {
+ throw new IllegalArgumentException
+ ("Protocol major version number must not be negative.");
+ }
+ if (minor < 0) {
+ throw new IllegalArgumentException
+ ("Protocol minor version number may not be negative");
+ }
+ this.protocol = protocol;
+ this.major = major;
+ this.minor = minor;
+ }
+
+ /**
+ * 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.
+ * <br/>
+ * The default implementation in this class returns <code>this</code>
+ * 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(int major, 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
+ */
+ 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 <i>not</i> 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 <code>true</code> if the argument is the same protocol version,
+ * <code>false</code> otherwise
+ */
+ public final boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ProtocolVersion)) {
+ return false;
+ }
+ 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 <code>true</code> if {@link #compareToVersion compareToVersion}
+ * can be called with the argument, <code>false</code> otherwise
+ */
+ public boolean isComparable(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 <i>not</i> define a total ordering, as it would be
+ * required for {@link java.lang.Comparable}.
+ *
+ * @param that the protocl 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 <code>null</code>
+ */
+ public int compareToVersion(ProtocolVersion that) {
+ if (that == null) {
+ throw new IllegalArgumentException
+ ("Protocol version must not be null.");
+ }
+ if (!this.protocol.equals(that.protocol)) {
+ throw new IllegalArgumentException
+ ("Versions for different protocols cannot be compared. " +
+ 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 <code>true</code> if this protocol version is
+ * {@link #isComparable comparable} to the argument
+ * and {@link #compareToVersion compares} as greater or equal,
+ * <code>false</code> otherwise
+ */
+ public final boolean greaterEquals(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 <code>true</code> if this protocol version is
+ * {@link #isComparable comparable} to the argument
+ * and {@link #compareToVersion compares} as less or equal,
+ * <code>false</code> otherwise
+ */
+ public final boolean lessEquals(ProtocolVersion version) {
+ return isComparable(version) && (compareToVersion(version) <= 0);
+ }
+
+
+ /**
+ * Converts this protocol version to a string.
+ *
+ * @return a protocol version string, like "HTTP/1.1"
+ */
+ public String toString() {
+ CharArrayBuffer buffer = new CharArrayBuffer(16);
+ buffer.append(this.protocol);
+ buffer.append('/');
+ buffer.append(Integer.toString(this.major));
+ buffer.append('.');
+ buffer.append(Integer.toString(this.minor));
+ return buffer.toString();
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+}
diff --git a/src/org/apache/http/ReasonPhraseCatalog.java b/src/org/apache/http/ReasonPhraseCatalog.java
new file mode 100644
index 0000000..12ad6d9
--- /dev/null
+++ b/src/org/apache/http/ReasonPhraseCatalog.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ReasonPhraseCatalog.java $
+ * $Revision: 505744 $
+ * $Date: 2007-02-10 10:58:45 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import java.util.Locale;
+
+
+/**
+ * Interface for obtaining reason phrases for HTTP status codes.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 505744 $
+ *
+ * @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 <code>null</code> if unknown
+ */
+ public String getReason(int status, Locale loc)
+ ;
+
+}
diff --git a/src/org/apache/http/RequestLine.java b/src/org/apache/http/RequestLine.java
new file mode 100644
index 0000000..e865929
--- /dev/null
+++ b/src/org/apache/http/RequestLine.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/RequestLine.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * The first line of an {@link HttpRequest HttpRequest}.
+ * It contains the method, URI, and HTTP version of the request.
+ * For details, see RFC 2616.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @since 4.0
+ */
+public interface RequestLine {
+
+ String getMethod();
+
+ ProtocolVersion getProtocolVersion();
+
+ String getUri();
+
+}
diff --git a/src/org/apache/http/StatusLine.java b/src/org/apache/http/StatusLine.java
new file mode 100644
index 0000000..aa8a3bc
--- /dev/null
+++ b/src/org/apache/http/StatusLine.java
@@ -0,0 +1,55 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/StatusLine.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+/**
+ * Represents a status line as returned from a HTTP server.
+ * See <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>,
+ * section 6.1.
+ * Implementations are expected to be thread safe.
+ *
+ * @see HttpStatus
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @version $Id: StatusLine.java 573864 2007-09-08 15:53:25Z rolandw $
+ *
+ * @since 4.0
+ */
+public interface StatusLine {
+
+ ProtocolVersion getProtocolVersion();
+
+ int getStatusCode();
+
+ String getReasonPhrase();
+
+}
diff --git a/src/org/apache/http/TokenIterator.java b/src/org/apache/http/TokenIterator.java
new file mode 100644
index 0000000..bfe3473
--- /dev/null
+++ b/src/org/apache/http/TokenIterator.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/TokenIterator.java $
+ * $Revision: 601000 $
+ * $Date: 2007-12-04 09:03:49 -0800 (Tue, 04 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+
+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.
+ *
+ * @version $Revision: 601000 $
+ */
+public interface TokenIterator extends Iterator {
+
+ /**
+ * Indicates whether there is another token in this iteration.
+ *
+ * @return <code>true</code> if there is another token,
+ * <code>false</code> 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/src/org/apache/http/UnsupportedHttpVersionException.java b/src/org/apache/http/UnsupportedHttpVersionException.java
new file mode 100644
index 0000000..716d7a5
--- /dev/null
+++ b/src/org/apache/http/UnsupportedHttpVersionException.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL:https://svn.apache.org/repos/asf/jakarta/httpcomponents/trunk/coyote-httpconnector/src/java/org/apache/http/tcconnector/UnsupportedHttpVersionException.java $
+ * $Revision:379772 $
+ * $Date:2006-02-22 14:52:29 +0100 (Wed, 22 Feb 2006) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Indicates an unsupported version of the HTTP protocol.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision:379772 $
+ */
+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/src/org/apache/http/auth/AUTH.java b/src/org/apache/http/auth/AUTH.java
new file mode 100644
index 0000000..8ab6dc2
--- /dev/null
+++ b/src/org/apache/http/auth/AUTH.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AUTH.java $
+ * $Revision: 618365 $
+ * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+/**
+ * Constants and static helpers related to the HTTP authentication.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+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/src/org/apache/http/auth/AuthScheme.java b/src/org/apache/http/auth/AuthScheme.java
new file mode 100644
index 0000000..bdaebdb
--- /dev/null
+++ b/src/org/apache/http/auth/AuthScheme.java
@@ -0,0 +1,140 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthScheme.java $
+ * $Revision: 537144 $
+ * $Date: 2007-05-11 02:30:13 -0700 (Fri, 11 May 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+
+/**
+ * <p>
+ * This interface represents an abstract challenge-response oriented
+ * authentication scheme.
+ * </p>
+ * <p>
+ * An authentication scheme should be able to support the following
+ * functions:
+ * <ul>
+ * <li>Parse and process the challenge sent by the targer server
+ * in response to request for a protected resource
+ * <li>Provide its textual designation
+ * <li>Provide its parameters, if available
+ * <li>Provide the realm this authentication scheme is applicable to,
+ * if available
+ * <li>Generate authorization string for the given set of credentials,
+ * request method and URI as specificed in the HTTP request line
+ * in response to the actual authorization challenge
+ * </ul>
+ * </p>
+ * <p>
+ * Authentication schemes may ignore method name and URI parameters
+ * if they are not relevant for the given authentication mechanism
+ * </p>
+ * <p>
+ * Authentication schemes may be stateful involving a series of
+ * challenge-response exchanges
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ *
+ * @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
+ * <code>null</code>.
+ *
+ * @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 <tt>true</tt> if the scheme is connection based, <tt>false</tt>
+ * 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 <tt>true</tt> if the authentication process has been completed,
+ * <tt>false</tt> 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
+ */
+ Header authenticate(Credentials credentials, HttpRequest request)
+ throws AuthenticationException;
+
+}
diff --git a/src/org/apache/http/auth/AuthSchemeFactory.java b/src/org/apache/http/auth/AuthSchemeFactory.java
new file mode 100644
index 0000000..8f985b0
--- /dev/null
+++ b/src/org/apache/http/auth/AuthSchemeFactory.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthSchemeFactory.java $
+ * $Revision: 527900 $
+ * $Date: 2007-04-12 05:35:25 -0700 (Thu, 12 Apr 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface AuthSchemeFactory {
+
+ AuthScheme newInstance(HttpParams params);
+
+}
diff --git a/src/org/apache/http/auth/AuthSchemeRegistry.java b/src/org/apache/http/auth/AuthSchemeRegistry.java
new file mode 100644
index 0000000..62a5d4d
--- /dev/null
+++ b/src/org/apache/http/auth/AuthSchemeRegistry.java
@@ -0,0 +1,149 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthSchemeRegistry.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * Authentication scheme registry that can be used to obtain the corresponding
+ * authentication scheme implementation for a given type of authorization challenge.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * @version $Revision: 652950 $
+ * @since 4.0
+ */
+public final class AuthSchemeRegistry {
+
+ private final Map<String,AuthSchemeFactory> registeredSchemes;
+
+ public AuthSchemeRegistry() {
+ super();
+ this.registeredSchemes = new LinkedHashMap<String,AuthSchemeFactory>();
+ }
+
+ /**
+ * 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}.
+ *
+ * <p>
+ * Please note that custom authentication preferences, if used, need to be updated accordingly
+ * for the new {@link AuthScheme authentication scheme} to take effect.
+ * </p>
+ *
+ * @param name the identifier for this scheme
+ * @param factory the {@link AuthSchemeFactory} class to register
+ *
+ * @see #getAuthScheme
+ */
+ public synchronized void register(
+ final String name,
+ final AuthSchemeFactory factory) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ if (factory == null) {
+ throw new IllegalArgumentException("Authentication scheme factory may not be null");
+ }
+ 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 synchronized void unregister(final String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ 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 synchronized AuthScheme getAuthScheme(final String name, final HttpParams params)
+ throws IllegalStateException {
+
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ 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 names of all registered {@link AuthScheme authentication
+ * schemes} in their default order.
+ *
+ * @return list of registered scheme names
+ */
+ public synchronized List<String> getSchemeNames() {
+ return new ArrayList<String>(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 synchronized void setItems(final Map<String, AuthSchemeFactory> map) {
+ if (map == null) {
+ return;
+ }
+ registeredSchemes.clear();
+ registeredSchemes.putAll(map);
+ }
+
+}
diff --git a/src/org/apache/http/auth/AuthScope.java b/src/org/apache/http/auth/AuthScope.java
new file mode 100644
index 0000000..c9d7f56
--- /dev/null
+++ b/src/org/apache/http/auth/AuthScope.java
@@ -0,0 +1,292 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthScope.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.util.Locale;
+
+import org.apache.http.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.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
+ *
+ * @since 4.0
+ */
+public class AuthScope {
+
+ /**
+ * The <tt>null</tt> 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 <tt>-1</tt> value represents any port.
+ */
+ public static final int ANY_PORT = -1;
+
+ /**
+ * The <tt>null</tt> value represents any realm.
+ */
+ public static final String ANY_REALM = null;
+
+ /**
+ * The <tt>null</tt> 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
+ * <tt>host</tt>, <tt>port</tt>, <tt>realm</tt>, and
+ * <tt>authentication scheme</tt>.
+ *
+ * @param host the host the credentials apply to. May be set
+ * to <tt>null</tt> if credenticals are applicable to
+ * any host.
+ * @param port the port the credentials apply to. May be set
+ * to negative value if credenticals are applicable to
+ * any port.
+ * @param realm the realm the credentials apply to. May be set
+ * to <tt>null</tt> if credenticals are applicable to
+ * any realm.
+ * @param scheme the authentication scheme the credentials apply to.
+ * May be set to <tt>null</tt> if credenticals are applicable to
+ * any authentication scheme.
+ */
+ public AuthScope(final String host, 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);
+ }
+
+ /** Creates a new credentials scope for the given
+ * <tt>host</tt>, <tt>port</tt>, <tt>realm</tt>, and any
+ * authentication scheme.
+ *
+ * @param host the host the credentials apply to. May be set
+ * to <tt>null</tt> if credenticals are applicable to
+ * any host.
+ * @param port the port the credentials apply to. May be set
+ * to negative value if credenticals are applicable to
+ * any port.
+ * @param realm the realm the credentials apply to. May be set
+ * to <tt>null</tt> if credenticals are applicable to
+ * any realm.
+ */
+ public AuthScope(final String host, int port, final String realm) {
+ this(host, port, realm, ANY_SCHEME);
+ }
+
+ /** Creates a new credentials scope for the given
+ * <tt>host</tt>, <tt>port</tt>, any realm name, and any
+ * authentication scheme.
+ *
+ * @param host the host the credentials apply to. May be set
+ * to <tt>null</tt> if credenticals are applicable to
+ * any host.
+ * @param port the port the credentials apply to. May be set
+ * to negative value if credenticals are applicable to
+ * any port.
+ */
+ public AuthScope(final String host, int port) {
+ this(host, port, ANY_REALM, ANY_SCHEME);
+ }
+
+ /**
+ * Creates a copy of the given credentials scope.
+ */
+ public AuthScope(final AuthScope authscope) {
+ super();
+ if (authscope == null) {
+ throw new IllegalArgumentException("Scope may not be null");
+ }
+ 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(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof AuthScope)) {
+ return super.equals(o);
+ }
+ 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() {
+ StringBuffer buffer = new StringBuffer();
+ 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("<any realm>");
+ }
+ 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/src/org/apache/http/auth/AuthState.java b/src/org/apache/http/auth/AuthState.java
new file mode 100644
index 0000000..f55bf86
--- /dev/null
+++ b/src/org/apache/http/auth/AuthState.java
@@ -0,0 +1,147 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthState.java $
+ * $Revision: 659971 $
+ * $Date: 2008-05-25 05:01:22 -0700 (Sun, 25 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+
+/**
+ * This class provides detailed information about the state of the
+ * authentication process.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class AuthState {
+
+ /** Actual authentication scheme */
+ private AuthScheme authScheme;
+
+ /** Actual authentication scope */
+ private AuthScope authScope;
+
+ /** Credentials selected for authentication */
+ private Credentials credentials;
+
+ /**
+ * Default constructor.
+ *
+ */
+ public AuthState() {
+ super();
+ }
+
+ /**
+ * Invalidates the authentication state by resetting its parameters.
+ */
+ public void invalidate() {
+ this.authScheme = null;
+ this.authScope = null;
+ this.credentials = null;
+ }
+
+ public boolean isValid() {
+ return this.authScheme != null;
+ }
+
+ /**
+ * Assigns the given {@link AuthScheme authentication scheme}.
+ *
+ * @param authScheme the {@link AuthScheme authentication scheme}
+ */
+ public void setAuthScheme(final AuthScheme authScheme) {
+ if (authScheme == null) {
+ invalidate();
+ return;
+ }
+ this.authScheme = authScheme;
+ }
+
+ /**
+ * Returns the {@link AuthScheme authentication scheme}.
+ *
+ * @return {@link AuthScheme authentication scheme}
+ */
+ public AuthScheme getAuthScheme() {
+ return this.authScheme;
+ }
+
+
+ /**
+ * Returns user {@link Credentials} selected for authentication if available
+ *
+ * @return user credentials if available, <code>null</code otherwise
+ */
+ public Credentials getCredentials() {
+ return this.credentials;
+ }
+
+
+ /**
+ * Sets user {@link Credentials} to be used for authentication
+ *
+ * @param credentials User credentials
+ */
+ public void setCredentials(final Credentials credentials) {
+ this.credentials = credentials;
+ }
+
+
+ /**
+ * Returns actual {@link AuthScope} if available
+ *
+ * @return actual authentication scope if available, <code>null</code otherwise
+ */
+ public AuthScope getAuthScope() {
+ return this.authScope;
+ }
+
+ /**
+ * Sets actual {@link AuthScope}.
+ *
+ * @param authScope Authentication scope
+ */
+ public void setAuthScope(final AuthScope authScope) {
+ this.authScope = authScope;
+ }
+
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("auth scope [");
+ buffer.append(this.authScope);
+ buffer.append("]; credentials set [");
+ buffer.append(this.credentials != null ? "true" : "false");
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+}
diff --git a/src/org/apache/http/auth/AuthenticationException.java b/src/org/apache/http/auth/AuthenticationException.java
new file mode 100644
index 0000000..8b307be
--- /dev/null
+++ b/src/org/apache/http/auth/AuthenticationException.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/AuthenticationException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals a failure in authentication process
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class AuthenticationException extends ProtocolException {
+
+ private static final long serialVersionUID = -6794031905674764776L;
+
+ /**
+ * Creates a new AuthenticationException with a <tt>null</tt> detail message.
+ */
+ public AuthenticationException() {
+ super();
+ }
+
+ /**
+ * Creates a new AuthenticationException with the specified message.
+ *
+ * @param message the exception detail message
+ */
+ public AuthenticationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new AuthenticationException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public AuthenticationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/org/apache/http/auth/BasicUserPrincipal.java b/src/org/apache/http/auth/BasicUserPrincipal.java
new file mode 100644
index 0000000..2485011
--- /dev/null
+++ b/src/org/apache/http/auth/BasicUserPrincipal.java
@@ -0,0 +1,90 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/BasicUserPrincipal.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+
+import org.apache.http.util.LangUtils;
+
+/**
+ * Basic user principal used for HTTP authentication
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public final class BasicUserPrincipal implements Principal {
+
+ private final String username;
+
+ public BasicUserPrincipal(final String username) {
+ super();
+ if (username == null) {
+ throw new IllegalArgumentException("User name may not be null");
+ }
+ 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(Object o) {
+ if (o == null) return false;
+ if (this == o) return true;
+ if (o instanceof BasicUserPrincipal) {
+ BasicUserPrincipal that = (BasicUserPrincipal) o;
+ if (LangUtils.equals(this.username, that.username)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("[principal: ");
+ buffer.append(this.username);
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+}
+
diff --git a/src/org/apache/http/auth/Credentials.java b/src/org/apache/http/auth/Credentials.java
new file mode 100644
index 0000000..846a23b
--- /dev/null
+++ b/src/org/apache/http/auth/Credentials.java
@@ -0,0 +1,49 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/Credentials.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+
+/**
+ * User name and password based authentication credentials.
+ *
+ * @author Unascribed
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @version $Revision: 658430 $ $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ */
+public interface Credentials {
+
+ Principal getUserPrincipal();
+
+ String getPassword();
+
+}
diff --git a/src/org/apache/http/auth/InvalidCredentialsException.java b/src/org/apache/http/auth/InvalidCredentialsException.java
new file mode 100644
index 0000000..50155ec
--- /dev/null
+++ b/src/org/apache/http/auth/InvalidCredentialsException.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/InvalidCredentialsException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+/**
+ * Authentication credentials required to respond to a authentication
+ * challenge are invalid
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class InvalidCredentialsException extends AuthenticationException {
+
+ private static final long serialVersionUID = -4834003835215460648L;
+
+ /**
+ * Creates a new InvalidCredentialsException with a <tt>null</tt> detail message.
+ */
+ public InvalidCredentialsException() {
+ super();
+ }
+
+ /**
+ * Creates a new InvalidCredentialsException with the specified message.
+ *
+ * @param message the exception detail message
+ */
+ public InvalidCredentialsException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new InvalidCredentialsException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public InvalidCredentialsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/auth/MalformedChallengeException.java b/src/org/apache/http/auth/MalformedChallengeException.java
new file mode 100644
index 0000000..8c7e373
--- /dev/null
+++ b/src/org/apache/http/auth/MalformedChallengeException.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/MalformedChallengeException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals that authentication challenge is in some way invalid or
+ * illegal in the given context
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class MalformedChallengeException extends ProtocolException {
+
+ private static final long serialVersionUID = 814586927989932284L;
+
+ /**
+ * Creates a new MalformedChallengeException with a <tt>null</tt> detail message.
+ */
+ public MalformedChallengeException() {
+ super();
+ }
+
+ /**
+ * Creates a new MalformedChallengeException with the specified message.
+ *
+ * @param message the exception detail message
+ */
+ public MalformedChallengeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new MalformedChallengeException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public MalformedChallengeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/auth/NTCredentials.java b/src/org/apache/http/auth/NTCredentials.java
new file mode 100644
index 0000000..6800c42
--- /dev/null
+++ b/src/org/apache/http/auth/NTCredentials.java
@@ -0,0 +1,180 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/NTCredentials.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+import java.util.Locale;
+
+import org.apache.http.util.LangUtils;
+
+/** {@link Credentials} specific to the Windows platform.
+ *
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 2.0
+ */
+public class NTCredentials implements Credentials {
+
+ /** 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(String usernamePassword) {
+ super();
+ if (usernamePassword == null) {
+ throw new IllegalArgumentException("Username:password string may not be null");
+ }
+ String username;
+ int atColon = usernamePassword.indexOf(':');
+ if (atColon >= 0) {
+ username = usernamePassword.substring(0, atColon);
+ this.password = usernamePassword.substring(atColon + 1);
+ } else {
+ username = usernamePassword;
+ this.password = null;
+ }
+ 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();
+ if (userName == null) {
+ throw new IllegalArgumentException("User name may not be null");
+ }
+ 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(Object o) {
+ if (o == null) return false;
+ if (this == o) return true;
+ if (o instanceof NTCredentials) {
+ 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() {
+ 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/src/org/apache/http/auth/NTUserPrincipal.java b/src/org/apache/http/auth/NTUserPrincipal.java
new file mode 100644
index 0000000..ac91bb8
--- /dev/null
+++ b/src/org/apache/http/auth/NTUserPrincipal.java
@@ -0,0 +1,113 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/NTUserPrincipal.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+import java.util.Locale;
+
+import org.apache.http.util.LangUtils;
+
+/** NT (MS Windows specific) user principal used for HTTP authentication
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class NTUserPrincipal implements Principal {
+
+ private final String username;
+ private final String domain;
+ private final String ntname;
+
+ public NTUserPrincipal(
+ final String domain,
+ final String username) {
+ super();
+ if (username == null) {
+ throw new IllegalArgumentException("User name may not be null");
+ }
+ this.username = username;
+ if (domain != null) {
+ this.domain = domain.toUpperCase(Locale.ENGLISH);
+ } else {
+ this.domain = null;
+ }
+ if (this.domain != null && this.domain.length() > 0) {
+ 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(Object o) {
+ if (o == null) return false;
+ if (this == o) return true;
+ if (o instanceof NTUserPrincipal) {
+ 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/src/org/apache/http/auth/UsernamePasswordCredentials.java b/src/org/apache/http/auth/UsernamePasswordCredentials.java
new file mode 100644
index 0000000..f82608c
--- /dev/null
+++ b/src/org/apache/http/auth/UsernamePasswordCredentials.java
@@ -0,0 +1,126 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/UsernamePasswordCredentials.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import java.security.Principal;
+
+import org.apache.http.util.LangUtils;
+
+/**
+ * Username and password {@link Credentials}
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 658430 $ $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ */
+public class UsernamePasswordCredentials implements Credentials {
+
+ 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(String usernamePassword) {
+ super();
+ if (usernamePassword == null) {
+ throw new IllegalArgumentException("Username:password string may not be null");
+ }
+ 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(String userName, String password) {
+ super();
+ if (userName == null) {
+ throw new IllegalArgumentException("Username may not be null");
+ }
+ 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(Object o) {
+ if (o == null) return false;
+ if (this == o) return true;
+ if (o instanceof UsernamePasswordCredentials) {
+ 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/src/org/apache/http/auth/package.html b/src/org/apache/http/auth/package.html
new file mode 100644
index 0000000..1493dfb
--- /dev/null
+++ b/src/org/apache/http/auth/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The API for client-side HTTP authentication against a server,
+commonly referred to as <i>HttpAuth</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/auth/params/AuthPNames.java b/src/org/apache/http/auth/params/AuthPNames.java
new file mode 100644
index 0000000..a053435
--- /dev/null
+++ b/src/org/apache/http/auth/params/AuthPNames.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/AuthPNames.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth.params;
+
+/**
+ * Parameter names for HttpAuth.
+ *
+ * @version $Revision: 578403 $
+ *
+ * @since 4.0
+ */
+public interface AuthPNames {
+
+ /**
+ * Defines the charset to be used when encoding
+ * {@link org.apache.http.auth.Credentials}.
+ * <p>
+ * This parameter expects a value of type {@link String}.
+ * If not defined, then
+ * {@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET
+ * HttpProtocolParams.HTTP_ELEMENT_CHARSET}
+ * should be used.
+ * </p>
+ */
+ public static final String CREDENTIAL_CHARSET = "http.auth.credential-charset";
+
+}
diff --git a/src/org/apache/http/auth/params/AuthParamBean.java b/src/org/apache/http/auth/params/AuthParamBean.java
new file mode 100644
index 0000000..5b27328
--- /dev/null
+++ b/src/org/apache/http/auth/params/AuthParamBean.java
@@ -0,0 +1,47 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/AuthParamBean.java $
+ * $Revision: 632313 $
+ * $Date: 2008-02-29 05:19:50 -0800 (Fri, 29 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth.params;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+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/src/org/apache/http/auth/params/AuthParams.java b/src/org/apache/http/auth/params/AuthParams.java
new file mode 100644
index 0000000..a724a8c
--- /dev/null
+++ b/src/org/apache/http/auth/params/AuthParams.java
@@ -0,0 +1,94 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/AuthParams.java $
+ * $Revision: 618365 $
+ * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth.params;
+
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * This class implements an adaptor around the {@link HttpParams} interface
+ * to simplify manipulation of the HTTP authentication specific parameters.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618365 $
+ *
+ * @since 4.0
+ *
+ * @see AuthPNames
+ */
+public final class AuthParams {
+
+ private AuthParams() {
+ super();
+ }
+
+ /**
+ * Obtains the charset for encoding
+ * {@link org.apache.http.auth.Credentials}.
+ * If not configured,
+ * {@link HTTP#DEFAULT_PROTOCOL_CHARSET HTTP.DEFAULT_PROTOCOL_CHARSET}
+ * is used instead.
+ *
+ * @return The charset
+ *
+ * @see AuthPNames#CREDENTIAL_CHARSET
+ */
+ public static String getCredentialCharset(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ String charset = (String) params.getParameter
+ (AuthPNames.CREDENTIAL_CHARSET);
+ //@@@ TODO: inconsistent with JavaDoc in AuthPNames,
+ //@@@ TODO: check HTTP_ELEMENT_CHARSET first, or fix JavaDocs
+ if (charset == null) {
+ charset = HTTP.DEFAULT_PROTOCOL_CHARSET;
+ }
+ return charset;
+ }
+
+
+ /**
+ * Sets the charset to be used when encoding
+ * {@link org.apache.http.auth.Credentials}.
+ *
+ * @param charset The charset
+ */
+ public static void setCredentialCharset(final HttpParams params, final String charset) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setParameter(AuthPNames.CREDENTIAL_CHARSET, charset);
+ }
+
+}
diff --git a/src/org/apache/http/auth/params/package.html b/src/org/apache/http/auth/params/package.html
new file mode 100644
index 0000000..f9258fd
--- /dev/null
+++ b/src/org/apache/http/auth/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/auth/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpAuth</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/AuthenticationHandler.java b/src/org/apache/http/client/AuthenticationHandler.java
new file mode 100644
index 0000000..dacc1b8
--- /dev/null
+++ b/src/org/apache/http/client/AuthenticationHandler.java
@@ -0,0 +1,61 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/AuthenticationHandler.java $
+ * $Revision: 603318 $
+ * $Date: 2007-12-11 10:06:50 -0800 (Tue, 11 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public interface AuthenticationHandler {
+
+ boolean isAuthenticationRequested(
+ HttpResponse response,
+ HttpContext context);
+
+ Map<String, Header> getChallenges(
+ HttpResponse response,
+ HttpContext context) throws MalformedChallengeException;
+
+ AuthScheme selectScheme(
+ Map<String, Header> challenges,
+ HttpResponse response,
+ HttpContext context) throws AuthenticationException;
+
+}
diff --git a/src/org/apache/http/client/CircularRedirectException.java b/src/org/apache/http/client/CircularRedirectException.java
new file mode 100644
index 0000000..08dca63
--- /dev/null
+++ b/src/org/apache/http/client/CircularRedirectException.java
@@ -0,0 +1,70 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/CircularRedirectException.java $
+ * $Revision: 558123 $
+ * $Date: 2007-07-20 13:29:58 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+/**
+ * Signals a circular redirect
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class CircularRedirectException extends RedirectException {
+
+ private static final long serialVersionUID = 6830063487001091803L;
+
+ /**
+ * Creates a new CircularRedirectException with a <tt>null</tt> detail message.
+ */
+ public CircularRedirectException() {
+ super();
+ }
+
+ /**
+ * Creates a new CircularRedirectException with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public CircularRedirectException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new CircularRedirectException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public CircularRedirectException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/client/ClientProtocolException.java b/src/org/apache/http/client/ClientProtocolException.java
new file mode 100644
index 0000000..b5a991a
--- /dev/null
+++ b/src/org/apache/http/client/ClientProtocolException.java
@@ -0,0 +1,60 @@
+/*
+ * $HeadURL: $
+ * $Revision: $
+ * $Date: $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+/**
+ * Signals an error in the HTTP protocol.
+ */
+public class ClientProtocolException extends IOException {
+
+ private static final long serialVersionUID = -5596590843227115865L;
+
+ public ClientProtocolException() {
+ super();
+ }
+
+ public ClientProtocolException(String s) {
+ super(s);
+ }
+
+ public ClientProtocolException(Throwable cause) {
+ initCause(cause);
+ }
+
+ public ClientProtocolException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+
+
+}
diff --git a/src/org/apache/http/client/CookieStore.java b/src/org/apache/http/client/CookieStore.java
new file mode 100644
index 0000000..bc239ac
--- /dev/null
+++ b/src/org/apache/http/client/CookieStore.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/CookieStore.java $
+ * $Revision: 604434 $
+ * $Date: 2007-12-15 06:45:48 -0800 (Sat, 15 Dec 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.http.cookie.Cookie;
+
+/**
+ * Abstract cookie store.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface CookieStore {
+
+ /**
+ * 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
+ */
+ void addCookie(Cookie cookie);
+
+ /**
+ * Returns all cookies contained in this store.
+ *
+ * @return all cookies
+ */
+ List<Cookie> getCookies();
+
+ /**
+ * Removes all of {@link Cookie cookies} in this store that have expired by
+ * the specified {@link java.util.Date date}.
+ *
+ * @return true if any cookies were purged.
+ */
+ boolean clearExpired(Date date);
+
+ /**
+ * Clears all cookies.
+ */
+ void clear();
+
+}
diff --git a/src/org/apache/http/client/CredentialsProvider.java b/src/org/apache/http/client/CredentialsProvider.java
new file mode 100644
index 0000000..8396d84
--- /dev/null
+++ b/src/org/apache/http/client/CredentialsProvider.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/CredentialsProvider.java $
+ * $Revision: 558124 $
+ * $Date: 2007-07-20 13:36:47 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+
+/**
+ * Abstract credentials provider.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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/src/org/apache/http/client/HttpClient.java b/src/org/apache/http/client/HttpClient.java
new file mode 100644
index 0000000..aaa09e0
--- /dev/null
+++ b/src/org/apache/http/client/HttpClient.java
@@ -0,0 +1,249 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/HttpClient.java $
+ * $Revision: 676020 $
+ * $Date: 2008-07-11 09:38:49 -0700 (Fri, 11 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+
+/**
+ * Interface for an HTTP client.
+ * HTTP clients encapsulate a smorgasbord of objects required to
+ * execute HTTP requests while handling cookies, authentication,
+ * connection management, and other features.
+ * Thread safety of HTTP clients depends on the implementation
+ * and configuration of the specific client.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 676020 $
+ *
+ * @since 4.0
+ */
+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
+ */
+ HttpParams getParams()
+ ;
+
+
+ /**
+ * Obtains the connection manager used by this client.
+ *
+ * @return the connection manager
+ */
+ ClientConnectionManager getConnectionManager()
+ ;
+
+ /**
+ * Executes a 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 a request using the given context.
+ * The route to the target will be determined by the HTTP client.
+ *
+ * @param request the request to execute
+ * @param context the context to use for the execution, or
+ * <code>null</code> 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 a request to the target using the default context.
+ *
+ * @param target the target host for the request.
+ * Implementations may accept <code>null</code>
+ * 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 a request to the target using the given context.
+ *
+ * @param target the target host for the request.
+ * Implementations may accept <code>null</code>
+ * 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
+ * <code>null</code> 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 a request using the default context and processes the
+ * response using the given response handler.
+ *
+ * @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> T execute(
+ HttpUriRequest request,
+ ResponseHandler<? extends T> responseHandler)
+ throws IOException, ClientProtocolException
+ ;
+
+ /**
+ * Executes a request using the given context and processes the
+ * response using the given response handler.
+ *
+ * @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> T execute(
+ HttpUriRequest request,
+ ResponseHandler<? extends T> responseHandler,
+ HttpContext context)
+ throws IOException, ClientProtocolException
+ ;
+
+ /**
+ * Executes a request to the target using the default context and
+ * processes the response using the given response handler.
+ *
+ * @param target the target host for the request.
+ * Implementations may accept <code>null</code>
+ * 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> T execute(
+ HttpHost target,
+ HttpRequest request,
+ ResponseHandler<? extends T> responseHandler)
+ throws IOException, ClientProtocolException
+ ;
+
+ /**
+ * Executes a request to the target using the given context and
+ * processes the response using the given response handler.
+ *
+ * @param target the target host for the request.
+ * Implementations may accept <code>null</code>
+ * 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
+ * <code>null</code> 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> T execute(
+ HttpHost target,
+ HttpRequest request,
+ ResponseHandler<? extends T> responseHandler,
+ HttpContext context)
+ throws IOException, ClientProtocolException
+ ;
+
+} // interface HttpClient
diff --git a/src/org/apache/http/client/HttpRequestRetryHandler.java b/src/org/apache/http/client/HttpRequestRetryHandler.java
new file mode 100644
index 0000000..9ef8ef9
--- /dev/null
+++ b/src/org/apache/http/client/HttpRequestRetryHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/HttpRequestRetryHandler.java $
+ * $Revision: 535610 $
+ * $Date: 2007-05-06 06:28:13 -0700 (Sun, 06 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * A handler for determining if an HttpRequest should be retried after a
+ * recoverable exception during execution.
+ *
+ * <p>
+ * Classes implementing this interface must synchronize access to shared
+ * data as methods of this interfrace may be executed from multiple threads
+ * </p>
+ *
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+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 <code>true</code> if the method should be retried, <code>false</code>
+ * otherwise
+ */
+ boolean retryRequest(IOException exception, int executionCount, HttpContext context);
+
+}
diff --git a/src/org/apache/http/client/HttpResponseException.java b/src/org/apache/http/client/HttpResponseException.java
new file mode 100644
index 0000000..4d8de91
--- /dev/null
+++ b/src/org/apache/http/client/HttpResponseException.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/HttpResponseException.java $
+ * $Revision: 672425 $
+ * $Date: 2008-06-27 16:33:05 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+/**
+ * Signals a non 2xx HTTP response.
+ */
+public class HttpResponseException extends ClientProtocolException {
+
+ private static final long serialVersionUID = -7186627969477257933L;
+
+ private final int statusCode;
+
+ public HttpResponseException(int statusCode, final String s) {
+ super(s);
+ this.statusCode = statusCode;
+ }
+
+ public int getStatusCode() {
+ return this.statusCode;
+ }
+
+}
diff --git a/src/org/apache/http/client/NonRepeatableRequestException.java b/src/org/apache/http/client/NonRepeatableRequestException.java
new file mode 100644
index 0000000..13ff4d1
--- /dev/null
+++ b/src/org/apache/http/client/NonRepeatableRequestException.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/NonRepeatableRequestException.java $
+ * $Revision: 664326 $
+ * $Date: 2008-06-07 04:48:27 -0700 (Sat, 07 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals failure to retry the request due to non-repeatable request
+ * entity.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class NonRepeatableRequestException extends ProtocolException {
+
+ private static final long serialVersionUID = 82685265288806048L;
+
+ /**
+ * Creates a new NonRepeatableEntityException with a <tt>null</tt> detail message.
+ */
+ public NonRepeatableRequestException() {
+ super();
+ }
+
+ /**
+ * Creates a new NonRepeatableEntityException with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public NonRepeatableRequestException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/org/apache/http/client/RedirectException.java b/src/org/apache/http/client/RedirectException.java
new file mode 100644
index 0000000..82ea9ea
--- /dev/null
+++ b/src/org/apache/http/client/RedirectException.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/RedirectException.java $
+ * $Revision: 664066 $
+ * $Date: 2008-06-06 11:13:18 -0700 (Fri, 06 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals violation of HTTP specification caused by an invalid redirect
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class RedirectException extends ProtocolException {
+
+ private static final long serialVersionUID = 4418824536372559326L;
+
+ /**
+ * Creates a new RedirectException with a <tt>null</tt> detail message.
+ */
+ public RedirectException() {
+ super();
+ }
+
+ /**
+ * Creates a new RedirectException with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public RedirectException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new RedirectException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public RedirectException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/client/RedirectHandler.java b/src/org/apache/http/client/RedirectHandler.java
new file mode 100644
index 0000000..a98b4ae
--- /dev/null
+++ b/src/org/apache/http/client/RedirectHandler.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/RedirectHandler.java $
+ * $Revision: 538647 $
+ * $Date: 2007-05-16 09:41:42 -0700 (Wed, 16 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.net.URI;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolException;
+import org.apache.http.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.
+ *
+ * <p>
+ * Classes implementing this interface must synchronize access to shared
+ * data as methods of this interfrace may be executed from multiple threads
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+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 <code>true</code> if the request should be redirected, <code>false</code>
+ * 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/src/org/apache/http/client/RequestDirector.java b/src/org/apache/http/client/RequestDirector.java
new file mode 100644
index 0000000..924c312
--- /dev/null
+++ b/src/org/apache/http/client/RequestDirector.java
@@ -0,0 +1,92 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/RequestDirector.java $
+ * $Revision: 676020 $
+ * $Date: 2008-07-11 09:38:49 -0700 (Fri, 11 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpException;
+import org.apache.http.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.
+ *
+ * <br/><b>Note:</b>
+ * It is most likely that implementations of this interface will
+ * allocate connections, and return responses that depend on those
+ * connections for reading the response entity. Such connections
+ * MUST be released, but that is out of the scope of a request director.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 676020 $
+ *
+ * @since 4.0
+ */
+public interface RequestDirector {
+
+
+ /**
+ * Executes a request.
+ * <br/><b>Note:</b>
+ * For the time being, a new director is instantiated for each request.
+ * This is the same behavior as for <code>HttpMethodDirector</code>
+ * in HttpClient 3.
+ *
+ * @param target the target host for the request.
+ * Implementations may accept <code>null</code>
+ * 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
+ ;
+
+} // class ClientRequestDirector
diff --git a/src/org/apache/http/client/ResponseHandler.java b/src/org/apache/http/client/ResponseHandler.java
new file mode 100644
index 0000000..33a3391
--- /dev/null
+++ b/src/org/apache/http/client/ResponseHandler.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/ResponseHandler.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpResponse;
+
+/**
+ * Handler that encapsulates the process of generating a response object
+ * from a {@link HttpResponse}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface ResponseHandler<T> {
+
+ /**
+ * 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/src/org/apache/http/client/UserTokenHandler.java b/src/org/apache/http/client/UserTokenHandler.java
new file mode 100644
index 0000000..f8e55d8
--- /dev/null
+++ b/src/org/apache/http/client/UserTokenHandler.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/UserTokenHandler.java $
+ * $Revision: 658759 $
+ * $Date: 2008-05-21 10:06:17 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.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
+ * <code>null</code> if the context does not contain any resources or details
+ * specific to the current user.
+ * <p/>
+ * The user token will be used to ensure that user specific resouces will not
+ * shared with or reused by other users.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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
+ * <code>null</code> if it is not.
+ *
+ * @param context the execution context
+ *
+ * @return user token that uniquely identifies the user or
+ * <code>null</null> if the context is not user specific.
+ */
+ Object getUserToken(HttpContext context);
+
+}
diff --git a/src/org/apache/http/client/entity/UrlEncodedFormEntity.java b/src/org/apache/http/client/entity/UrlEncodedFormEntity.java
new file mode 100644
index 0000000..89b9c45
--- /dev/null
+++ b/src/org/apache/http/client/entity/UrlEncodedFormEntity.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/entity/UrlEncodedFormEntity.java $
+ * $Revision: 655107 $
+ * $Date: 2008-05-10 08:20:42 -0700 (Sat, 10 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.entity;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * An entity composed of a list of url-encoded pairs.
+ * This is typically useful while sending an HTTP POST request.
+ */
+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 encoding encoding the name/value pairs be encoded with
+ * @throws UnsupportedEncodingException if the encoding isn't supported
+ */
+ public UrlEncodedFormEntity (
+ final List <? extends NameValuePair> parameters,
+ final String encoding) throws UnsupportedEncodingException {
+ super(URLEncodedUtils.format(parameters, encoding),
+ encoding);
+ setContentType(URLEncodedUtils.CONTENT_TYPE);
+ }
+
+ /**
+ * 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 <? extends NameValuePair> parameters) throws UnsupportedEncodingException {
+ super(URLEncodedUtils.format(parameters, HTTP.DEFAULT_CONTENT_CHARSET),
+ HTTP.DEFAULT_CONTENT_CHARSET);
+ setContentType(URLEncodedUtils.CONTENT_TYPE);
+ }
+
+}
diff --git a/src/org/apache/http/client/methods/AbortableHttpRequest.java b/src/org/apache/http/client/methods/AbortableHttpRequest.java
new file mode 100644
index 0000000..c402609
--- /dev/null
+++ b/src/org/apache/http/client/methods/AbortableHttpRequest.java
@@ -0,0 +1,91 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/AbortableHttpRequest.java $
+ * $Revision: 639600 $
+ * $Date: 2008-03-21 04:28:15 -0700 (Fri, 21 Mar 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.io.IOException;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+
+/**
+ * Interface representing an HTTP request that can be aborted by shutting
+ * down the underlying HTTP connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 639600 $
+ *
+ * @since 4.0
+ */
+public interface AbortableHttpRequest {
+
+ /**
+ * Sets the {@link 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 ClientConnectionManager
+ * @see ThreadSafeClientConnManager
+ */
+ 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 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 HttpClient#execute(HttpUriRequest)
+ * @see HttpClient#execute(org.apache.http.HttpHost,
+ * org.apache.http.HttpRequest)
+ * @see HttpClient#execute(HttpUriRequest,
+ * org.apache.http.protocol.HttpContext)
+ * @see HttpClient#execute(org.apache.http.HttpHost,
+ * org.apache.http.HttpRequest, org.apache.http.protocol.HttpContext)
+ */
+ void abort();
+
+}
+
diff --git a/src/org/apache/http/client/methods/HttpDelete.java b/src/org/apache/http/client/methods/HttpDelete.java
new file mode 100644
index 0000000..4a0fb77
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpDelete.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpDelete.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP DELETE method
+ * <p>
+ * The HTTP DELETE method is defined in section 9.7 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * 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.
+ * </blockquote>
+ */
+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/src/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java b/src/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java
new file mode 100644
index 0000000..8ac6f01
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpEntityEnclosingRequestBase.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.utils.CloneUtils;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Basic implementation of an HTTP request that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ *
+ * @since 4.0
+ */
+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() {
+ Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
+ return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ HttpEntityEnclosingRequestBase clone =
+ (HttpEntityEnclosingRequestBase) super.clone();
+ if (this.entity != null) {
+ clone.entity = (HttpEntity) CloneUtils.clone(this.entity);
+ }
+ return clone;
+ }
+
+}
diff --git a/src/org/apache/http/client/methods/HttpGet.java b/src/org/apache/http/client/methods/HttpGet.java
new file mode 100644
index 0000000..2908f1d
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpGet.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpGet.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP GET method.
+ * <p>
+ * The HTTP GET method is defined in section 9.3 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * 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.
+ * </blockquote>
+ * </p>
+ * <p>
+ * GetMethods will follow redirect requests from the http server by default.
+ * This behavour can be disabled by calling setFollowRedirects(false).</p>
+ *
+ * @version $Revision: 664505 $
+ *
+ * @since 4.0
+ */
+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/src/org/apache/http/client/methods/HttpHead.java b/src/org/apache/http/client/methods/HttpHead.java
new file mode 100644
index 0000000..29e58a3
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpHead.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpHead.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP HEAD method.
+ * <p>
+ * The HTTP HEAD method is defined in section 9.4 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * 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.
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ *
+ * @since 4.0
+ */
+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/src/org/apache/http/client/methods/HttpOptions.java b/src/org/apache/http/client/methods/HttpOptions.java
new file mode 100644
index 0000000..3758360
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpOptions.java
@@ -0,0 +1,105 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpOptions.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpResponse;
+
+/**
+ * HTTP OPTIONS method.
+ * <p>
+ * The HTTP OPTIONS method is defined in section 9.2 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * 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.
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ *
+ * @since 4.0
+ */
+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<String> getAllowedMethods(final HttpResponse response) {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+
+ HeaderIterator it = response.headerIterator("Allow");
+ Set<String> methods = new HashSet<String>();
+ while (it.hasNext()) {
+ Header header = it.nextHeader();
+ HeaderElement[] elements = header.getElements();
+ for (HeaderElement element : elements) {
+ methods.add(element.getName());
+ }
+ }
+ return methods;
+ }
+
+}
diff --git a/src/org/apache/http/client/methods/HttpPost.java b/src/org/apache/http/client/methods/HttpPost.java
new file mode 100644
index 0000000..bc58803
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpPost.java
@@ -0,0 +1,87 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpPost.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP POST method.
+ * <p>
+ * The HTTP POST method is defined in section 9.5 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * 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:
+ * <ul>
+ * <li>Annotation of existing resources</li>
+ * <li>Posting a message to a bulletin board, newsgroup, mailing list, or
+ * similar group of articles</li>
+ * <li>Providing a block of data, such as the result of submitting a form,
+ * to a data-handling process</li>
+ * <li>Extending a database through an append operation</li>
+ * </ul>
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ *
+ * @since 4.0
+ */
+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/src/org/apache/http/client/methods/HttpPut.java b/src/org/apache/http/client/methods/HttpPut.java
new file mode 100644
index 0000000..5b50135
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpPut.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpPut.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP PUT method.
+ * <p>
+ * The HTTP PUT method is defined in section 9.6 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * 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.
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ *
+ * @since 4.0
+ */
+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/src/org/apache/http/client/methods/HttpRequestBase.java b/src/org/apache/http/client/methods/HttpRequestBase.java
new file mode 100644
index 0000000..8938ea0
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpRequestBase.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpRequestBase.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.client.utils.CloneUtils;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.message.AbstractHttpMessage;
+import org.apache.http.message.BasicRequestLine;
+import org.apache.http.message.HeaderGroup;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * Basic implementation of an HTTP request that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ *
+ * @since 4.0
+ */
+public abstract class HttpRequestBase extends AbstractHttpMessage
+ implements HttpUriRequest, AbortableHttpRequest, Cloneable {
+
+ private Lock abortLock;
+
+ private boolean aborted;
+
+ private URI uri;
+ private ClientConnectionRequest connRequest;
+ private ConnectionReleaseTrigger releaseTrigger;
+
+ public HttpRequestBase() {
+ super();
+ this.abortLock = new ReentrantLock();
+ }
+
+ public abstract String getMethod();
+
+ public ProtocolVersion getProtocolVersion() {
+ return HttpProtocolParams.getVersion(getParams());
+ }
+
+ public URI getURI() {
+ return this.uri;
+ }
+
+ public RequestLine getRequestLine() {
+ String method = getMethod();
+ ProtocolVersion ver = getProtocolVersion();
+ 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 void setURI(final URI uri) {
+ this.uri = uri;
+ }
+
+ public void setConnectionRequest(final ClientConnectionRequest connRequest)
+ throws IOException {
+ this.abortLock.lock();
+ try {
+ if (this.aborted) {
+ throw new IOException("Request already aborted");
+ }
+
+ this.releaseTrigger = null;
+ this.connRequest = connRequest;
+ } finally {
+ this.abortLock.unlock();
+ }
+ }
+
+ public void setReleaseTrigger(final ConnectionReleaseTrigger releaseTrigger)
+ throws IOException {
+ this.abortLock.lock();
+ try {
+ if (this.aborted) {
+ throw new IOException("Request already aborted");
+ }
+
+ this.connRequest = null;
+ this.releaseTrigger = releaseTrigger;
+ } finally {
+ this.abortLock.unlock();
+ }
+ }
+
+ public void abort() {
+ ClientConnectionRequest localRequest;
+ ConnectionReleaseTrigger localTrigger;
+
+ this.abortLock.lock();
+ try {
+ if (this.aborted) {
+ return;
+ }
+ this.aborted = true;
+
+ localRequest = connRequest;
+ localTrigger = releaseTrigger;
+ } finally {
+ this.abortLock.unlock();
+ }
+
+ // Trigger the callbacks outside of the lock, to prevent
+ // deadlocks in the scenario where the callbacks have
+ // their own locks that may be used while calling
+ // setReleaseTrigger or setConnectionRequest.
+ if (localRequest != null) {
+ localRequest.abortRequest();
+ }
+ if (localTrigger != null) {
+ try {
+ localTrigger.abortConnection();
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+ }
+
+ public boolean isAborted() {
+ return this.aborted;
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ HttpRequestBase clone = (HttpRequestBase) super.clone();
+ clone.abortLock = new ReentrantLock();
+ clone.aborted = false;
+ clone.releaseTrigger = null;
+ clone.connRequest = null;
+ clone.headergroup = (HeaderGroup) CloneUtils.clone(this.headergroup);
+ clone.params = (HttpParams) CloneUtils.clone(this.params);
+ return clone;
+ }
+
+}
diff --git a/src/org/apache/http/client/methods/HttpTrace.java b/src/org/apache/http/client/methods/HttpTrace.java
new file mode 100644
index 0000000..94f18ff
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpTrace.java
@@ -0,0 +1,82 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpTrace.java $
+ * $Revision: 664505 $
+ * $Date: 2008-06-08 06:21:20 -0700 (Sun, 08 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+/**
+ * HTTP TRACE method.
+ * <p>
+ * The HTTP TRACE method is defined in section 9.6 of
+ * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * 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.
+ * </blockquote>
+ * </p>
+ *
+ * @version $Revision: 664505 $
+ *
+ * @since 4.0
+ */
+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/src/org/apache/http/client/methods/HttpUriRequest.java b/src/org/apache/http/client/methods/HttpUriRequest.java
new file mode 100644
index 0000000..56d064a
--- /dev/null
+++ b/src/org/apache/http/client/methods/HttpUriRequest.java
@@ -0,0 +1,80 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/HttpUriRequest.java $
+ * $Revision: 659191 $
+ * $Date: 2008-05-22 11:26:53 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+import org.apache.http.HttpRequest;
+
+/**
+ * Extended version of the {@link HttpRequest} interface that provides
+ * convenience methods to access request properties such as request URI
+ * and method type.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 659191 $
+ *
+ * @since 4.0
+ */
+public interface HttpUriRequest extends HttpRequest {
+
+ /**
+ * Returns the HTTP method this request uses, such as <code>GET</code>,
+ * <code>PUT</code>, <code>POST</code>, or other.
+ */
+ String getMethod();
+
+ /**
+ * Returns the URI this request uses, such as
+ * <code>http://example.org/path/to/file</code>.
+ */
+ 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 <code>true</code> if the request execution has been aborted,
+ * <code>false</code> otherwise.
+ */
+ boolean isAborted();
+
+}
diff --git a/src/org/apache/http/client/methods/package.html b/src/org/apache/http/client/methods/package.html
new file mode 100644
index 0000000..c2a602a
--- /dev/null
+++ b/src/org/apache/http/client/methods/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/methods/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Request implementations for the various HTTP methods like GET and POST.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/package.html b/src/org/apache/http/client/package.html
new file mode 100644
index 0000000..64c7287
--- /dev/null
+++ b/src/org/apache/http/client/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The API for client-side HTTP communication and
+entry point to the <i>HttpClient</i> module.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/params/AllClientPNames.java b/src/org/apache/http/client/params/AllClientPNames.java
new file mode 100644
index 0000000..e55bca7
--- /dev/null
+++ b/src/org/apache/http/client/params/AllClientPNames.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/AllClientPNames.java $
+ * $Revision: 576078 $
+ * $Date: 2007-09-16 04:50:41 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.auth.params.AuthPNames;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.conn.params.ConnManagerPNames;
+import org.apache.http.conn.params.ConnConnectionPNames;
+import org.apache.http.conn.params.ConnRoutePNames;
+
+
+/**
+ * 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.
+ * <br/>
+ * 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.
+ *
+ * @version $Revision: 576078 $
+ *
+ * @since 4.0
+ */
+public interface AllClientPNames extends
+ CoreConnectionPNames, CoreProtocolPNames,
+ ClientPNames, AuthPNames, CookieSpecPNames,
+ ConnConnectionPNames, ConnManagerPNames, ConnRoutePNames {
+
+ // no additional definitions
+}
+
diff --git a/src/org/apache/http/client/params/AuthPolicy.java b/src/org/apache/http/client/params/AuthPolicy.java
new file mode 100644
index 0000000..5bcdd38
--- /dev/null
+++ b/src/org/apache/http/client/params/AuthPolicy.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/AuthPolicy.java $
+ * $Revision: 534839 $
+ * $Date: 2007-05-03 06:03:41 -0700 (Thu, 03 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+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";
+
+}
diff --git a/src/org/apache/http/client/params/ClientPNames.java b/src/org/apache/http/client/params/ClientPNames.java
new file mode 100644
index 0000000..f98eeb7
--- /dev/null
+++ b/src/org/apache/http/client/params/ClientPNames.java
@@ -0,0 +1,139 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/ClientPNames.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+
+/**
+ * Parameter names for the HttpClient module.
+ * This does not include parameters for informational units
+ * HttpAuth, HttpCookie, or HttpConn.
+ *
+ * @version $Revision: 659595 $
+ *
+ * @since 4.0
+ */
+public interface ClientPNames {
+
+ /**
+ * Defines the class name of the default {@link org.apache.http.conn.ClientConnectionManager}
+ * <p>
+ * This parameter expects a value of type {@link String}.
+ * </p>
+ */
+ public static final String CONNECTION_MANAGER_FACTORY_CLASS_NAME = "http.connection-manager.factory-class-name";
+
+ /**
+ * Defines the factory to create a default {@link org.apache.http.conn.ClientConnectionManager}.
+ * <p>
+ * This parameters expects a value of type {@link org.apache.http.conn.ClientConnectionManagerFactory}.
+ * </p>
+ */
+ public static final String CONNECTION_MANAGER_FACTORY = "http.connection-manager.factory-object";
+
+ /**
+ * Defines whether redirects should be handled automatically
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ public static final String HANDLE_REDIRECTS = "http.protocol.handle-redirects";
+
+ /**
+ * Defines whether relative redirects should be rejected.
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ 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.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ */
+ 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
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ public static final String ALLOW_CIRCULAR_REDIRECTS = "http.protocol.allow-circular-redirects";
+
+ /**
+ * Defines whether authentication should be handled automatically.
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ public static final String HANDLE_AUTHENTICATION = "http.protocol.handle-authentication";
+
+ /**
+ * Defines the name of the cookie specification to be used for HTTP state management.
+ * <p>
+ * This parameter expects a value of type {@link String}.
+ * </p>
+ */
+ public static final String COOKIE_POLICY = "http.protocol.cookie-policy";
+
+ /**
+ * Defines the virtual host name.
+ * <p>
+ * This parameter expects a value of type {@link org.apache.http.HttpHost}.
+ * </p>
+ */
+ public static final String VIRTUAL_HOST = "http.virtual-host";
+
+ /**
+ * Defines the request headers to be sent per default with each request.
+ * <p>
+ * This parameter expects a value of type {@link java.util.Collection}. The
+ * collection is expected to contain {@link org.apache.http.Header}s.
+ * </p>
+ */
+ 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.
+ * <p>
+ * This parameter expects a value of type {@link org.apache.http.HttpHost}.
+ * </p>
+ */
+ public static final String DEFAULT_HOST = "http.default-host";
+
+}
+
diff --git a/src/org/apache/http/client/params/ClientParamBean.java b/src/org/apache/http/client/params/ClientParamBean.java
new file mode 100644
index 0000000..76431a7
--- /dev/null
+++ b/src/org/apache/http/client/params/ClientParamBean.java
@@ -0,0 +1,92 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/ClientParamBean.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+import java.util.Collection;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.conn.ClientConnectionManagerFactory;
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+public class ClientParamBean extends HttpAbstractParamBean {
+
+ public ClientParamBean (final HttpParams params) {
+ super(params);
+ }
+
+ public void setConnectionManagerFactoryClassName (final String factory) {
+ params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME, factory);
+ }
+
+ public void setConnectionManagerFactory(ClientConnectionManagerFactory factory) {
+ params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY, 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 <Header> headers) {
+ params.setParameter(ClientPNames.DEFAULT_HEADERS, headers);
+ }
+
+ public void setDefaultHost (final HttpHost host) {
+ params.setParameter(ClientPNames.DEFAULT_HOST, host);
+ }
+
+}
diff --git a/src/org/apache/http/client/params/CookiePolicy.java b/src/org/apache/http/client/params/CookiePolicy.java
new file mode 100644
index 0000000..04a131d
--- /dev/null
+++ b/src/org/apache/http/client/params/CookiePolicy.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/CookiePolicy.java $
+ * $Revision: 613865 $
+ * $Date: 2008-01-21 04:04:30 -0800 (Mon, 21 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+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";
+
+ private CookiePolicy() {
+ super();
+ }
+
+}
diff --git a/src/org/apache/http/client/params/HttpClientParams.java b/src/org/apache/http/client/params/HttpClientParams.java
new file mode 100644
index 0000000..c21e2b0
--- /dev/null
+++ b/src/org/apache/http/client/params/HttpClientParams.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/HttpClientParams.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.params;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * An adaptor for accessing HTTP client parameters in {@link HttpParams}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 659595 $
+ *
+ * @since 4.0
+ */
+public class HttpClientParams {
+
+ private HttpClientParams() {
+ super();
+ }
+
+ public static boolean isRedirecting(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getBooleanParameter
+ (ClientPNames.HANDLE_REDIRECTS, true);
+ }
+
+ public static void setRedirecting(final HttpParams params, boolean value) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setBooleanParameter
+ (ClientPNames.HANDLE_REDIRECTS, value);
+ }
+
+ public static boolean isAuthenticating(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getBooleanParameter
+ (ClientPNames.HANDLE_AUTHENTICATION, true);
+ }
+
+ public static void setAuthenticating(final HttpParams params, boolean value) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setBooleanParameter
+ (ClientPNames.HANDLE_AUTHENTICATION, value);
+ }
+
+ public static String getCookiePolicy(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ 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) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setParameter(ClientPNames.COOKIE_POLICY, cookiePolicy);
+ }
+
+}
diff --git a/src/org/apache/http/client/params/package.html b/src/org/apache/http/client/params/package.html
new file mode 100644
index 0000000..b66cdfb
--- /dev/null
+++ b/src/org/apache/http/client/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpClient</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/protocol/ClientContext.java b/src/org/apache/http/client/protocol/ClientContext.java
new file mode 100644
index 0000000..1859f9e
--- /dev/null
+++ b/src/org/apache/http/client/protocol/ClientContext.java
@@ -0,0 +1,52 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/ClientContext.java $
+ * $Revision: 658759 $
+ * $Date: 2008-05-21 10:06:17 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+
+/**
+ * {@link org.apache.http.protocol.HttpContext Context}
+ * attribute names for client.
+ */
+public interface ClientContext {
+
+ public static final String COOKIESPEC_REGISTRY = "http.cookiespec-registry";
+ public static final String AUTHSCHEME_REGISTRY = "http.authscheme-registry";
+ public static final String COOKIE_STORE = "http.cookie-store";
+ public static final String COOKIE_SPEC = "http.cookie-spec";
+ public static final String COOKIE_ORIGIN = "http.cookie-origin";
+ public static final String CREDS_PROVIDER = "http.auth.credentials-provider";
+ public static final String TARGET_AUTH_STATE = "http.auth.target-scope";
+ public static final String PROXY_AUTH_STATE = "http.auth.proxy-scope";
+ public static final String AUTH_SCHEME_PREF = "http.auth.scheme-pref";
+ public static final String USER_TOKEN = "http.user-token";
+
+}
diff --git a/src/org/apache/http/client/protocol/ClientContextConfigurer.java b/src/org/apache/http/client/protocol/ClientContextConfigurer.java
new file mode 100644
index 0000000..f2ced63
--- /dev/null
+++ b/src/org/apache/http/client/protocol/ClientContextConfigurer.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/ClientContextConfigurer.java $
+ * $Revision: 654886 $
+ * $Date: 2008-05-09 10:06:12 -0700 (Fri, 09 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.util.List;
+
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.protocol.HttpContext;
+
+public class ClientContextConfigurer implements ClientContext {
+
+ private final HttpContext context;
+
+ public ClientContextConfigurer (final HttpContext context) {
+ if (context == null)
+ throw new IllegalArgumentException("HTTP context may not be null");
+ 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);
+ }
+
+ public void setAuthSchemePref(final List<String> list) {
+ this.context.setAttribute(AUTH_SCHEME_PREF, list);
+ }
+
+}
diff --git a/src/org/apache/http/client/protocol/RequestAddCookies.java b/src/org/apache/http/client/protocol/RequestAddCookies.java
new file mode 100644
index 0000000..0de8c40
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestAddCookies.java
@@ -0,0 +1,192 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestAddCookies.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.ProtocolException;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+
+/**
+ * Request interceptor that matches cookies available in the current
+ * {@link CookieStore} to the request being executed and generates
+ * corresponding cookierequest headers.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class RequestAddCookies implements HttpRequestInterceptor {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ public RequestAddCookies() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ // Obtain cookie store
+ CookieStore cookieStore = (CookieStore) context.getAttribute(
+ ClientContext.COOKIE_STORE);
+ if (cookieStore == null) {
+ this.log.info("Cookie store not available in HTTP context");
+ return;
+ }
+
+ // Obtain the registry of cookie specs
+ CookieSpecRegistry registry= (CookieSpecRegistry) context.getAttribute(
+ ClientContext.COOKIESPEC_REGISTRY);
+ if (registry == null) {
+ this.log.info("CookieSpec registry not available in HTTP context");
+ return;
+ }
+
+ // Obtain the target host (required)
+ HttpHost targetHost = (HttpHost) context.getAttribute(
+ ExecutionContext.HTTP_TARGET_HOST);
+ if (targetHost == null) {
+ throw new IllegalStateException("Target host not specified in HTTP context");
+ }
+
+ // Obtain the client connection (required)
+ ManagedClientConnection conn = (ManagedClientConnection) context.getAttribute(
+ ExecutionContext.HTTP_CONNECTION);
+ if (conn == null) {
+ throw new IllegalStateException("Client connection not specified in HTTP context");
+ }
+
+ String policy = HttpClientParams.getCookiePolicy(request.getParams());
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("CookieSpec selected: " + policy);
+ }
+
+ URI requestURI;
+ if (request instanceof HttpUriRequest) {
+ requestURI = ((HttpUriRequest) request).getURI();
+ } else {
+ try {
+ requestURI = new URI(request.getRequestLine().getUri());
+ } catch (URISyntaxException ex) {
+ throw new ProtocolException("Invalid request URI: " +
+ request.getRequestLine().getUri(), ex);
+ }
+ }
+
+ String hostName = targetHost.getHostName();
+ int port = targetHost.getPort();
+ if (port < 0) {
+ port = conn.getRemotePort();
+ }
+
+ CookieOrigin cookieOrigin = new CookieOrigin(
+ hostName,
+ port,
+ requestURI.getPath(),
+ conn.isSecure());
+
+ // Get an instance of the selected cookie policy
+ CookieSpec cookieSpec = registry.getCookieSpec(policy, request.getParams());
+ // Get all cookies available in the HTTP state
+ List<Cookie> cookies = new ArrayList<Cookie>(cookieStore.getCookies());
+ // Find cookies matching the given origin
+ List<Cookie> matchedCookies = new ArrayList<Cookie>();
+ for (Cookie cookie : cookies) {
+ if (cookieSpec.match(cookie, cookieOrigin)) {
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("Cookie " + cookie + " match " + cookieOrigin);
+ }
+ matchedCookies.add(cookie);
+ }
+ }
+ // Generate Cookie request headers
+ if (!matchedCookies.isEmpty()) {
+ List<Header> headers = cookieSpec.formatCookies(matchedCookies);
+ for (Header header : headers) {
+ request.addHeader(header);
+ }
+ }
+
+ int ver = cookieSpec.getVersion();
+ if (ver > 0) {
+ boolean needVersionHeader = false;
+ for (Cookie cookie : matchedCookies) {
+ if (ver != cookie.getVersion()) {
+ needVersionHeader = true;
+ }
+ }
+
+ if (needVersionHeader) {
+ 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(ClientContext.COOKIE_SPEC, cookieSpec);
+ context.setAttribute(ClientContext.COOKIE_ORIGIN, cookieOrigin);
+ }
+
+}
diff --git a/src/org/apache/http/client/protocol/RequestDefaultHeaders.java b/src/org/apache/http/client/protocol/RequestDefaultHeaders.java
new file mode 100644
index 0000000..27d5cc7
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestDefaultHeaders.java
@@ -0,0 +1,74 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestDefaultHeaders.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Request interceptor that adds default request headers.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 653041 $
+ *
+ * @since 4.0
+ */
+public class RequestDefaultHeaders implements HttpRequestInterceptor {
+
+ public RequestDefaultHeaders() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ // Add default headers
+ Collection<?> defHeaders = (Collection<?>) request.getParams().getParameter(
+ ClientPNames.DEFAULT_HEADERS);
+ if (defHeaders != null) {
+ for (Object defHeader : defHeaders) {
+ request.addHeader((Header) defHeader);
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/client/protocol/RequestProxyAuthentication.java b/src/org/apache/http/client/protocol/RequestProxyAuthentication.java
new file mode 100644
index 0000000..b4dfe76
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestProxyAuthentication.java
@@ -0,0 +1,104 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestProxyAuthentication.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class RequestProxyAuthentication implements HttpRequestInterceptor {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ public RequestProxyAuthentication() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ if (request.containsHeader(AUTH.PROXY_AUTH_RESP)) {
+ return;
+ }
+
+ // Obtain authentication state
+ AuthState authState = (AuthState) context.getAttribute(
+ ClientContext.PROXY_AUTH_STATE);
+ if (authState == null) {
+ return;
+ }
+
+ AuthScheme authScheme = authState.getAuthScheme();
+ if (authScheme == null) {
+ return;
+ }
+
+ Credentials creds = authState.getCredentials();
+ if (creds == null) {
+ this.log.debug("User credentials not available");
+ return;
+ }
+ if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
+ try {
+ request.addHeader(authScheme.authenticate(creds, request));
+ } catch (AuthenticationException ex) {
+ if (this.log.isErrorEnabled()) {
+ this.log.error("Proxy authentication error: " + ex.getMessage());
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/client/protocol/RequestTargetAuthentication.java b/src/org/apache/http/client/protocol/RequestTargetAuthentication.java
new file mode 100644
index 0000000..c140183
--- /dev/null
+++ b/src/org/apache/http/client/protocol/RequestTargetAuthentication.java
@@ -0,0 +1,105 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/RequestTargetAuthentication.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class RequestTargetAuthentication implements HttpRequestInterceptor {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ public RequestTargetAuthentication() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ if (request.containsHeader(AUTH.WWW_AUTH_RESP)) {
+ return;
+ }
+
+ // Obtain authentication state
+ AuthState authState = (AuthState) context.getAttribute(
+ ClientContext.TARGET_AUTH_STATE);
+ if (authState == null) {
+ return;
+ }
+
+ AuthScheme authScheme = authState.getAuthScheme();
+ if (authScheme == null) {
+ return;
+ }
+
+ Credentials creds = authState.getCredentials();
+ if (creds == null) {
+ this.log.debug("User credentials not available");
+ return;
+ }
+
+ if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
+ try {
+ request.addHeader(authScheme.authenticate(creds, request));
+ } catch (AuthenticationException ex) {
+ if (this.log.isErrorEnabled()) {
+ this.log.error("Authentication error: " + ex.getMessage());
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/client/protocol/ResponseProcessCookies.java b/src/org/apache/http/client/protocol/ResponseProcessCookies.java
new file mode 100644
index 0000000..0689e93
--- /dev/null
+++ b/src/org/apache/http/client/protocol/ResponseProcessCookies.java
@@ -0,0 +1,146 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/ResponseProcessCookies.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.client.CookieStore;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Response interceptor that populates the current {@link CookieStore} with data
+ * contained in response cookies received in the given the HTTP response.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class ResponseProcessCookies implements HttpResponseInterceptor {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ public ResponseProcessCookies() {
+ super();
+ }
+
+ public void process(final HttpResponse response, final HttpContext context)
+ throws HttpException, IOException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ // Obtain cookie store
+ CookieStore cookieStore = (CookieStore) context.getAttribute(
+ ClientContext.COOKIE_STORE);
+ if (cookieStore == null) {
+ this.log.info("Cookie store not available in HTTP context");
+ return;
+ }
+ // Obtain actual CookieSpec instance
+ CookieSpec cookieSpec = (CookieSpec) context.getAttribute(
+ ClientContext.COOKIE_SPEC);
+ if (cookieSpec == null) {
+ this.log.info("CookieSpec not available in HTTP context");
+ return;
+ }
+ // Obtain actual CookieOrigin instance
+ CookieOrigin cookieOrigin = (CookieOrigin) context.getAttribute(
+ ClientContext.COOKIE_ORIGIN);
+ if (cookieOrigin == null) {
+ this.log.info("CookieOrigin not available 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()) {
+ Header header = iterator.nextHeader();
+ try {
+ List<Cookie> cookies = cookieSpec.parse(header, cookieOrigin);
+ for (Cookie cookie : cookies) {
+ try {
+ cookieSpec.validate(cookie, cookieOrigin);
+ cookieStore.addCookie(cookie);
+
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("Cookie accepted: \""
+ + cookie + "\". ");
+ }
+ } catch (MalformedCookieException ex) {
+ if (this.log.isWarnEnabled()) {
+ this.log.warn("Cookie rejected: \""
+ + cookie + "\". " + ex.getMessage());
+ }
+ }
+ }
+ } catch (MalformedCookieException ex) {
+ if (this.log.isWarnEnabled()) {
+ this.log.warn("Invalid cookie header: \""
+ + header + "\". " + ex.getMessage());
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/client/protocol/package.html b/src/org/apache/http/client/protocol/package.html
new file mode 100644
index 0000000..43dd0d6
--- /dev/null
+++ b/src/org/apache/http/client/protocol/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/protocol/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Additional request and response interceptors.
+
+</body>
+</html>
diff --git a/src/org/apache/http/client/utils/CloneUtils.java b/src/org/apache/http/client/utils/CloneUtils.java
new file mode 100644
index 0000000..fec534b
--- /dev/null
+++ b/src/org/apache/http/client/utils/CloneUtils.java
@@ -0,0 +1,75 @@
+/*
+ * $HeadURL:$
+ * $Revision:$
+ * $Date:$
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.client.utils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * A collection of utilities to workaround limitations of Java clone framework.
+ */
+public class CloneUtils {
+
+ public static Object clone(final Object obj) throws CloneNotSupportedException {
+ if (obj == null) {
+ return null;
+ }
+ if (obj instanceof Cloneable) {
+ Class<?> clazz = obj.getClass ();
+ Method m;
+ try {
+ m = clazz.getMethod("clone", (Class[]) null);
+ } catch (NoSuchMethodException ex) {
+ throw new NoSuchMethodError(ex.getMessage());
+ }
+ try {
+ return m.invoke(obj, (Object []) null);
+ } catch (InvocationTargetException ex) {
+ Throwable cause = ex.getCause();
+ if (cause instanceof CloneNotSupportedException) {
+ throw ((CloneNotSupportedException) cause);
+ } else {
+ throw new Error("Unexpected exception", cause);
+ }
+ } catch (IllegalAccessException ex) {
+ throw new IllegalAccessError(ex.getMessage());
+ }
+ } else {
+ throw new CloneNotSupportedException();
+ }
+ }
+
+ /**
+ * This class should not be instantiated.
+ */
+ private CloneUtils() {
+ }
+
+}
diff --git a/src/org/apache/http/client/utils/URIUtils.java b/src/org/apache/http/client/utils/URIUtils.java
new file mode 100644
index 0000000..1cbb9af
--- /dev/null
+++ b/src/org/apache/http/client/utils/URIUtils.java
@@ -0,0 +1,209 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/utils/URIUtils.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.client.utils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.http.HttpHost;
+
+/**
+ * A collection of utilities for {@link URI URIs}, to workaround
+ * bugs within the class or for ease-of-use features.
+ */
+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.
+ *
+ * See <a
+ * href="https://issues.apache.org/jira/browse/HTTPCLIENT-730">HTTPCLIENT-730</a>
+ * for more information.
+ *
+ * @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&nbsp;2396, or if the authority
+ * component of the string is present but cannot be parsed
+ * as a server-based authority
+ */
+ public static URI createURI(
+ final String scheme,
+ final String host,
+ int port,
+ final String path,
+ final String query,
+ final String fragment) throws URISyntaxException {
+
+ 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.
+ *
+ * @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,
+ boolean dropFragment) throws URISyntaxException {
+ if (uri == null) {
+ throw new IllegalArgumentException("URI may nor be null");
+ }
+ if (target != null) {
+ return URIUtils.createURI(
+ target.getSchemeName(),
+ target.getHostName(),
+ target.getPort(),
+ uri.getRawPath(),
+ uri.getRawQuery(),
+ dropFragment ? null : uri.getRawFragment());
+ } else {
+ return URIUtils.createURI(
+ null,
+ null,
+ -1,
+ uri.getRawPath(),
+ uri.getRawQuery(),
+ dropFragment ? null : uri.getRawFragment());
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Resolves a URI reference against a base URI. Work-around for bug in
+ * java.net.URI (<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
+ *
+ * @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 bug in
+ * java.net.URI (<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
+ *
+ * @param baseURI the base URI
+ * @param reference the URI reference
+ * @return the resulting URI
+ */
+ public static URI resolve(final URI baseURI, URI reference){
+ if (baseURI == null) {
+ throw new IllegalArgumentException("Base URI may nor be null");
+ }
+ if (reference == null) {
+ throw new IllegalArgumentException("Reference URI may nor be null");
+ }
+ boolean emptyReference = reference.toString().length() == 0;
+ if (emptyReference) {
+ reference = URI.create("#");
+ }
+ URI resolved = baseURI.resolve(reference);
+ if (emptyReference) {
+ String resolvedString = resolved.toString();
+ resolved = URI.create(resolvedString.substring(0,
+ resolvedString.indexOf('#')));
+ }
+ return resolved;
+ }
+
+ /**
+ * This class should not be instantiated.
+ */
+ private URIUtils() {
+ }
+
+}
diff --git a/src/org/apache/http/client/utils/URLEncodedUtils.java b/src/org/apache/http/client/utils/URLEncodedUtils.java
new file mode 100644
index 0000000..8b08f90
--- /dev/null
+++ b/src/org/apache/http/client/utils/URLEncodedUtils.java
@@ -0,0 +1,191 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java $
+ * $Revision: 655107 $
+ * $Date: 2008-05-10 08:20:42 -0700 (Sat, 10 May 2008) $
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.utils;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Scanner;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * A collection of utilities for encoding URLs.
+ */
+public class URLEncodedUtils {
+
+ public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
+ private static final String PARAMETER_SEPARATOR = "&";
+ 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.
+ * <p>
+ * This is typically useful while parsing an HTTP PUT.
+ *
+ * @param uri
+ * uri to parse
+ * @param encoding
+ * encoding to use while parsing the query
+ */
+ public static List <NameValuePair> parse (final URI uri, final String encoding) {
+ List <NameValuePair> result = Collections.emptyList();
+ final String query = uri.getRawQuery();
+ if (query != null && query.length() > 0) {
+ result = new ArrayList <NameValuePair>();
+ parse(result, new Scanner(query), encoding);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an
+ * {@link HttpEntity}. The encoding is taken from the entity's
+ * Content-Encoding header.
+ * <p>
+ * This is typically used while parsing an HTTP POST.
+ *
+ * @param entity
+ * The entity to parse
+ * @throws IOException
+ * If there was an exception getting the entity's data.
+ */
+ public static List <NameValuePair> parse (
+ final HttpEntity entity) throws IOException {
+ List <NameValuePair> result = Collections.emptyList();
+ if (isEncoded(entity)) {
+ final String content = EntityUtils.toString(entity);
+ final Header encoding = entity.getContentEncoding();
+ if (content != null && content.length() > 0) {
+ result = new ArrayList <NameValuePair>();
+ parse(result, new Scanner(content),
+ encoding != null ? encoding.getValue() : null);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if the entity's Content-Type header is
+ * <code>application/x-www-form-urlencoded</code>.
+ */
+ public static boolean isEncoded (final HttpEntity entity) {
+ final Header contentType = entity.getContentType();
+ return (contentType != null && contentType.getValue().equalsIgnoreCase(CONTENT_TYPE));
+ }
+
+ /**
+ * Adds all parameters within the Scanner to the list of
+ * <code>parameters</code>, as encoded by <code>encoding</code>. For
+ * example, a scanner containing the string <code>a=1&b=2&c=3</code> 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 encoding
+ * Encoding to use when decoding the parameters.
+ */
+ public static void parse (
+ final List <NameValuePair> parameters,
+ final Scanner scanner,
+ final String encoding) {
+ scanner.useDelimiter(PARAMETER_SEPARATOR);
+ while (scanner.hasNext()) {
+ final String[] nameValue = scanner.next().split(NAME_VALUE_SEPARATOR);
+ if (nameValue.length == 0 || nameValue.length > 2)
+ throw new IllegalArgumentException("bad parameter");
+
+ final String name = decode(nameValue[0], encoding);
+ String value = null;
+ if (nameValue.length == 2)
+ value = decode(nameValue[1], encoding);
+ parameters.add(new BasicNameValuePair(name, value));
+ }
+ }
+
+ /**
+ * Returns a String that is suitable for use as an <code>application/x-www-form-urlencoded</code>
+ * list of parameters in an HTTP PUT or HTTP POST.
+ *
+ * @param parameters The parameters to include.
+ * @param encoding The encoding to use.
+ */
+ public static String format (
+ final List <? extends NameValuePair> parameters,
+ final String encoding) {
+ final StringBuilder result = new StringBuilder();
+ for (final NameValuePair parameter : parameters) {
+ final String encodedName = encode(parameter.getName(), encoding);
+ final String value = parameter.getValue();
+ final String encodedValue = value != null ? encode(value, encoding) : "";
+ if (result.length() > 0)
+ result.append(PARAMETER_SEPARATOR);
+ result.append(encodedName);
+ result.append(NAME_VALUE_SEPARATOR);
+ result.append(encodedValue);
+ }
+ return result.toString();
+ }
+
+ private static String decode (final String content, final String encoding) {
+ try {
+ return URLDecoder.decode(content,
+ encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET);
+ } catch (UnsupportedEncodingException problem) {
+ throw new IllegalArgumentException(problem);
+ }
+ }
+
+ private static String encode (final String content, final String encoding) {
+ try {
+ return URLEncoder.encode(content,
+ encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET);
+ } catch (UnsupportedEncodingException problem) {
+ throw new IllegalArgumentException(problem);
+ }
+ }
+
+}
diff --git a/src/org/apache/http/client/utils/package.html b/src/org/apache/http/client/utils/package.html
new file mode 100644
index 0000000..7abeb93
--- /dev/null
+++ b/src/org/apache/http/client/utils/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/client/utils/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Helpers and utility classes for <i>HttpClient</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/BasicEofSensorWatcher.java b/src/org/apache/http/conn/BasicEofSensorWatcher.java
new file mode 100644
index 0000000..9a9f3c5
--- /dev/null
+++ b/src/org/apache/http/conn/BasicEofSensorWatcher.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/BasicEofSensorWatcher.java $
+ * $Revision $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * Basic implementation of {@link EofSensorWatcher EofSensorWatcher}.
+ * The underlying connection is released on close or EOF.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @since 4.0
+ */
+public class BasicEofSensorWatcher implements EofSensorWatcher {
+
+
+ /** The connection to auto-release. */
+ protected ManagedClientConnection managedConn;
+
+ /** Whether to keep the connection alive. */
+ protected 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(ManagedClientConnection conn,
+ boolean reuse) {
+ if (conn == null)
+ throw new IllegalArgumentException
+ ("Connection may not be null.");
+
+ managedConn = conn;
+ attemptReuse = reuse;
+ }
+
+
+ // non-javadoc, see interface EofSensorWatcher
+ public boolean eofDetected(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;
+ }
+
+
+ // non-javadoc, see interface EofSensorWatcher
+ public boolean streamClosed(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;
+ }
+
+
+ // non-javadoc, see interface EofSensorWatcher
+ public boolean streamAbort(InputStream wrapped)
+ throws IOException {
+
+ managedConn.abortConnection();
+ return false;
+ }
+
+} // class BasicEofSensorWatcher
diff --git a/src/org/apache/http/conn/BasicManagedEntity.java b/src/org/apache/http/conn/BasicManagedEntity.java
new file mode 100644
index 0000000..9719e1a
--- /dev/null
+++ b/src/org/apache/http/conn/BasicManagedEntity.java
@@ -0,0 +1,220 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/BasicManagedEntity.java $
+ * $Revision $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.HttpEntityWrapper;
+
+
+/**
+ * An entity that releases a {@link ManagedClientConnection connection}.
+ * A {@link ManagedClientConnection} will
+ * typically <i>not</i> return a managed entity, but you can replace
+ * the unmanaged entity in the response with a managed one.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @since 4.0
+ */
+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(HttpEntity entity,
+ ManagedClientConnection conn,
+ boolean reuse) {
+ super(entity);
+
+ if (conn == null)
+ throw new IllegalArgumentException
+ ("Connection may not be null.");
+
+ this.managedConn = conn;
+ this.attemptReuse = reuse;
+ }
+
+
+ // non-javadoc, see interface HttpEntity
+ @Override
+ public boolean isRepeatable() {
+ return false;
+ }
+
+
+ // non-javadoc, see interface HttpEntity
+ @Override
+ public InputStream getContent() throws IOException {
+
+ return new EofSensorInputStream(wrappedEntity.getContent(), this);
+ }
+
+
+ // non-javadoc, see interface HttpEntity
+ @Override
+ public void consumeContent() throws IOException {
+
+ if (managedConn == null)
+ return;
+
+ try {
+ if (attemptReuse) {
+ // this will not trigger a callback from EofSensorInputStream
+ wrappedEntity.consumeContent();
+ managedConn.markReusable();
+ }
+ } finally {
+ releaseManagedConnection();
+ }
+ }
+
+
+ // non-javadoc, see interface HttpEntity
+ @Override
+ public void writeTo(final OutputStream outstream) throws IOException {
+ super.writeTo(outstream);
+ consumeContent();
+ }
+
+
+ // non-javadoc, see interface ConnectionReleaseTrigger
+ public void releaseConnection()
+ throws IOException {
+
+ this.consumeContent();
+ }
+
+
+ // non-javadoc, see interface ConnectionReleaseTrigger
+ public void abortConnection()
+ throws IOException {
+
+ if (managedConn != null) {
+ try {
+ managedConn.abortConnection();
+ } finally {
+ managedConn = null;
+ }
+ }
+ }
+
+
+ // non-javadoc, see interface EofSensorWatcher
+ public boolean eofDetected(InputStream wrapped)
+ throws IOException {
+
+ try {
+ if (attemptReuse && (managedConn != null)) {
+ // there may be some cleanup required, such as
+ // reading trailers after the response body:
+ wrapped.close();
+ managedConn.markReusable();
+ }
+ } finally {
+ releaseManagedConnection();
+ }
+ return false;
+ }
+
+
+ // non-javadoc, see interface EofSensorWatcher
+ public boolean streamClosed(InputStream wrapped)
+ throws IOException {
+
+ try {
+ if (attemptReuse && (managedConn != null)) {
+ // this assumes that closing the stream will
+ // consume the remainder of the response body:
+ wrapped.close();
+ managedConn.markReusable();
+ }
+ } finally {
+ releaseManagedConnection();
+ }
+ return false;
+ }
+
+
+ // non-javadoc, see interface EofSensorWatcher
+ public boolean streamAbort(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;
+ }
+ }
+ }
+
+} // class BasicManagedEntity
diff --git a/src/org/apache/http/conn/ClientConnectionManager.java b/src/org/apache/http/conn/ClientConnectionManager.java
new file mode 100644
index 0000000..e3375e1
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionManager.java
@@ -0,0 +1,129 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManager.java $
+ * $Revision: 671717 $
+ * $Date: 2008-06-25 21:03:24 -0700 (Wed, 25 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.SchemeRegistry;
+
+/**
+ * Management interface for {@link ManagedClientConnection client connections}.
+ *
+ * @author Michael Becke
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 671717 $
+ *
+ * @since 4.0
+ */
+public interface ClientConnectionManager {
+
+ /**
+ * Obtains the scheme registry used by this manager.
+ *
+ * @return the scheme registry, never <code>null</code>
+ */
+ 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 <code>idletime</code>
+ *
+ * @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()
+ ;
+
+
+} // interface ClientConnectionManager
diff --git a/src/org/apache/http/conn/ClientConnectionManagerFactory.java b/src/org/apache/http/conn/ClientConnectionManagerFactory.java
new file mode 100644
index 0000000..4bedc4e
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionManagerFactory.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManagerFactory.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.params.HttpParams;
+
+/**
+ * A factory for creating new {@link ClientConnectionManager} instances.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface ClientConnectionManagerFactory {
+
+ ClientConnectionManager newInstance(
+ HttpParams params,
+ SchemeRegistry schemeRegistry);
+
+}
diff --git a/src/org/apache/http/conn/ClientConnectionOperator.java b/src/org/apache/http/conn/ClientConnectionOperator.java
new file mode 100644
index 0000000..980b867
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionOperator.java
@@ -0,0 +1,120 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionOperator.java $
+ * $Revision: 645850 $
+ * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.scheme.SocketFactory;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+
+
+/**
+ * Interface for opening {@link OperatedClientConnection connections}.
+ * This interface encapsulates the logic to create sockets and to
+ * open or update the connection with the new socket.
+ * Implementations will most likely make use of
+ * {@link SocketFactory socket factories}.
+ * <br/>
+ * 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.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 645850 $ $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * @since 4.0
+ */
+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
+ * <code>null</code> 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
+ ;
+
+
+} // interface ClientConnectionOperator
+
diff --git a/src/org/apache/http/conn/ClientConnectionRequest.java b/src/org/apache/http/conn/ClientConnectionRequest.java
new file mode 100644
index 0000000..6ba02d0
--- /dev/null
+++ b/src/org/apache/http/conn/ClientConnectionRequest.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java $
+ * $Revision: 651815 $
+ * $Date: 2008-04-26 04:14:43 -0700 (Sat, 26 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Encapsulates a request for a {@link ManagedClientConnection}.
+ */
+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 <code>timeout</code>,
+ * may be <code>null</code> 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/src/org/apache/http/conn/ConnectTimeoutException.java b/src/org/apache/http/conn/ConnectTimeoutException.java
new file mode 100644
index 0000000..83a731a
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectTimeoutException.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectTimeoutException.java $
+ * $Revision: 617645 $
+ * $Date: 2008-02-01 13:05:31 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InterruptedIOException;
+
+/**
+ * A timeout while connecting to an HTTP server or waiting for an
+ * available connection from an HttpConnectionManager.
+ *
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ *
+ * @since 4.0
+ */
+public class ConnectTimeoutException extends InterruptedIOException {
+
+ private static final long serialVersionUID = -4816682903149535989L;
+
+ /**
+ * Creates a ConnectTimeoutException with a <tt>null</tt> detail message.
+ */
+ public ConnectTimeoutException() {
+ super();
+ }
+
+ /**
+ * Creates a ConnectTimeoutException with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public ConnectTimeoutException(final String message) {
+ super(message);
+ }
+
+}
diff --git a/src/org/apache/http/conn/ConnectionKeepAliveStrategy.java b/src/org/apache/http/conn/ConnectionKeepAliveStrategy.java
new file mode 100644
index 0000000..27d56cb
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectionKeepAliveStrategy.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: $
+ * $Revision: $
+ * $Date: $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpResponse;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Interface for deciding how long a connection can remain
+ * idle before being reused.
+ *
+ * @author <a href="mailto:sberlin at gmail.com">Sam Berlin</a>
+ *
+ *
+ * @version $Revision: $
+ *
+ * @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 ConnectionReuseStrategy}, if
+ * {@link 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/src/org/apache/http/conn/ConnectionPoolTimeoutException.java b/src/org/apache/http/conn/ConnectionPoolTimeoutException.java
new file mode 100644
index 0000000..7d4985e
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectionPoolTimeoutException.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectionPoolTimeoutException.java $
+ * $Revision: 505684 $
+ * $Date: 2007-02-10 04:40:02 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+/**
+ * A timeout while waiting for an available connection
+ * from a connection manager.
+ *
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ *
+ * @since 4.0
+ */
+public class ConnectionPoolTimeoutException extends ConnectTimeoutException {
+
+ private static final long serialVersionUID = -7898874842020245128L;
+
+ /**
+ * Creates a ConnectTimeoutException with a <tt>null</tt> detail message.
+ */
+ public ConnectionPoolTimeoutException() {
+ super();
+ }
+
+ /**
+ * Creates a ConnectTimeoutException with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public ConnectionPoolTimeoutException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/org/apache/http/conn/ConnectionReleaseTrigger.java b/src/org/apache/http/conn/ConnectionReleaseTrigger.java
new file mode 100644
index 0000000..a9ac12e
--- /dev/null
+++ b/src/org/apache/http/conn/ConnectionReleaseTrigger.java
@@ -0,0 +1,86 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectionReleaseTrigger.java $
+ * $Revision: 672367 $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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.
+ * <br/>
+ * The methods in this interface can safely be called multiple times.
+ * The first invocation releases the connection, subsequent calls
+ * are ignored.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @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 releaseConnection} for a graceful release.
+ *
+ * @throws IOException in case of an IO problem.
+ * The connection will be released anyway.
+ */
+ void abortConnection()
+ throws IOException
+ ;
+
+
+} // interface ConnectionReleaseTrigger
diff --git a/src/org/apache/http/conn/EofSensorInputStream.java b/src/org/apache/http/conn/EofSensorInputStream.java
new file mode 100644
index 0000000..0e1b90e
--- /dev/null
+++ b/src/org/apache/http/conn/EofSensorInputStream.java
@@ -0,0 +1,326 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/EofSensorInputStream.java $
+ * $Revision: 672367 $
+ * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * A stream wrapper that triggers actions on {@link #close close()} and EOF.
+ * Primarily used to auto-release an underlying
+ * {@link ManagedClientConnection connection}
+ * when the response body is consumed or no longer needed.
+ *
+ * <p>
+ * This class is based on <code>AutoCloseInputStream</code> in HttpClient 3.1,
+ * but has notable differences. It does not allow mark/reset, distinguishes
+ * different kinds of event, and does not always close the underlying stream
+ * on EOF. That decision is left to the {@link EofSensorWatcher watcher}.
+ * </p>
+ *
+ * @see EofSensorWatcher EofSensorWatcher
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Ortwin Glueck
+ * @author Eric Johnson
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672367 $
+ *
+ * @since 4.0
+ */
+// don't use FilterInputStream as the base class, we'd have to
+// override markSupported(), mark(), and reset() to disable them
+public class EofSensorInputStream extends InputStream
+ implements ConnectionReleaseTrigger {
+
+ /**
+ * The wrapped input stream, while accessible.
+ * The value changes to <code>null</code> 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 <code>null</code>, 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 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 <code>null</code> for
+ * auto-close behavior without notification
+ */
+ public EofSensorInputStream(final InputStream in,
+ final EofSensorWatcher watcher) {
+ if (in == null) {
+ throw new IllegalArgumentException
+ ("Wrapped stream may not be null.");
+ }
+
+ wrappedStream = in;
+ selfClosed = false;
+ eofWatcher = watcher;
+ }
+
+
+ /**
+ * Checks whether the underlying stream can be read from.
+ *
+ * @return <code>true</code> if the underlying stream is accessible,
+ * <code>false</code> 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);
+ }
+
+
+ // non-javadoc, see base class InputStream
+ @Override
+ public int read() throws IOException {
+ int l = -1;
+
+ if (isReadAllowed()) {
+ try {
+ l = wrappedStream.read();
+ checkEOF(l);
+ } catch (IOException ex) {
+ checkAbort();
+ throw ex;
+ }
+ }
+
+ return l;
+ }
+
+
+ // non-javadoc, see base class InputStream
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int l = -1;
+
+ if (isReadAllowed()) {
+ try {
+ l = wrappedStream.read(b, off, len);
+ checkEOF(l);
+ } catch (IOException ex) {
+ checkAbort();
+ throw ex;
+ }
+ }
+
+ return l;
+ }
+
+
+ // non-javadoc, see base class InputStream
+ @Override
+ public int read(byte[] b) throws IOException {
+ int l = -1;
+
+ if (isReadAllowed()) {
+ try {
+ l = wrappedStream.read(b);
+ checkEOF(l);
+ } catch (IOException ex) {
+ checkAbort();
+ throw ex;
+ }
+ }
+ return l;
+ }
+
+
+ // non-javadoc, see base class InputStream
+ @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 (IOException ex) {
+ checkAbort();
+ throw ex;
+ }
+ }
+
+ return a;
+ }
+
+
+ // non-javadoc, see base class InputStream
+ @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.
+ * <br/>
+ * 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(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 {
+ this.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();
+ }
+
+} // class EOFSensorInputStream
+
diff --git a/src/org/apache/http/conn/EofSensorWatcher.java b/src/org/apache/http/conn/EofSensorWatcher.java
new file mode 100644
index 0000000..837f8d9
--- /dev/null
+++ b/src/org/apache/http/conn/EofSensorWatcher.java
@@ -0,0 +1,112 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/EofSensorWatcher.java $
+ * $Revision: 552264 $
+ * $Date: 2007-07-01 02:37:47 -0700 (Sun, 01 Jul 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * A watcher for {@link EofSensorInputStream EofSensorInputStream}.
+ * Each stream will notify it's watcher at most once.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 552264 $
+ *
+ * @since 4.0
+ */
+public interface EofSensorWatcher {
+
+ /**
+ * Indicates that EOF is detected.
+ *
+ * @param wrapped the underlying stream which has reached EOF
+ *
+ * @return <code>true</code> if <code>wrapped</code> should be closed,
+ * <code>false</code> 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 <code>false</code> 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 <i>not</i> detected
+ * before closing. Otherwise, {@link #eofDetected eofDetected} is called.
+ *
+ * @param wrapped the underlying stream which has not reached EOF
+ *
+ * @return <code>true</code> if <code>wrapped</code> should be closed,
+ * <code>false</code> 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 <code>false</code> 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 <i>not</i> detected
+ * before aborting. Otherwise, {@link #eofDetected eofDetected} is called.
+ * <p/>
+ * 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 <code>true</code> if <code>wrapped</code> should be closed,
+ * <code>false</code> 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 <code>false</code> was returned.
+ */
+ boolean streamAbort(InputStream wrapped)
+ throws IOException
+ ;
+
+
+} // interface EofSensorWatcher
diff --git a/src/org/apache/http/conn/HttpHostConnectException.java b/src/org/apache/http/conn/HttpHostConnectException.java
new file mode 100644
index 0000000..743ca77
--- /dev/null
+++ b/src/org/apache/http/conn/HttpHostConnectException.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/HttpHostConnectException.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.net.ConnectException;
+
+import org.apache.http.HttpHost;
+
+/**
+ * A {@link ConnectException} that specifies the {@link HttpHost} that was
+ * being connected to.
+ */
+public class HttpHostConnectException extends ConnectException {
+
+ private static final long serialVersionUID = -3194482710275220224L;
+
+ private final HttpHost host;
+
+ public HttpHostConnectException(final HttpHost host, final ConnectException cause) {
+ super("Connection to " + host + " refused");
+ this.host = host;
+ initCause(cause);
+ }
+
+ public HttpHost getHost() {
+ return this.host;
+ }
+
+}
diff --git a/src/org/apache/http/conn/ManagedClientConnection.java b/src/org/apache/http/conn/ManagedClientConnection.java
new file mode 100644
index 0000000..f642cb9
--- /dev/null
+++ b/src/org/apache/http/conn/ManagedClientConnection.java
@@ -0,0 +1,261 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ManagedClientConnection.java $
+ * $Revision: 672969 $
+ * $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+
+
+/**
+ * A client-side connection with advanced connection logic.
+ * Instances are typically obtained from a connection manager.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672969 $
+ *
+ * @since 4.0
+ */
+public interface ManagedClientConnection extends
+ HttpClientConnection, HttpInetConnection, 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 <code>true</code> if this connection is secure,
+ * <code>false</code> otherwise
+ */
+ boolean isSecure()
+ ;
+
+
+ /**
+ * Obtains the current route of this connection.
+ *
+ * @return the route established so far, or
+ * <code>null</code> 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.
+ * <br/>
+ * <b>Note:</b> 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,
+ * <code>null</code> 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.
+ * <br/>
+ * <b>Note:</b> 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 <code>true</code> if the tunnel should be considered
+ * secure, <code>false</code> 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 <i>proxy chains</i>, 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 <i>not</i> the proxy <i>through</i> which
+ * the tunnel was established, but the new end point
+ * of the tunnel. The tunnel does <i>not</i> yet
+ * reach to the target, use {@link #tunnelTarget}
+ * to indicate an end-to-end tunnel.
+ * @param secure <code>true</code> if the connection should be
+ * considered secure, <code>false</code> 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 it's 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.
+ * <br/>
+ * 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 it's reuse. Reasons for preventing reuse include
+ * error conditions and the evaluation of a
+ * {@link org.apache.http.ConnectionReuseStrategy reuse strategy}.
+ * <br/>
+ * <b>Note:</b>
+ * It is <i>not</i> 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 <code>true</code> if this connection is marked as being in
+ * a reusable communication state,
+ * <code>false</code> 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);
+
+} // interface ManagedClientConnection
diff --git a/src/org/apache/http/conn/MultihomePlainSocketFactory.java b/src/org/apache/http/conn/MultihomePlainSocketFactory.java
new file mode 100644
index 0000000..e9549ab
--- /dev/null
+++ b/src/org/apache/http/conn/MultihomePlainSocketFactory.java
@@ -0,0 +1,212 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/MultihomePlainSocketFactory.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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.Collections;
+import java.util.List;
+import java.util.Arrays;
+
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.SocketFactory;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * 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.
+ */
+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 org.apache.http.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 sock 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(Socket sock, String host, int port,
+ InetAddress localAddress, int localPort,
+ HttpParams params)
+ throws IOException {
+
+ if (host == null) {
+ throw new IllegalArgumentException("Target host may not be null.");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null.");
+ }
+
+ if (sock == null)
+ sock = createSocket();
+
+ if ((localAddress != null) || (localPort > 0)) {
+
+ // we need to bind explicitly
+ if (localPort < 0)
+ localPort = 0; // indicates "any"
+
+ InetSocketAddress isa =
+ new InetSocketAddress(localAddress, localPort);
+ sock.bind(isa);
+ }
+
+ int timeout = HttpConnectionParams.getConnectionTimeout(params);
+
+ InetAddress[] inetadrs = InetAddress.getAllByName(host);
+ List<InetAddress> addresses = new ArrayList<InetAddress>(inetadrs.length);
+ addresses.addAll(Arrays.asList(inetadrs));
+ Collections.shuffle(addresses);
+
+ IOException lastEx = null;
+ for (InetAddress address: addresses) {
+ try {
+ sock.connect(new InetSocketAddress(address, port), timeout);
+ break;
+ } catch (SocketTimeoutException ex) {
+ throw ex;
+ } catch (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 <code>false</code>
+ *
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public final boolean isSecure(Socket sock)
+ throws IllegalArgumentException {
+
+ if (sock == null) {
+ throw new IllegalArgumentException("Socket may not be null.");
+ }
+ // This class check assumes that createSocket() calls the constructor
+ // directly. If it was using javax.net.SocketFactory, we couldn't make
+ // an assumption about the socket class here.
+ if (sock.getClass() != Socket.class) {
+ throw new IllegalArgumentException
+ ("Socket not created by this factory.");
+ }
+ // This check is performed last since it calls a method implemented
+ // by the argument object. getClass() is final in java.lang.Object.
+ if (sock.isClosed()) {
+ throw new IllegalArgumentException("Socket is closed.");
+ }
+
+ return false;
+
+ } // isSecure
+
+
+ /**
+ * Compares this factory with an object.
+ * There is only one instance of this class.
+ *
+ * @param obj the object to compare with
+ *
+ * @return iff the argument is this object
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this);
+ }
+
+ /**
+ * Obtains a hash code for this object.
+ * All instances of this class have the same hash code.
+ * There is only one instance of this class.
+ */
+ @Override
+ public int hashCode() {
+ return PlainSocketFactory.class.hashCode();
+ }
+
+}
diff --git a/src/org/apache/http/conn/OperatedClientConnection.java b/src/org/apache/http/conn/OperatedClientConnection.java
new file mode 100644
index 0000000..2eda8e9
--- /dev/null
+++ b/src/org/apache/http/conn/OperatedClientConnection.java
@@ -0,0 +1,174 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/OperatedClientConnection.java $
+ * $Revision: 646087 $
+ * $Date: 2008-04-08 14:36:46 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.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}.
+ *
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 646087 $ $Date: 2008-04-08 14:36:46 -0700 (Tue, 08 Apr 2008) $
+ *
+ * @since 4.0
+ */
+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.
+ * <br/>
+ * 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 <code>true</code> if this connection is secure,
+ * <code>false</code> 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()
+ ;
+
+
+ // There is no getParams(). For the moment, we
+ // do not require connections to store parameters.
+
+
+ /**
+ * Signals that this connection is in the process of being open.
+ * <br/>
+ * By calling this method, you can provide the connection with
+ * the unconnected socket that will be connected before
+ * {@link #openCompleted} is called. This allows
+ * the connection to close that socket if
+ * {@link org.apache.http.HttpConnection#shutdown shutdown}
+ * is called before it is open. Closing the 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.
+ * <br/>
+ * You also must call {@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 <code>true</code> if this connection is secure, for
+ * example if an <code>SSLSocket</code> is used, or
+ * <code>false</code> 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.
+ * <br/>
+ * <b>Note:</b> Updating the connection will <i>not</i> 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 <code>null</code> to continue using the old socket.
+ * If <code>null</code> 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 <code>true</code> if this connection is now secure,
+ * <code>false</code> if it is not secure
+ * @param params new parameters for this connection
+ */
+ void update(Socket sock, HttpHost target,
+ boolean secure, HttpParams params)
+ throws IOException
+ ;
+
+
+} // interface OperatedClientConnection
diff --git a/src/org/apache/http/conn/package.html b/src/org/apache/http/conn/package.html
new file mode 100644
index 0000000..e0b29cd
--- /dev/null
+++ b/src/org/apache/http/conn/package.html
@@ -0,0 +1,91 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/package.html $
+ * $Revision: 651813 $
+ * $Date: 2008-04-26 03:43:34 -0700 (Sat, 26 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The client-side connection management and handling API
+at the heart of what is referred to as <i>HttpConn</i>.
+This component provides interfaces and implementations for
+opening and managing connections.
+
+<p>
+The lowest layer of connection handling is comprised of
+{@link org.apache.http.conn.OperatedClientConnection OperatedClientConnection}
+and
+{@link org.apache.http.conn.ClientConnectionOperator ClientConnectionOperator}.
+The connection interface extends the core
+{@link org.apache.http.HttpClientConnection HttpClientConnection}
+by operations to set and update a socket.
+An operator encapsulates the logic to open and layer sockets,
+typically using a {@link org.apache.http.conn.scheme.SocketFactory SocketFactory}.
+The socket factory for a protocol
+{@link org.apache.http.conn.scheme.Scheme Scheme}
+such as "http" or "https" can be looked up in a
+{@link org.apache.http.conn.scheme.SchemeRegistry SchemeRegistry}.
+Applications without a need for sophisticated connection management
+can use this layer directly.
+</p>
+
+<p>
+On top of that lies the connection management layer. A
+{@link org.apache.http.conn.ClientConnectionManager ClientConnectionManager}
+internally manages operated connections, but hands out instances of
+{@link org.apache.http.conn.ManagedClientConnection ManagedClientConnection}.
+This interface abstracts from the underlying socket operations and
+provides convenient methods for opening and updating sockets in order
+to establish a {@link org.apache.http.conn.routing.HttpRoute route}.
+The operator is encapsulated by the connection manager and called
+automatically.
+
+<br/>
+
+Connections obtained from a manager have to be returned after use.
+This can be {@link org.apache.http.conn.ConnectionReleaseTrigger triggered}
+on various levels, either by releasing the
+{@link org.apache.http.conn.ManagedClientConnection
+ connection}
+directly, or by calling a method on an
+{@link org.apache.http.conn.BasicManagedEntity entity}
+received from the connection, or by closing the
+{@link org.apache.http.conn.EofSensorInputStream stream}
+from which that entity is being read.
+
+Connection managers will try to keep returned connections alive in
+order to re-use them for subsequent requests along the same route.
+The managed connection interface and all triggers for connection release
+provide methods to enable or disable this behavior.
+</p>
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/params/ConnConnectionPNames.java b/src/org/apache/http/conn/params/ConnConnectionPNames.java
new file mode 100644
index 0000000..ff1a090
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnConnectionPNames.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnConnectionPNames.java $
+ * $Revision: 576068 $
+ * $Date: 2007-09-16 03:25:01 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+
+/**
+ * Parameter names for connections in HttpConn.
+ *
+ * @version $Revision: 576068 $
+ *
+ * @since 4.0
+ */
+public interface ConnConnectionPNames {
+
+
+ /**
+ * Defines the maximum number of ignorable lines before we expect
+ * a HTTP response's status line.
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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
+ * (default in lenient mode).
+ * </p>
+ */
+ public static final String MAX_STATUS_LINE_GARBAGE = "http.connection.max-status-line-garbage";
+
+
+}
diff --git a/src/org/apache/http/conn/params/ConnConnectionParamBean.java b/src/org/apache/http/conn/params/ConnConnectionParamBean.java
new file mode 100644
index 0000000..094b152
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnConnectionParamBean.java
@@ -0,0 +1,55 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnConnectionParamBean.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Allows for setting parameters relating to connections on
+ * {@link HttpParams}. This class ensures that the values set on the params
+ * are type-safe.
+ */
+public class ConnConnectionParamBean extends HttpAbstractParamBean {
+
+ public ConnConnectionParamBean (final HttpParams params) {
+ super(params);
+ }
+
+ /**
+ * @see ConnConnectionPNames#MAX_STATUS_LINE_GARBAGE
+ */
+ public void setMaxStatusLineGarbage (final int maxStatusLineGarbage) {
+ params.setIntParameter(ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, maxStatusLineGarbage);
+ }
+
+}
diff --git a/src/org/apache/http/conn/params/ConnManagerPNames.java b/src/org/apache/http/conn/params/ConnManagerPNames.java
new file mode 100644
index 0000000..1184b12
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnManagerPNames.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnManagerPNames.java $
+ * $Revision: 658781 $
+ * $Date: 2008-05-21 10:42:13 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+
+/**
+ * Parameter names for connection managers in HttpConn.
+ *
+ * @version $Revision: 658781 $
+ *
+ * @since 4.0
+ */
+public interface ConnManagerPNames {
+
+ /**
+ * Defines the timeout in milliseconds used when retrieving an instance of
+ * {@link org.apache.http.conn.ManagedClientConnection} from the
+ * {@link org.apache.http.conn.ClientConnectionManager}.
+ * <p>
+ * This parameter expects a value of type {@link Long}.
+ * </p>
+ */
+ 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.
+ * <p>
+ * This parameter expects a value of type {@link ConnPerRoute}.
+ * </p>
+ */
+ 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.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ */
+ public static final String MAX_TOTAL_CONNECTIONS = "http.conn-manager.max-total";
+
+}
diff --git a/src/org/apache/http/conn/params/ConnManagerParamBean.java b/src/org/apache/http/conn/params/ConnManagerParamBean.java
new file mode 100644
index 0000000..830b7bf
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnManagerParamBean.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnManagerParamBean.java $
+ * $Revision: 658781 $
+ * $Date: 2008-05-21 10:42:13 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Allows for setting parameters relating to connection managers on
+ * {@link HttpParams}. This class ensures that the values set on the params
+ * are type-safe.
+ */
+public class ConnManagerParamBean extends HttpAbstractParamBean {
+
+ public ConnManagerParamBean (final HttpParams params) {
+ super(params);
+ }
+
+ public void setTimeout (final long timeout) {
+ params.setLongParameter(ConnManagerPNames.TIMEOUT, timeout);
+ }
+
+ /** @see ConnManagerPNames#MAX_TOTAL_CONNECTIONS */
+ public void setMaxTotalConnections (final int maxConnections) {
+ params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, maxConnections);
+ }
+
+ /** @see ConnManagerPNames#MAX_CONNECTIONS_PER_ROUTE */
+ public void setConnectionsPerRoute(final ConnPerRouteBean connPerRoute) {
+ params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, connPerRoute);
+ }
+
+}
diff --git a/src/org/apache/http/conn/params/ConnManagerParams.java b/src/org/apache/http/conn/params/ConnManagerParams.java
new file mode 100644
index 0000000..c6e042e
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnManagerParams.java
@@ -0,0 +1,169 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnManagerParams.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.params.HttpParams;
+
+/**
+ * This class represents a collection of HTTP protocol parameters applicable
+ * to client-side
+ * {@link org.apache.http.conn.ClientConnectionManager connection managers}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Michael Becke
+ *
+ * @version $Revision: 658785 $
+ *
+ * @since 4.0
+ *
+ * @see ConnManagerPNames
+ */
+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 org.apache.http.conn.ManagedClientConnection} from the
+ * {@link org.apache.http.conn.ClientConnectionManager}.
+ *
+ * @return timeout in milliseconds.
+ */
+ public static long getTimeout(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getLongParameter(TIMEOUT, 0);
+ }
+
+ /**
+ * Sets the timeout in milliseconds used when retrieving a
+ * {@link org.apache.http.conn.ManagedClientConnection} from the
+ * {@link org.apache.http.conn.ClientConnectionManager}.
+ *
+ * @param timeout the timeout in milliseconds
+ */
+ public static void setTimeout(final HttpParams params, long timeout) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ 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(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
+ *
+ * @see ConnManagerPNames#MAX_CONNECTIONS_PER_ROUTE
+ */
+ public static void setMaxConnectionsPerRoute(final HttpParams params,
+ final ConnPerRoute connPerRoute) {
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("HTTP parameters must not be null.");
+ }
+ 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.
+ *
+ * @see ConnManagerPNames#MAX_CONNECTIONS_PER_ROUTE
+ */
+ public static ConnPerRoute getMaxConnectionsPerRoute(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("HTTP parameters must not be null.");
+ }
+ 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.
+ *
+ * @see ConnManagerPNames#MAX_TOTAL_CONNECTIONS
+ */
+ public static void setMaxTotalConnections(
+ final HttpParams params,
+ int maxTotalConnections) {
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("HTTP parameters must not be null.");
+ }
+ params.setIntParameter(MAX_TOTAL_CONNECTIONS, maxTotalConnections);
+ }
+
+ /**
+ * Gets the maximum number of connections allowed.
+ *
+ * @param params HTTP parameters
+ *
+ * @return The maximum number of connections allowed.
+ *
+ * @see ConnManagerPNames#MAX_TOTAL_CONNECTIONS
+ */
+ public static int getMaxTotalConnections(
+ final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("HTTP parameters must not be null.");
+ }
+ return params.getIntParameter(MAX_TOTAL_CONNECTIONS, DEFAULT_MAX_TOTAL_CONNECTIONS);
+ }
+
+
+}
diff --git a/src/org/apache/http/conn/params/ConnPerRoute.java b/src/org/apache/http/conn/params/ConnPerRoute.java
new file mode 100644
index 0000000..abff04e
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnPerRoute.java
@@ -0,0 +1,51 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnPerRoute.java $
+ * $Revision: 651813 $
+ * $Date: 2008-04-26 03:43:34 -0700 (Sat, 26 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * This interface is intended for looking up maximum number of connections
+ * allowed for for a given route. This class can be used by pooling
+ * {@link org.apache.http.conn.ClientConnectionManager connection managers} for
+ * a fine-grained control of connections on a per route basis.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 651813 $
+ *
+ * @since 4.0
+ */
+public interface ConnPerRoute {
+
+ int getMaxForRoute(HttpRoute route);
+
+}
diff --git a/src/org/apache/http/conn/params/ConnPerRouteBean.java b/src/org/apache/http/conn/params/ConnPerRouteBean.java
new file mode 100644
index 0000000..c6a36e3
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnPerRouteBean.java
@@ -0,0 +1,114 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnPerRouteBean.java $
+ * $Revision: 652947 $
+ * $Date: 2008-05-02 16:15:40 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * 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 org.apache.http.conn.ClientConnectionManager connection managers} for
+ * a fine-grained control of connections on a per route basis.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 652947 $
+ *
+ * @since 4.0
+ */
+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 Map<HttpRoute, Integer> maxPerHostMap;
+
+ private int defaultMax;
+
+ public ConnPerRouteBean(int defaultMax) {
+ super();
+ this.maxPerHostMap = new HashMap<HttpRoute, Integer>();
+ setDefaultMaxPerRoute(defaultMax);
+ }
+
+ public ConnPerRouteBean() {
+ this(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
+ }
+
+ public int getDefaultMax() {
+ return this.defaultMax;
+ }
+
+ public void setDefaultMaxPerRoute(int max) {
+ if (max < 1) {
+ throw new IllegalArgumentException
+ ("The maximum must be greater than 0.");
+ }
+ this.defaultMax = max;
+ }
+
+ public void setMaxForRoute(final HttpRoute route, int max) {
+ if (route == null) {
+ throw new IllegalArgumentException
+ ("HTTP route may not be null.");
+ }
+ if (max < 1) {
+ throw new IllegalArgumentException
+ ("The maximum must be greater than 0.");
+ }
+ this.maxPerHostMap.put(route, Integer.valueOf(max));
+ }
+
+ public int getMaxForRoute(final HttpRoute route) {
+ if (route == null) {
+ throw new IllegalArgumentException
+ ("HTTP route may not be null.");
+ }
+ Integer max = this.maxPerHostMap.get(route);
+ if (max != null) {
+ return max.intValue();
+ } else {
+ return this.defaultMax;
+ }
+ }
+
+ public void setMaxForRoutes(final Map<HttpRoute, Integer> map) {
+ if (map == null) {
+ return;
+ }
+ this.maxPerHostMap.clear();
+ this.maxPerHostMap.putAll(map);
+ }
+
+}
diff --git a/src/org/apache/http/conn/params/ConnRoutePNames.java b/src/org/apache/http/conn/params/ConnRoutePNames.java
new file mode 100644
index 0000000..f9d42db
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnRoutePNames.java
@@ -0,0 +1,84 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnRoutePNames.java $
+ * $Revision: 613656 $
+ * $Date: 2008-01-20 11:06:56 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+/**
+ * Parameter names for routing in HttpConn.
+ *
+ * @version $Revision: 613656 $
+ *
+ * @since 4.0
+ */
+public interface ConnRoutePNames {
+
+ /**
+ * Parameter for the default proxy.
+ * The default value will be used by some
+ * {@link org.apache.http.conn.routing.HttpRoutePlanner HttpRoutePlanner}
+ * implementations, in particular the default implementation.
+ * <p>
+ * This parameter expects a value of type {@link org.apache.http.HttpHost}.
+ * </p>
+ */
+ 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 org.apache.http.conn.routing.HttpRoutePlanner HttpRoutePlanner}
+ * implementations, in particular the default implementation.
+ * <p>
+ * This parameter expects a value of type {@link java.net.InetAddress}.
+ * </p>
+ */
+ 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 org.apache.http.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.
+ * <p>
+ * This parameter expects a value of type
+ * {@link org.apache.http.conn.routing.HttpRoute HttpRoute}.
+ * </p>
+ */
+ public static final String FORCED_ROUTE = "http.route.forced-route";
+
+}
+
diff --git a/src/org/apache/http/conn/params/ConnRouteParamBean.java b/src/org/apache/http/conn/params/ConnRouteParamBean.java
new file mode 100644
index 0000000..9464c02
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnRouteParamBean.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnRouteParamBean.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Allows for setting parameters relating to connection routes on
+ * {@link HttpParams}. This class ensures that the values set on the params
+ * are type-safe.
+ */
+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/src/org/apache/http/conn/params/ConnRouteParams.java b/src/org/apache/http/conn/params/ConnRouteParams.java
new file mode 100644
index 0000000..2fa1654
--- /dev/null
+++ b/src/org/apache/http/conn/params/ConnRouteParams.java
@@ -0,0 +1,203 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/ConnRouteParams.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.params;
+
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.conn.routing.HttpRoute;
+
+
+
+/**
+ * An adaptor for accessing route related parameters in {@link HttpParams}.
+ * See {@link ConnRoutePNames} for parameter name definitions.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 658785 $
+ *
+ * @since 4.0
+ */
+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 <i>valid</i> host.
+ */
+ public static final HttpHost NO_HOST =
+ new HttpHost("127.0.0.255", 0, "no-host");
+
+ /**
+ * 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);
+
+
+ /** Disabled default constructor. */
+ private ConnRouteParams() {
+ // no body
+ }
+
+
+ /**
+ * Obtains the {@link ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}
+ * parameter value.
+ * {@link #NO_HOST} will be mapped to <code>null</code>,
+ * 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
+ * <code>null</code> if not set
+ */
+ public static HttpHost getDefaultProxy(HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters must not be null.");
+ }
+ 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 <code>null</code>.
+ * Note that {@link #NO_HOST} will be mapped to
+ * <code>null</code> by {@link #getDefaultProxy},
+ * to allow for explicit unsetting in hierarchies.
+ */
+ public static void setDefaultProxy(HttpParams params,
+ HttpHost proxy) {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters must not be null.");
+ }
+ params.setParameter(DEFAULT_PROXY, proxy);
+ }
+
+
+ /**
+ * Obtains the {@link ConnRoutePNames#FORCED_ROUTE FORCED_ROUTE}
+ * parameter value.
+ * {@link #NO_ROUTE} will be mapped to <code>null</code>,
+ * 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
+ * <code>null</code> if not set
+ */
+ public static HttpRoute getForcedRoute(HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters must not be null.");
+ }
+ 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 <code>null</code>.
+ * Note that {@link #NO_ROUTE} will be mapped to
+ * <code>null</code> by {@link #getForcedRoute},
+ * to allow for explicit unsetting in hierarchies.
+ */
+ public static void setForcedRoute(HttpParams params,
+ HttpRoute route) {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters must not be null.");
+ }
+ 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
+ * <code>null</code>. 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
+ * <code>null</code> if not set
+ */
+ public static InetAddress getLocalAddress(HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters must not be null.");
+ }
+ 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 <code>null</code>
+ */
+ public static void setLocalAddress(HttpParams params,
+ InetAddress local) {
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters must not be null.");
+ }
+ params.setParameter(LOCAL_ADDRESS, local);
+ }
+
+}
+
diff --git a/src/org/apache/http/conn/params/package.html b/src/org/apache/http/conn/params/package.html
new file mode 100644
index 0000000..9b80420
--- /dev/null
+++ b/src/org/apache/http/conn/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpConn</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/routing/BasicRouteDirector.java b/src/org/apache/http/conn/routing/BasicRouteDirector.java
new file mode 100644
index 0000000..a3714ec
--- /dev/null
+++ b/src/org/apache/http/conn/routing/BasicRouteDirector.java
@@ -0,0 +1,181 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/BasicRouteDirector.java $
+ * $Revision: 620255 $
+ * $Date: 2008-02-10 02:23:55 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+
+
+/**
+ * Basic implementation of an {@link HttpRouteDirector HttpRouteDirector}.
+ * This implementation is stateless and therefore thread-safe.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 620255 $
+ *
+ * @since 4.0
+ */
+public class BasicRouteDirector implements HttpRouteDirector {
+
+ // public default constructor
+
+
+ /**
+ * Provides the next step.
+ *
+ * @param plan the planned route
+ * @param fact the currently established route, or
+ * <code>null</code> 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(RouteInfo plan, RouteInfo fact) {
+ if (plan == null) {
+ throw new IllegalArgumentException
+ ("Planned route may not be null.");
+ }
+
+ 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(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(RouteInfo plan, 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(RouteInfo plan, 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-1; i++) {
+ if (!plan.getHopTarget(i).equals(fact.getHopTarget(i)))
+ return UNREACHABLE;
+ }
+ // now we know that the target matches and proxies so far are the same
+ if (phc > 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;
+ }
+
+
+} // class BasicRouteDirector
diff --git a/src/org/apache/http/conn/routing/HttpRoute.java b/src/org/apache/http/conn/routing/HttpRoute.java
new file mode 100644
index 0000000..1e870b8
--- /dev/null
+++ b/src/org/apache/http/conn/routing/HttpRoute.java
@@ -0,0 +1,443 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/HttpRoute.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+
+/**
+ * The route for a request.
+ * Instances of this class are unmodifiable and therefore suitable
+ * for use as lookup keys.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 653041 $
+ *
+ * @since 4.0
+ */
+public final class HttpRoute implements RouteInfo, Cloneable {
+
+ /** The target host to connect to. */
+ private final HttpHost targetHost;
+
+ /**
+ * The local address to connect from.
+ * <code>null</code> indicates that the default should be used.
+ */
+ private final InetAddress localAddress;
+
+ /** The proxy servers, if any. */
+ private final HttpHost[] 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;
+
+
+ /**
+ * Internal, fully-specified constructor.
+ * This constructor does <i>not</i> clone the proxy chain array,
+ * nor test it for <code>null</code> elements. This conversion and
+ * check is the responsibility of the public constructors.
+ * The order of arguments here is different from the similar public
+ * constructor, as required by Java.
+ *
+ * @param local the local address to route from, or
+ * <code>null</code> for the default
+ * @param target the host to which to route
+ * @param proxies the proxy chain to use, or
+ * <code>null</code> for a direct route
+ * @param secure <code>true</code> if the route is (to be) secure,
+ * <code>false</code> otherwise
+ * @param tunnelled the tunnel type of this route, or
+ * <code>null</code> for PLAIN
+ * @param layered the layering type of this route, or
+ * <code>null</code> for PLAIN
+ */
+ private HttpRoute(InetAddress local,
+ HttpHost target, HttpHost[] proxies,
+ boolean secure,
+ TunnelType tunnelled, LayerType layered) {
+ if (target == null) {
+ throw new IllegalArgumentException
+ ("Target host may not be null.");
+ }
+ if ((tunnelled == TunnelType.TUNNELLED) && (proxies == null)) {
+ throw new IllegalArgumentException
+ ("Proxy required if tunnelled.");
+ }
+
+ // tunnelled is already checked above, that is in line with the default
+ if (tunnelled == null)
+ tunnelled = TunnelType.PLAIN;
+ if (layered == null)
+ layered = LayerType.PLAIN;
+
+ this.targetHost = target;
+ this.localAddress = local;
+ this.proxyChain = proxies;
+ this.secure = secure;
+ this.tunnelled = tunnelled;
+ this.layered = layered;
+ }
+
+
+ /**
+ * 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
+ * <code>null</code> for the default
+ * @param proxies the proxy chain to use, or
+ * <code>null</code> for a direct route
+ * @param secure <code>true</code> if the route is (to be) secure,
+ * <code>false</code> otherwise
+ * @param tunnelled the tunnel type of this route
+ * @param layered the layering type of this route
+ */
+ public HttpRoute(HttpHost target, InetAddress local, HttpHost[] proxies,
+ boolean secure, TunnelType tunnelled, LayerType layered) {
+ this(local, target, toChain(proxies), 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
+ * <code>null</code> for the default
+ * @param proxy the proxy to use, or
+ * <code>null</code> for a direct route
+ * @param secure <code>true</code> if the route is (to be) secure,
+ * <code>false</code> otherwise
+ * @param tunnelled <code>true</code> if the route is (to be) tunnelled
+ * via the proxy,
+ * <code>false</code> otherwise
+ * @param layered <code>true</code> if the route includes a
+ * layered protocol,
+ * <code>false</code> otherwise
+ */
+ public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
+ boolean secure, TunnelType tunnelled, LayerType layered) {
+ this(local, target, toChain(proxy), 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
+ * <code>null</code> for the default
+ * @param secure <code>true</code> if the route is (to be) secure,
+ * <code>false</code> otherwise
+ */
+ public HttpRoute(HttpHost target, InetAddress local, boolean secure) {
+ this(local, target, null, secure, TunnelType.PLAIN, LayerType.PLAIN);
+ }
+
+
+ /**
+ * Creates a new direct insecure route.
+ *
+ * @param target the host to which to route
+ */
+ public HttpRoute(HttpHost target) {
+ this(null, target, null, false, TunnelType.PLAIN, LayerType.PLAIN);
+ }
+
+
+ /**
+ * Creates a new route through a proxy.
+ * When using this constructor, the <code>proxy</code> 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
+ * <code>null</code> for the default
+ * @param proxy the proxy to use
+ * @param secure <code>true</code> if the route is (to be) secure,
+ * <code>false</code> otherwise
+ */
+ public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
+ boolean secure) {
+ this(local, target, toChain(proxy), secure,
+ secure ? TunnelType.TUNNELLED : TunnelType.PLAIN,
+ secure ? LayerType.LAYERED : LayerType.PLAIN);
+ if (proxy == null) {
+ throw new IllegalArgumentException
+ ("Proxy host may not be null.");
+ }
+ }
+
+
+ /**
+ * Helper to convert a proxy to a proxy chain.
+ *
+ * @param proxy the only proxy in the chain, or <code>null</code>
+ *
+ * @return a proxy chain array, or <code>null</code>
+ */
+ private static HttpHost[] toChain(HttpHost proxy) {
+ if (proxy == null)
+ return null;
+
+ return new HttpHost[]{ proxy };
+ }
+
+
+ /**
+ * Helper to duplicate and check a proxy chain.
+ * An empty proxy chain is converted to <code>null</code>.
+ *
+ * @param proxies the proxy chain to duplicate, or <code>null</code>
+ *
+ * @return a new proxy chain array, or <code>null</code>
+ */
+ private static HttpHost[] toChain(HttpHost[] proxies) {
+ if ((proxies == null) || (proxies.length < 1))
+ return null;
+
+ for (HttpHost proxy : proxies) {
+ if (proxy == null)
+ throw new IllegalArgumentException
+ ("Proxy chain may not contain null elements.");
+ }
+
+ // copy the proxy chain, the traditional way
+ HttpHost[] result = new HttpHost[proxies.length];
+ System.arraycopy(proxies, 0, result, 0, proxies.length);
+
+ return result;
+ }
+
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final HttpHost getTargetHost() {
+ return this.targetHost;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final InetAddress getLocalAddress() {
+ return this.localAddress;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final int getHopCount() {
+ return (proxyChain == null) ? 1 : (proxyChain.length+1);
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final HttpHost getHopTarget(int hop) {
+ if (hop < 0)
+ throw new IllegalArgumentException
+ ("Hop index must not be negative: " + hop);
+ final int hopcount = getHopCount();
+ if (hop >= hopcount)
+ throw new IllegalArgumentException
+ ("Hop index " + hop +
+ " exceeds route length " + hopcount);
+
+ HttpHost result = null;
+ if (hop < hopcount-1)
+ result = this.proxyChain[hop];
+ else
+ result = this.targetHost;
+
+ return result;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final HttpHost getProxyHost() {
+ return (this.proxyChain == null) ? null : this.proxyChain[0];
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final TunnelType getTunnelType() {
+ return this.tunnelled;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final boolean isTunnelled() {
+ return (this.tunnelled == TunnelType.TUNNELLED);
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final LayerType getLayerType() {
+ return this.layered;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final boolean isLayered() {
+ return (this.layered == LayerType.LAYERED);
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final boolean isSecure() {
+ return this.secure;
+ }
+
+
+ /**
+ * Compares this route to another.
+ *
+ * @param o the object to compare with
+ *
+ * @return <code>true</code> if the argument is the same route,
+ * <code>false</code>
+ */
+ @Override
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof HttpRoute))
+ return false;
+
+ HttpRoute that = (HttpRoute) o;
+ boolean equal = this.targetHost.equals(that.targetHost);
+ equal &=
+ ( this.localAddress == that.localAddress) ||
+ ((this.localAddress != null) &&
+ this.localAddress.equals(that.localAddress));
+ equal &=
+ ( this.proxyChain == that.proxyChain) ||
+ ((this.proxyChain != null) &&
+ (that.proxyChain != null) &&
+ (this.proxyChain.length == that.proxyChain.length));
+ // comparison of actual proxies follows below
+ equal &=
+ (this.secure == that.secure) &&
+ (this.tunnelled == that.tunnelled) &&
+ (this.layered == that.layered);
+
+ // chain length has been compared above, now check the proxies
+ if (equal && (this.proxyChain != null)) {
+ for (int i=0; equal && (i<this.proxyChain.length); i++)
+ equal = this.proxyChain[i].equals(that.proxyChain[i]);
+ }
+
+ return equal;
+ }
+
+
+ /**
+ * Generates a hash code for this route.
+ *
+ * @return the hash code
+ */
+ @Override
+ public final int hashCode() {
+
+ int hc = this.targetHost.hashCode();
+
+ if (this.localAddress != null)
+ hc ^= localAddress.hashCode();
+ if (this.proxyChain != null) {
+ hc ^= proxyChain.length;
+ for (HttpHost aProxyChain : proxyChain) hc ^= aProxyChain.hashCode();
+ }
+
+ if (this.secure)
+ hc ^= 0x11111111;
+
+ hc ^= this.tunnelled.hashCode();
+ hc ^= this.layered.hashCode();
+
+ return hc;
+ }
+
+
+ /**
+ * Obtains a description of this route.
+ *
+ * @return a human-readable representation of this route
+ */
+ @Override
+ public final String toString() {
+ StringBuilder cab = new StringBuilder(50 + getHopCount()*30);
+
+ cab.append("HttpRoute[");
+ 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 (HttpHost aProxyChain : this.proxyChain) {
+ cab.append(aProxyChain);
+ 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();
+ }
+
+
+} // class HttpRoute
diff --git a/src/org/apache/http/conn/routing/HttpRouteDirector.java b/src/org/apache/http/conn/routing/HttpRouteDirector.java
new file mode 100644
index 0000000..8cfcf67
--- /dev/null
+++ b/src/org/apache/http/conn/routing/HttpRouteDirector.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/HttpRouteDirector.java $
+ * $Revision: 620255 $
+ * $Date: 2008-02-10 02:23:55 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 620255 $
+ *
+ * @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
+ * <code>null</code> 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)
+ ;
+
+
+} // interface HttpRouteDirector
diff --git a/src/org/apache/http/conn/routing/HttpRoutePlanner.java b/src/org/apache/http/conn/routing/HttpRoutePlanner.java
new file mode 100644
index 0000000..489702a
--- /dev/null
+++ b/src/org/apache/http/conn/routing/HttpRoutePlanner.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/HttpRoutePlanner.java $
+ * $Revision: 613654 $
+ * $Date: 2008-01-20 11:00:19 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.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.
+ */
+public interface HttpRoutePlanner {
+
+ /**
+ * Determines the route for a request.
+ *
+ * @param target the target host for the request.
+ * Implementations may accept <code>null</code>
+ * 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 <code>null</code>.
+ *
+ * @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/src/org/apache/http/conn/routing/RouteInfo.java b/src/org/apache/http/conn/routing/RouteInfo.java
new file mode 100644
index 0000000..3449cb1
--- /dev/null
+++ b/src/org/apache/http/conn/routing/RouteInfo.java
@@ -0,0 +1,194 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/RouteInfo.java $
+ * $Revision: 652200 $
+ * $Date: 2008-04-29 17:22:43 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+
+
+/**
+ * Read-only interface for route information.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 652200 $
+ *
+ * @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.
+ * <br/>
+ * 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 <code>null</code>
+ */
+ 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 <i>n</i> proxies has <i>n+1</i> 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
+ * <code>null</code> 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 <code>true</code> if tunnelled end-to-end through at least
+ * one proxy,
+ * <code>false</code> 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 <code>true</code> if layered,
+ * <code>false</code> otherwise
+ */
+ boolean isLayered()
+ ;
+
+
+ /**
+ * Checks whether this route is secure.
+ *
+ * @return <code>true</code> if secure,
+ * <code>false</code> otherwise
+ */
+ boolean isSecure()
+ ;
+
+
+} // interface RouteInfo
diff --git a/src/org/apache/http/conn/routing/RouteTracker.java b/src/org/apache/http/conn/routing/RouteTracker.java
new file mode 100644
index 0000000..ba8213e
--- /dev/null
+++ b/src/org/apache/http/conn/routing/RouteTracker.java
@@ -0,0 +1,439 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/RouteTracker.java $
+ * $Revision: 620254 $
+ * $Date: 2008-02-10 02:18:48 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.routing;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+
+
+/**
+ * Helps tracking the steps in establishing a route.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 620254 $
+ *
+ * @since 4.0
+ */
+public final class RouteTracker implements RouteInfo, Cloneable {
+
+ /** The target host to connect to. */
+ private final HttpHost targetHost;
+
+ /**
+ * The local address to connect from.
+ * <code>null</code> 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
+ * <code>null</code> for the default
+ */
+ public RouteTracker(HttpHost target, InetAddress local) {
+ if (target == null) {
+ throw new IllegalArgumentException("Target host may not be null.");
+ }
+ this.targetHost = target;
+ this.localAddress = local;
+ this.tunnelled = TunnelType.PLAIN;
+ this.layered = LayerType.PLAIN;
+ }
+
+
+ /**
+ * 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(HttpRoute route) {
+ this(route.getTargetHost(), route.getLocalAddress());
+ }
+
+
+ /**
+ * Tracks connecting to the target.
+ *
+ * @param secure <code>true</code> if the route is secure,
+ * <code>false</code> otherwise
+ */
+ public final void connectTarget(boolean secure) {
+ if (this.connected) {
+ throw new IllegalStateException("Already connected.");
+ }
+ this.connected = true;
+ this.secure = secure;
+ }
+
+
+ /**
+ * Tracks connecting to the first proxy.
+ *
+ * @param proxy the proxy connected to
+ * @param secure <code>true</code> if the route is secure,
+ * <code>false</code> otherwise
+ */
+ public final void connectProxy(HttpHost proxy, boolean secure) {
+ if (proxy == null) {
+ throw new IllegalArgumentException("Proxy host may not be null.");
+ }
+ if (this.connected) {
+ throw new IllegalStateException("Already connected.");
+ }
+ this.connected = true;
+ this.proxyChain = new HttpHost[]{ proxy };
+ this.secure = secure;
+ }
+
+
+ /**
+ * Tracks tunnelling to the target.
+ *
+ * @param secure <code>true</code> if the route is secure,
+ * <code>false</code> otherwise
+ */
+ public final void tunnelTarget(boolean secure) {
+ if (!this.connected) {
+ throw new IllegalStateException("No tunnel unless connected.");
+ }
+ if (this.proxyChain == null) {
+ throw new IllegalStateException("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 <code>true</code> if the route is secure,
+ * <code>false</code> otherwise
+ */
+ public final void tunnelProxy(HttpHost proxy, boolean secure) {
+ if (proxy == null) {
+ throw new IllegalArgumentException("Proxy host may not be null.");
+ }
+ if (!this.connected) {
+ throw new IllegalStateException("No tunnel unless connected.");
+ }
+ if (this.proxyChain == null) {
+ throw new IllegalStateException("No proxy tunnel without proxy.");
+ }
+
+ // prepare an extended proxy chain
+ 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 <code>true</code> if the route is secure,
+ * <code>false</code> otherwise
+ */
+ public final void layerProtocol(boolean secure) {
+ // it is possible to layer a protocol over a direct connection,
+ // although this case is probably not considered elsewhere
+ if (!this.connected) {
+ throw new IllegalStateException
+ ("No layered protocol unless connected.");
+ }
+ this.layered = LayerType.LAYERED;
+ this.secure = secure;
+ }
+
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final HttpHost getTargetHost() {
+ return this.targetHost;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final InetAddress getLocalAddress() {
+ return this.localAddress;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final int getHopCount() {
+ int hops = 0;
+ if (this.connected) {
+ if (proxyChain == null)
+ hops = 1;
+ else
+ hops = proxyChain.length + 1;
+ }
+ return hops;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final HttpHost getHopTarget(int hop) {
+ if (hop < 0)
+ throw new IllegalArgumentException
+ ("Hop index must not be negative: " + hop);
+ final int hopcount = getHopCount();
+ if (hop >= hopcount) {
+ throw new IllegalArgumentException
+ ("Hop index " + hop +
+ " exceeds tracked route length " + hopcount +".");
+ }
+
+ HttpHost result = null;
+ if (hop < hopcount-1)
+ result = this.proxyChain[hop];
+ else
+ result = this.targetHost;
+
+ return result;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final HttpHost getProxyHost() {
+ return (this.proxyChain == null) ? null : this.proxyChain[0];
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final boolean isConnected() {
+ return this.connected;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final TunnelType getTunnelType() {
+ return this.tunnelled;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final boolean isTunnelled() {
+ return (this.tunnelled == TunnelType.TUNNELLED);
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final LayerType getLayerType() {
+ return this.layered;
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ public final boolean isLayered() {
+ return (this.layered == LayerType.LAYERED);
+ }
+
+
+ // non-JavaDoc, see interface RouteInfo
+ 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
+ * <code>null</code> 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 <code>true</code> if the argument is the same tracked route,
+ * <code>false</code>
+ */
+ @Override
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof RouteTracker))
+ return false;
+
+ RouteTracker that = (RouteTracker) o;
+ boolean equal = this.targetHost.equals(that.targetHost);
+ equal &=
+ ( this.localAddress == that.localAddress) ||
+ ((this.localAddress != null) &&
+ this.localAddress.equals(that.localAddress));
+ equal &=
+ ( this.proxyChain == that.proxyChain) ||
+ ((this.proxyChain != null) &&
+ (that.proxyChain != null) &&
+ (this.proxyChain.length == that.proxyChain.length));
+ // comparison of actual proxies follows below
+ equal &=
+ (this.connected == that.connected) &&
+ (this.secure == that.secure) &&
+ (this.tunnelled == that.tunnelled) &&
+ (this.layered == that.layered);
+
+ // chain length has been compared above, now check the proxies
+ if (equal && (this.proxyChain != null)) {
+ for (int i=0; equal && (i<this.proxyChain.length); i++)
+ equal = this.proxyChain[i].equals(that.proxyChain[i]);
+ }
+
+ return equal;
+ }
+
+
+ /**
+ * 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 hc = this.targetHost.hashCode();
+
+ if (this.localAddress != null)
+ hc ^= localAddress.hashCode();
+ if (this.proxyChain != null) {
+ hc ^= proxyChain.length;
+ for (int i=0; i<proxyChain.length; i++)
+ hc ^= proxyChain[i].hashCode();
+ }
+
+ if (this.connected)
+ hc ^= 0x11111111;
+ if (this.secure)
+ hc ^= 0x22222222;
+
+ hc ^= this.tunnelled.hashCode();
+ hc ^= this.layered.hashCode();
+
+ return hc;
+ }
+
+
+ /**
+ * Obtains a description of the tracked route.
+ *
+ * @return a human-readable representation of the tracked route
+ */
+ @Override
+ public final String toString() {
+ 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 (int i=0; i<this.proxyChain.length; i++) {
+ cab.append(this.proxyChain[i]);
+ 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();
+ }
+
+
+} // class RouteTracker
diff --git a/src/org/apache/http/conn/routing/package.html b/src/org/apache/http/conn/routing/package.html
new file mode 100644
index 0000000..b50f97c
--- /dev/null
+++ b/src/org/apache/http/conn/routing/package.html
@@ -0,0 +1,62 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/package.html $
+ * $Revision: 613656 $
+ * $Date: 2008-01-20 11:06:56 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The client-side route representation and tracking API, part of <i>HttpConn</i>.
+
+<p>
+An {@link org.apache.http.conn.routing.HttpRoute HttpRoute}
+is the path along which a request has to be sent to the server.
+The route starts at a local network address and may pass
+through one or more proxies before reaching the target.
+Routes through proxies can be tunnelled, and a layered protocol (TLS/SSL)
+might be put on top of the tunnel.
+The {@link org.apache.http.conn.routing.RouteTracker RouteTracker}
+helps in tracking the steps for establishing a route, while an
+{@link org.apache.http.conn.routing.HttpRouteDirector HttpRouteDirector}
+determines the next step to take.
+</p>
+
+
+<p>
+The {@link org.apache.http.conn.routing.HttpRoutePlanner HttpRoutePlanner}
+is responsible for determining a route to a given target host.
+Implementations must know about proxies to use, and about exemptions
+for hosts that should be contacted directly without a proxy.
+</p>
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/scheme/HostNameResolver.java b/src/org/apache/http/conn/scheme/HostNameResolver.java
new file mode 100644
index 0000000..ca6615c
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/HostNameResolver.java
@@ -0,0 +1,41 @@
+/*
+ * $HeadURL:$
+ * $Revision:$
+ * $Date:$
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+public interface HostNameResolver {
+
+ InetAddress resolve (String hostname) throws IOException;
+
+}
diff --git a/src/org/apache/http/conn/scheme/LayeredSocketFactory.java b/src/org/apache/http/conn/scheme/LayeredSocketFactory.java
new file mode 100644
index 0000000..8dc6c6c
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/LayeredSocketFactory.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java $
+ * $Revision: 645850 $
+ * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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.
+ *
+ * @author Michael Becke
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @since 4.0
+ */
+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/src/org/apache/http/conn/scheme/PlainSocketFactory.java b/src/org/apache/http/conn/scheme/PlainSocketFactory.java
new file mode 100644
index 0000000..acc13f7
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/PlainSocketFactory.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java $
+ * $Revision: 659194 $
+ * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * The default class for creating sockets.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Michael Becke
+ */
+public final class PlainSocketFactory implements SocketFactory {
+
+ /**
+ * The factory singleton.
+ */
+ private static final
+ PlainSocketFactory DEFAULT_FACTORY = new PlainSocketFactory();
+
+ private final HostNameResolver nameResolver;
+
+ /**
+ * Gets the singleton instance of this class.
+ * @return the one and only plain socket factory
+ */
+ public static PlainSocketFactory getSocketFactory() {
+ return DEFAULT_FACTORY;
+ }
+
+ public PlainSocketFactory(final HostNameResolver nameResolver) {
+ super();
+ this.nameResolver = nameResolver;
+ }
+
+
+ public PlainSocketFactory() {
+ this(null);
+ }
+
+ // non-javadoc, see interface org.apache.http.conn.SocketFactory
+ public Socket createSocket() {
+ return new Socket();
+ }
+
+ // non-javadoc, see interface org.apache.http.conn.SocketFactory
+ public Socket connectSocket(Socket sock, String host, int port,
+ InetAddress localAddress, int localPort,
+ HttpParams params)
+ throws IOException {
+
+ if (host == null) {
+ throw new IllegalArgumentException("Target host may not be null.");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null.");
+ }
+
+ if (sock == null)
+ sock = createSocket();
+
+ if ((localAddress != null) || (localPort > 0)) {
+
+ // we need to bind explicitly
+ if (localPort < 0)
+ localPort = 0; // indicates "any"
+
+ InetSocketAddress isa =
+ new InetSocketAddress(localAddress, localPort);
+ sock.bind(isa);
+ }
+
+ int timeout = HttpConnectionParams.getConnectionTimeout(params);
+
+ InetSocketAddress remoteAddress;
+ if (this.nameResolver != null) {
+ remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port);
+ } else {
+ remoteAddress = new InetSocketAddress(host, port);
+ }
+
+ sock.connect(remoteAddress, timeout);
+
+ 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 <code>false</code>
+ *
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public final boolean isSecure(Socket sock)
+ throws IllegalArgumentException {
+
+ if (sock == null) {
+ throw new IllegalArgumentException("Socket may not be null.");
+ }
+ // This class check assumes that createSocket() calls the constructor
+ // directly. If it was using javax.net.SocketFactory, we couldn't make
+ // an assumption about the socket class here.
+ if (sock.getClass() != Socket.class) {
+ throw new IllegalArgumentException
+ ("Socket not created by this factory.");
+ }
+ // This check is performed last since it calls a method implemented
+ // by the argument object. getClass() is final in java.lang.Object.
+ if (sock.isClosed()) {
+ throw new IllegalArgumentException("Socket is closed.");
+ }
+
+ return false;
+
+ } // isSecure
+
+
+ /**
+ * Compares this factory with an object.
+ * There is only one instance of this class.
+ *
+ * @param obj the object to compare with
+ *
+ * @return iff the argument is this object
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this);
+ }
+
+ /**
+ * Obtains a hash code for this object.
+ * All instances of this class have the same hash code.
+ * There is only one instance of this class.
+ */
+ @Override
+ public int hashCode() {
+ return PlainSocketFactory.class.hashCode();
+ }
+
+}
diff --git a/src/org/apache/http/conn/scheme/Scheme.java b/src/org/apache/http/conn/scheme/Scheme.java
new file mode 100644
index 0000000..590d59d
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/Scheme.java
@@ -0,0 +1,223 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/Scheme.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn.scheme;
+
+import java.util.Locale;
+
+import org.apache.http.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}.
+ *
+ * <p>
+ * For example, to configure support for "https://" URLs,
+ * you could write code like the following:
+ * </p>
+ * <pre>
+ * Scheme https = new Scheme("https", new MySecureSocketFactory(), 443);
+ * SchemeRegistry.DEFAULT.register(https);
+ * </pre>
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Michael Becke
+ * @author Jeff Dever
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ */
+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 SocketFactory 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;
+
+
+ /**
+ * Creates a new scheme.
+ * Whether the created scheme allows for layered connections
+ * depends on the class of <code>factory</code>.
+ *
+ * @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
+ */
+ public Scheme(final String name,
+ final SocketFactory factory,
+ final int port) {
+
+ if (name == null) {
+ throw new IllegalArgumentException
+ ("Scheme name may not be null");
+ }
+ if (factory == null) {
+ throw new IllegalArgumentException
+ ("Socket factory may not be null");
+ }
+ if ((port <= 0) || (port > 0xffff)) {
+ throw new IllegalArgumentException
+ ("Port is invalid: " + port);
+ }
+
+ this.name = name.toLowerCase(Locale.ENGLISH);
+ this.socketFactory = factory;
+ this.defaultPort = port;
+ this.layered = (factory instanceof LayeredSocketFactory);
+ }
+
+
+ /**
+ * 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
+ */
+ public final SocketFactory getSocketFactory() {
+ return 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 <code>true</code> if layered connections are possible,
+ * <code>false</code> 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(int port) {
+ return ((port <= 0) || (port > 0xffff)) ? 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) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(this.name);
+ buffer.append(':');
+ buffer.append(Integer.toString(this.defaultPort));
+ stringRep = buffer.toString();
+ }
+ return stringRep;
+ }
+
+
+ /**
+ * Compares this scheme to an object.
+ *
+ * @param obj the object to compare with
+ *
+ * @return <code>true</code> iff the argument is equal to this scheme
+ */
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (!(obj instanceof Scheme)) return false;
+
+ Scheme s = (Scheme) obj;
+ return (name.equals(s.name) &&
+ defaultPort == s.defaultPort &&
+ layered == s.layered &&
+ socketFactory.equals(s.socketFactory)
+ );
+ } // equals
+
+
+ /**
+ * Obtains a hash code for this scheme.
+ *
+ * @return the hash code
+ */
+ @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);
+ hash = LangUtils.hashCode(hash, this.socketFactory);
+ return hash;
+ }
+
+} // class Scheme
diff --git a/src/org/apache/http/conn/scheme/SchemeRegistry.java b/src/org/apache/http/conn/scheme/SchemeRegistry.java
new file mode 100644
index 0000000..2ee8685
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/SchemeRegistry.java
@@ -0,0 +1,187 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/SchemeRegistry.java $
+ * $Revision: 648356 $
+ * $Date: 2008-04-15 10:57:53 -0700 (Tue, 15 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn.scheme;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.HttpHost;
+
+/**
+ * A set of supported protocol {@link Scheme schemes}.
+ * Schemes are identified by lowercase names.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 648356 $ $Date: 2008-04-15 10:57:53 -0700 (Tue, 15 Apr 2008) $
+ *
+ * @since 4.0
+ */
+public final class SchemeRegistry {
+
+ /** The available schemes in this registry. */
+ private final Map<String,Scheme> registeredSchemes;
+
+
+ /**
+ * Creates a new, empty scheme registry.
+ */
+ public SchemeRegistry() {
+ super();
+ registeredSchemes = new LinkedHashMap<String,Scheme>();
+ }
+
+
+ /**
+ * Obtains a scheme by name.
+ *
+ * @param name the name of the scheme to look up (in lowercase)
+ *
+ * @return the scheme, never <code>null</code>
+ *
+ * @throws IllegalStateException
+ * if the scheme with the given name is not registered
+ */
+ public synchronized final Scheme getScheme(String name) {
+ 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 <code>getScheme(host.getSchemeName())</pre>
+ *
+ * @param host the host for which to obtain the scheme
+ *
+ * @return the scheme for the given host, never <code>null</code>
+ *
+ * @throws IllegalStateException
+ * if a scheme with the respective name is not registered
+ */
+ public synchronized final Scheme getScheme(HttpHost host) {
+ if (host == null) {
+ throw new IllegalArgumentException("Host must not be null.");
+ }
+ 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
+ * <code>null</code> if there is none by this name
+ */
+ public synchronized final Scheme get(String name) {
+ if (name == null)
+ throw new IllegalArgumentException("Name must not be null.");
+
+ // leave it to the caller to use the correct name - all lowercase
+ //name = name.toLowerCase();
+ 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
+ * <code>null</code> if none was registered
+ */
+ public synchronized final Scheme register(Scheme sch) {
+ if (sch == null)
+ throw new IllegalArgumentException("Scheme must not be null.");
+
+ 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
+ * <code>null</code> if there was none
+ */
+ public synchronized final Scheme unregister(String name) {
+ if (name == null)
+ throw new IllegalArgumentException("Name must not be null.");
+
+ // leave it to the caller to use the correct name - all lowercase
+ //name = name.toLowerCase();
+ Scheme gone = registeredSchemes.remove(name);
+ return gone;
+ }
+
+
+ /**
+ * Obtains the names of the registered schemes in their default order.
+ *
+ * @return List containing registered scheme names.
+ */
+ public synchronized final List<String> getSchemeNames() {
+ return new ArrayList<String>(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 synchronized void setItems(final Map<String, Scheme> map) {
+ if (map == null) {
+ return;
+ }
+ registeredSchemes.clear();
+ registeredSchemes.putAll(map);
+ }
+
+} // class SchemeRegistry
+
diff --git a/src/org/apache/http/conn/scheme/SocketFactory.java b/src/org/apache/http/conn/scheme/SocketFactory.java
new file mode 100644
index 0000000..bb553b2
--- /dev/null
+++ b/src/org/apache/http/conn/scheme/SocketFactory.java
@@ -0,0 +1,138 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/SocketFactory.java $
+ * $Revision: 645850 $
+ * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.params.HttpParams;
+
+/**
+ * A factory for creating and connecting sockets.
+ * The factory encapsulates the logic for establishing a socket connection.
+ * <br/>
+ * Both {@link java.lang.Object#equals(java.lang.Object) Object.equals()}
+ * and {@link java.lang.Object#hashCode() Object.hashCode()}
+ * must be overridden for the correct operation of some connection managers.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author Michael Becke
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ */
+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}.
+ * <code>null</code> 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
+ * <code>null</code> 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 <code>sock</code> 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 <code>params</code>
+ */
+ 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 <i>not</i> perform I/O operations
+ * in this method.
+ * <br/>
+ * 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 cypher suite chosen for the connection.
+ *
+ * @param sock the connected socket to check
+ *
+ * @return <code>true</code> if the connection of the socket
+ * should be considered secure, or
+ * <code>false</code> 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 <i>not</i> 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/src/org/apache/http/conn/ssl/AbstractVerifier.java b/src/org/apache/http/conn/ssl/AbstractVerifier.java
new file mode 100644
index 0000000..5195e58
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/AbstractVerifier.java
@@ -0,0 +1,343 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import org.apache.http.conn.util.InetAddressUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+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.StringTokenizer;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Abstract base class for all standard {@link X509HostnameVerifier}
+ * implementations.
+ *
+ * @author Julius Davies
+ */
+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].
+ * <p/>
+ * 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 AbstractVerifier() {
+ super();
+ }
+
+ public final void verify(String host, SSLSocket ssl)
+ throws IOException {
+ if(host == null) {
+ throw new NullPointerException("host to verify is null");
+ }
+
+ ssl.startHandshake();
+ 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:
+ 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();
+ }
+ }
+
+ Certificate[] certs = session.getPeerCertificates();
+ X509Certificate x509 = (X509Certificate) certs[0];
+ verify(host, x509);
+ }
+
+ public final boolean verify(String host, SSLSession session) {
+ try {
+ Certificate[] certs = session.getPeerCertificates();
+ X509Certificate x509 = (X509Certificate) certs[0];
+ verify(host, x509);
+ return true;
+ }
+ catch(SSLException e) {
+ return false;
+ }
+ }
+
+ public final void verify(String host, X509Certificate cert)
+ throws SSLException {
+ String[] cns = getCNs(cert);
+ String[] subjectAlts = getDNSSubjectAlts(cert);
+ 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).
+ LinkedList<String> names = new LinkedList<String>();
+ if(cns != null && cns.length > 0 && cns[0] != null) {
+ names.add(cns[0]);
+ }
+ if(subjectAlts != null) {
+ for (String subjectAlt : subjectAlts) {
+ if (subjectAlt != null) {
+ names.add(subjectAlt);
+ }
+ }
+ }
+
+ if(names.isEmpty()) {
+ String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt";
+ throw new SSLException(msg);
+ }
+
+ // StringBuffer for building the error message.
+ StringBuffer buf = new StringBuffer();
+
+ // We're can be case-insensitive when comparing the host we used to
+ // establish the socket to the hostname in the certificate.
+ String hostName = host.trim().toLowerCase(Locale.ENGLISH);
+ boolean match = false;
+ for(Iterator<String> it = names.iterator(); it.hasNext();) {
+ // Don't trim the CN, though!
+ String cn = it.next();
+ cn = cn.toLowerCase(Locale.ENGLISH);
+ // Store CN in StringBuffer 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...
+ boolean doWildcard = cn.startsWith("*.") &&
+ cn.lastIndexOf('.') >= 0 &&
+ acceptableCountryWildcard(cn) &&
+ !InetAddressUtils.isIPv4Address(host);
+
+ if(doWildcard) {
+ 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(cn);
+ }
+ if(match) {
+ break;
+ }
+ }
+ if(!match) {
+ throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf);
+ }
+ }
+
+ public static boolean acceptableCountryWildcard(String cn) {
+ int cnLen = cn.length();
+ if(cnLen >= 7 && cnLen <= 9) {
+ // Look for the '.' in the 3rd-last position:
+ if(cn.charAt(cnLen - 3) == '.') {
+ // Trim off the [*.] and the [.XX].
+ String s = cn.substring(2, cnLen - 3);
+ // And test against the sorted array of bad 2lds:
+ int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
+ return x < 0;
+ }
+ }
+ return true;
+ }
+
+ public static String[] getCNs(X509Certificate cert) {
+ LinkedList<String> cnList = new LinkedList<String>();
+ /*
+ Sebastian Hauer's original StrictSSLProtocolSocketFactory used
+ getName() and had the following comment:
+
+ Parses a X.500 distinguished name for the value of the
+ "Common Name" field. This is done a bit sloppy right
+ now and should probably be done a bit more according to
+ <code>RFC 2253</code>.
+
+ I've noticed that toString() seems to do a better job than
+ getName() on these X500Principal objects, so I'm hoping that
+ addresses Sebastian's concern.
+
+ For example, getName() gives me this:
+ 1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
+
+ whereas toString() gives me this:
+ EMAILADDRESS=juliusdavies@cucbc.com
+
+ Looks like toString() even works with non-ascii domain names!
+ I tested it with "&#x82b1;&#x5b50;.co.jp" and it worked fine.
+ */
+ String subjectPrincipal = cert.getSubjectX500Principal().toString();
+ StringTokenizer st = new StringTokenizer(subjectPrincipal, ",");
+ while(st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ int x = tok.indexOf("CN=");
+ if(x >= 0) {
+ cnList.add(tok.substring(x + 3));
+ }
+ }
+ if(!cnList.isEmpty()) {
+ String[] cns = new String[cnList.size()];
+ cnList.toArray(cns);
+ return cns;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Extracts the array of SubjectAlt DNS names from an X509Certificate.
+ * Returns null if there aren't any.
+ * <p/>
+ * Note: Java doesn't appear able to extract international characters
+ * from the SubjectAlts. It can only extract international characters
+ * from the CN field.
+ * <p/>
+ * (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(X509Certificate cert) {
+ LinkedList<String> subjectAltList = new LinkedList<String>();
+ Collection<List<?>> c = null;
+ try {
+ c = cert.getSubjectAlternativeNames();
+ }
+ catch(CertificateParsingException cpe) {
+ Logger.getLogger(AbstractVerifier.class.getName())
+ .log(Level.FINE, "Error parsing certificate.", cpe);
+ }
+ if(c != null) {
+ for (List<?> aC : c) {
+ List<?> list = aC;
+ int type = ((Integer) list.get(0)).intValue();
+ // If type is 2, then we've got a dNSName
+ if (type == 2) {
+ String s = (String) list.get(1);
+ subjectAltList.add(s);
+ }
+ }
+ }
+ if(!subjectAltList.isEmpty()) {
+ String[] subjectAlts = new String[subjectAltList.size()];
+ subjectAltList.toArray(subjectAlts);
+ return subjectAlts;
+ } else {
+ return 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;
+ }
+
+}
diff --git a/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java b/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java
new file mode 100644
index 0000000..05828fb
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java
@@ -0,0 +1,54 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java $
+ * $Revision: 617642 $
+ * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+/**
+ * The ALLOW_ALL HostnameVerifier essentially turns hostname verification
+ * off. This implementation is a no-op, and never throws the SSLException.
+ *
+ * @author Julius Davies
+ */
+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/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java b/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java
new file mode 100644
index 0000000..f4129d6
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java $
+ * $Revision: 617642 $
+ * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import javax.net.ssl.SSLException;
+
+/**
+ * The HostnameVerifier that works the same way as Curl and Firefox.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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".
+ *
+ * @author Julius Davies
+ */
+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
+ public final String toString() {
+ return "BROWSER_COMPATIBLE";
+ }
+
+}
diff --git a/src/org/apache/http/conn/ssl/SSLSocketFactory.java b/src/org/apache/http/conn/ssl/SSLSocketFactory.java
new file mode 100644
index 0000000..498b43e
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/SSLSocketFactory.java
@@ -0,0 +1,384 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $
+ * $Revision: 659194 $
+ * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import org.apache.http.conn.scheme.HostNameResolver;
+import org.apache.http.conn.scheme.LayeredSocketFactory;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+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;
+
+/**
+ * Layered socket factory for TLS/SSL connections, based on JSSE.
+ *.
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * SSLSocketFactory will enable server authentication when supplied with
+ * a {@link KeyStore truststore} file containg 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.
+ * </p>
+ *
+ * <p>
+ * Use JDK keytool utility to import a trusted certificate and generate a truststore file:
+ * <pre>
+ * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * SSLSocketFactory will enable client authentication when supplied with
+ * a {@link KeyStore keystore} file containg 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
+ * </p>
+ *
+ * <p>
+ * Use the following sequence of actions to generate a keystore file
+ * </p>
+ * <ul>
+ * <li>
+ * <p>
+ * Use JDK keytool utility to generate a new key
+ * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
+ * For simplicity use the same password for the key as that of the keystore
+ * </p>
+ * </li>
+ * <li>
+ * <p>
+ * Issue a certificate signing request (CSR)
+ * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
+ * </p>
+ * </li>
+ * <li>
+ * <p>
+ * 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.
+ * </p>
+ * </li>
+ * <li>
+ * <p>
+ * Import the trusted CA root certificate
+ * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
+ * </p>
+ * </li>
+ * <li>
+ * <p>
+ * Import the PKCS#7 file containg the complete certificate chain
+ * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
+ * </p>
+ * </li>
+ * <li>
+ * <p>
+ * Verify the content the resultant keystore file
+ * <pre>keytool -list -v -keystore my.keystore</pre>
+ * </p>
+ * </li>
+ * </ul>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Julius Davies
+ */
+
+public class SSLSocketFactory implements 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();
+ /**
+ * The factory using the default JVM settings for secure connections.
+ */
+ private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
+
+ /**
+ * Gets an singleton instance of the SSLProtocolSocketFactory.
+ * @return a SSLProtocolSocketFactory
+ */
+ public static SSLSocketFactory getSocketFactory() {
+ return DEFAULT_FACTORY;
+ }
+
+ private final SSLContext sslcontext;
+ private final javax.net.ssl.SSLSocketFactory socketfactory;
+ private final HostNameResolver nameResolver;
+ private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
+
+ public SSLSocketFactory(
+ String algorithm,
+ final KeyStore keystore,
+ final String keystorePassword,
+ final KeyStore truststore,
+ final SecureRandom random,
+ final HostNameResolver nameResolver)
+ throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+ {
+ super();
+ if (algorithm == null) {
+ algorithm = TLS;
+ }
+ KeyManager[] keymanagers = null;
+ if (keystore != null) {
+ keymanagers = createKeyManagers(keystore, keystorePassword);
+ }
+ TrustManager[] trustmanagers = null;
+ if (truststore != null) {
+ trustmanagers = createTrustManagers(truststore);
+ }
+ this.sslcontext = SSLContext.getInstance(algorithm);
+ this.sslcontext.init(keymanagers, trustmanagers, random);
+ this.socketfactory = this.sslcontext.getSocketFactory();
+ this.nameResolver = nameResolver;
+ }
+
+ public SSLSocketFactory(
+ final KeyStore keystore,
+ final String keystorePassword,
+ final KeyStore truststore)
+ throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+ {
+ this(TLS, keystore, keystorePassword, truststore, null, null);
+ }
+
+ public SSLSocketFactory(final KeyStore keystore, final String keystorePassword)
+ throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+ {
+ this(TLS, keystore, keystorePassword, null, null, null);
+ }
+
+ public SSLSocketFactory(final KeyStore truststore)
+ throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+ {
+ this(TLS, null, null, truststore, null, null);
+ }
+
+ /**
+ * Creates the default SSL socket factory.
+ * This constructor is used exclusively to instantiate the factory for
+ * {@link #getSocketFactory getSocketFactory}.
+ */
+ private SSLSocketFactory() {
+ super();
+ this.sslcontext = null;
+ this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+ this.nameResolver = null;
+ }
+
+ private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
+ if (keystore == null) {
+ throw new IllegalArgumentException("Keystore may not be null");
+ }
+ KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
+ KeyManagerFactory.getDefaultAlgorithm());
+ kmfactory.init(keystore, password != null ? password.toCharArray(): null);
+ return kmfactory.getKeyManagers();
+ }
+
+ private static TrustManager[] createTrustManagers(final KeyStore keystore)
+ throws KeyStoreException, NoSuchAlgorithmException {
+ if (keystore == null) {
+ throw new IllegalArgumentException("Keystore may not be null");
+ }
+ TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
+ TrustManagerFactory.getDefaultAlgorithm());
+ tmfactory.init(keystore);
+ return tmfactory.getTrustManagers();
+ }
+
+
+ // non-javadoc, see interface org.apache.http.conn.SocketFactory
+ public Socket createSocket()
+ throws IOException {
+
+ // the cast makes sure that the factory is working as expected
+ return (SSLSocket) this.socketfactory.createSocket();
+ }
+
+
+ // non-javadoc, see interface org.apache.http.conn.SocketFactory
+ public Socket connectSocket(
+ final Socket sock,
+ final String host,
+ final int port,
+ final InetAddress localAddress,
+ int localPort,
+ final HttpParams params
+ ) throws IOException {
+
+ if (host == null) {
+ throw new IllegalArgumentException("Target host may not be null.");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null.");
+ }
+
+ SSLSocket sslsock = (SSLSocket)
+ ((sock != null) ? sock : createSocket());
+
+ if ((localAddress != null) || (localPort > 0)) {
+
+ // we need to bind explicitly
+ if (localPort < 0)
+ localPort = 0; // indicates "any"
+
+ InetSocketAddress isa =
+ new InetSocketAddress(localAddress, localPort);
+ sslsock.bind(isa);
+ }
+
+ int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
+ int soTimeout = HttpConnectionParams.getSoTimeout(params);
+
+ InetSocketAddress remoteAddress;
+ if (this.nameResolver != null) {
+ remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port);
+ } else {
+ remoteAddress = new InetSocketAddress(host, port);
+ }
+
+ sslsock.connect(remoteAddress, connTimeout);
+
+ sslsock.setSoTimeout(soTimeout);
+ try {
+ hostnameVerifier.verify(host, sslsock);
+ // verifyHostName() didn't blowup - good!
+ } catch (IOException iox) {
+ // close the socket before re-throwing the exception
+ try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
+ throw iox;
+ }
+
+ return sslsock;
+ }
+
+
+ /**
+ * Checks whether a socket connection is secure.
+ * This factory creates TLS/SSL socket connections
+ * which, by default, are considered secure.
+ * <br/>
+ * Derived classes may override this method to perform
+ * runtime checks, for example based on the cypher suite.
+ *
+ * @param sock the connected socket
+ *
+ * @return <code>true</code>
+ *
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public boolean isSecure(Socket sock)
+ throws IllegalArgumentException {
+
+ if (sock == null) {
+ throw new IllegalArgumentException("Socket may not be null.");
+ }
+ // This instanceof check is in line with createSocket() above.
+ if (!(sock instanceof SSLSocket)) {
+ throw new IllegalArgumentException
+ ("Socket not created by this factory.");
+ }
+ // This check is performed last since it calls the argument object.
+ if (sock.isClosed()) {
+ throw new IllegalArgumentException("Socket is closed.");
+ }
+
+ return true;
+
+ } // isSecure
+
+
+ // non-javadoc, see interface LayeredSocketFactory
+ public Socket createSocket(
+ final Socket socket,
+ final String host,
+ final int port,
+ final boolean autoClose
+ ) throws IOException, UnknownHostException {
+ SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
+ socket,
+ host,
+ port,
+ autoClose
+ );
+ hostnameVerifier.verify(host, sslSocket);
+ // verifyHostName() didn't blowup - good!
+ return sslSocket;
+ }
+
+ public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
+ if ( hostnameVerifier == null ) {
+ throw new IllegalArgumentException("Hostname verifier may not be null");
+ }
+ this.hostnameVerifier = hostnameVerifier;
+ }
+
+ public X509HostnameVerifier getHostnameVerifier() {
+ return hostnameVerifier;
+ }
+
+}
diff --git a/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java b/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java
new file mode 100644
index 0000000..5eb0d96
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java $
+ * $Revision: 617642 $
+ * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import javax.net.ssl.SSLException;
+
+/**
+ * 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.
+ * <p/>
+ * 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!).
+ * <p/>
+ * 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".
+ *
+ * @author Julius Davies
+ */
+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/src/org/apache/http/conn/ssl/X509HostnameVerifier.java b/src/org/apache/http/conn/ssl/X509HostnameVerifier.java
new file mode 100644
index 0000000..05ad04d
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/X509HostnameVerifier.java
@@ -0,0 +1,86 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/X509HostnameVerifier.java $
+ * $Revision: 618365 $
+ * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.ssl;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+/**
+ * Interface for checking if a hostname matches the names stored inside the
+ * server's X.509 certificate. Implements javax.net.ssl.HostnameVerifier, but
+ * we don't actually use that interface. Instead we added some methods that
+ * take String parameters (instead of javax.net.ssl.HostnameVerifier's
+ * SSLSession). JUnit is a lot easier this way! :-)
+ * <p/>
+ * We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and
+ * HostnameVerifier.ALLOW_ALL implementations. But feel free to define
+ * your own implementation!
+ * <p/>
+ * Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the
+ * HttpClient "contrib" repository.
+ *
+ * @author Julius Davies
+ * @author <a href="mailto:hauer@psicode.com">Sebastian Hauer</a>
+ *
+ * @since 4.0 (8-Dec-2006)
+ */
+public interface X509HostnameVerifier extends HostnameVerifier {
+
+ boolean verify(String host, SSLSession session);
+
+ void verify(String host, SSLSocket ssl) throws IOException;
+
+ 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 verification failed.
+ */
+ void verify(String host, String[] cns, String[] subjectAlts)
+ throws SSLException;
+
+
+}
diff --git a/src/org/apache/http/conn/ssl/package.html b/src/org/apache/http/conn/ssl/package.html
new file mode 100644
index 0000000..a5c737f
--- /dev/null
+++ b/src/org/apache/http/conn/ssl/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+TLS/SSL specific parts of the <i>HttpConn</i> API.
+
+</body>
+</html>
diff --git a/src/org/apache/http/conn/util/InetAddressUtils.java b/src/org/apache/http/conn/util/InetAddressUtils.java
new file mode 100644
index 0000000..71f2190
--- /dev/null
+++ b/src/org/apache/http/conn/util/InetAddressUtils.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/util/InetAddressUtils.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.util;
+
+import java.util.regex.Pattern;
+
+/**
+ * A collection of utilities relating to InetAddresses.
+ */
+public class InetAddressUtils {
+
+ private InetAddressUtils() {
+ }
+
+ private static final Pattern IPV4_PATTERN =
+ Pattern.compile(
+ "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
+
+ private static final Pattern IPV6_STD_PATTERN =
+ Pattern.compile(
+ "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
+
+ private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
+ Pattern.compile(
+ "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$");
+
+ public static boolean isIPv4Address(final String input) {
+ return IPV4_PATTERN.matcher(input).matches();
+ }
+
+ public static boolean isIPv6StdAddress(final String input) {
+ return IPV6_STD_PATTERN.matcher(input).matches();
+ }
+
+ public static boolean isIPv6HexCompressedAddress(final String input) {
+ return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
+ }
+
+ public static boolean isIPv6Address(final String input) {
+ return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
+ }
+
+}
diff --git a/src/org/apache/http/cookie/ClientCookie.java b/src/org/apache/http/cookie/ClientCookie.java
new file mode 100644
index 0000000..96edec9
--- /dev/null
+++ b/src/org/apache/http/cookie/ClientCookie.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/ClientCookie.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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 <tt>Cookie</tt>
+ * header because some cookie specifications require that the
+ * <tt>Cookie</tt> header should include certain attributes only if
+ * they were specified in the <tt>Set-Cookie</tt> header.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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);
+
+} \ No newline at end of file
diff --git a/src/org/apache/http/cookie/Cookie.java b/src/org/apache/http/cookie/Cookie.java
new file mode 100644
index 0000000..5eae9d5
--- /dev/null
+++ b/src/org/apache/http/cookie/Cookie.java
@@ -0,0 +1,139 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/Cookie.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.Date;
+
+/**
+ * HTTP "magic-cookie" represents a piece of state information
+ * that the HTTP agent and the target server can exchange to maintain
+ * a session.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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
+ * <tt>null</tt> 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 <tt>null</tt>
+ * if none exists.
+ * <p><strong>Note:</strong> 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. </p>
+ * @return Expiration {@link Date}, or <tt>null</tt>.
+ */
+ Date getExpiryDate();
+
+ /**
+ * Returns <tt>false</tt> if the cookie should be discarded at the end
+ * of the "session"; <tt>true</tt> otherwise.
+ *
+ * @return <tt>false</tt> if the cookie should be discarded at the end
+ * of the "session"; <tt>true</tt> otherwise
+ */
+ boolean isPersistent();
+
+ /**
+ * Returns domain attribute of the cookie.
+ *
+ * @return the value of the domain attribute
+ */
+ String getDomain();
+
+ /**
+ * Returns the path attribute of the cookie
+ *
+ * @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 <code>true</code> if this cookie should only be sent
+ * over secure connections, <code>false</code> 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 <tt>true</tt> if the cookie has expired.
+ */
+ boolean isExpired(final Date date);
+
+}
+
diff --git a/src/org/apache/http/cookie/CookieAttributeHandler.java b/src/org/apache/http/cookie/CookieAttributeHandler.java
new file mode 100644
index 0000000..a79d115
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieAttributeHandler.java
@@ -0,0 +1,78 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieAttributeHandler.java $
+ * $Revision: 558519 $
+ * $Date: 2007-07-22 11:19:49 -0700 (Sun, 22 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.cookie;
+
+/**
+ * Ths 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.
+ *
+ * @author jain.samit@gmail.com (Samit Jain)
+ *
+ * @since 4.0
+ */
+public interface CookieAttributeHandler {
+
+ /**
+ * Parse the given cookie attribute value and update the corresponding
+ * {@link org.apache.http.cookie.Cookie} property.
+ *
+ * @param cookie {@link org.apache.http.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 org.apache.http.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 org.apache.http.cookie.Cookie} to match
+ * @param origin the cookie source to match against
+ * @return <tt>true</tt> if the match is successful; <tt>false</tt> otherwise
+ */
+ boolean match(Cookie cookie, CookieOrigin origin);
+
+}
diff --git a/src/org/apache/http/cookie/CookieIdentityComparator.java b/src/org/apache/http/cookie/CookieIdentityComparator.java
new file mode 100644
index 0000000..4fc701c
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieIdentityComparator.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieIdentityComparator.java $
+ * $Revision: 618308 $
+ * $Date: 2008-02-04 07:51:19 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * This cookie comparator can be used to compare identity of cookies.
+ *
+ * <p>
+ * Cookies are considered identical if their names are equal and
+ * their domain attributes match ignoring case.
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class CookieIdentityComparator implements Serializable, Comparator<Cookie> {
+
+ 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 = "";
+ }
+ String d2 = c2.getDomain();
+ if (d2 == null) {
+ d2 = "";
+ }
+ res = d1.compareToIgnoreCase(d2);
+ }
+ return res;
+ }
+
+}
diff --git a/src/org/apache/http/cookie/CookieOrigin.java b/src/org/apache/http/cookie/CookieOrigin.java
new file mode 100644
index 0000000..ad0448a
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieOrigin.java
@@ -0,0 +1,108 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieOrigin.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.cookie;
+
+import java.util.Locale;
+
+/**
+ * CookieOrigin class incapsulates details of an origin server that
+ * are relevant when parsing, validating or matching HTTP cookies.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+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, int port, final String path, boolean secure) {
+ super();
+ if (host == null) {
+ throw new IllegalArgumentException(
+ "Host of origin may not be null");
+ }
+ if (host.trim().length() == 0) {
+ throw new IllegalArgumentException(
+ "Host of origin may not be blank");
+ }
+ if (port < 0) {
+ throw new IllegalArgumentException("Invalid port: " + port);
+ }
+ if (path == null) {
+ throw new IllegalArgumentException(
+ "Path of origin may not be null.");
+ }
+ 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() {
+ 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/src/org/apache/http/cookie/CookiePathComparator.java b/src/org/apache/http/cookie/CookiePathComparator.java
new file mode 100644
index 0000000..f5f0a66
--- /dev/null
+++ b/src/org/apache/http/cookie/CookiePathComparator.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookiePathComparator.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * This cookie comparator ensures that multiple cookies satisfying
+ * a common criteria are ordered in the <tt>Cookie</tt> header such
+ * that those with more specific Path attributes precede those with
+ * less specific.
+ *
+ * <p>
+ * This comparator assumes that Path attributes of two cookies
+ * path-match a commmon request-URI. Otherwise, the result of the
+ * comparison is undefined.
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class CookiePathComparator implements Serializable, Comparator<Cookie> {
+
+ 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) {
+ String path1 = normalizePath(c1);
+ 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/src/org/apache/http/cookie/CookieSpec.java b/src/org/apache/http/cookie/CookieSpec.java
new file mode 100644
index 0000000..1eb9f26
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieSpec.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieSpec.java $
+ * $Revision: 603563 $
+ * $Date: 2007-12-12 03:17:55 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.List;
+
+import org.apache.http.Header;
+
+/**
+ * Defines the cookie management specification.
+ * <p>Cookie management specification must define
+ * <ul>
+ * <li> rules of parsing "Set-Cookie" header
+ * <li> rules of validation of parsed cookies
+ * <li> formatting of "Cookie" header
+ * </ul>
+ * for a given host, port and path of origin
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ *
+ * @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 <tt>"Set-Cookie"</tt> Header into an array of Cookies.
+ *
+ * <p>This method will not perform the validation of the resultant
+ * {@link Cookie}s</p>
+ *
+ * @see #validate
+ *
+ * @param header the <tt>Set-Cookie</tt> received from the server
+ * @param origin details of the cookie origin
+ * @return an array of <tt>Cookie</tt>s parsed from the header
+ * @throws MalformedCookieException if an exception occurs during parsing
+ */
+ List<Cookie> 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 <tt>true</tt> if the cookie should be submitted with a request
+ * with given attributes, <tt>false</tt> otherwise.
+ */
+ boolean match(Cookie cookie, CookieOrigin origin);
+
+ /**
+ * Create <tt>"Cookie"</tt> 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<Header> formatCookies(List<Cookie> cookies);
+
+ /**
+ * Returns a request header identifying what version of the state management
+ * specification is understood. May be <code>null</code> if the cookie
+ * specification does not support <tt>Cookie2</tt> header.
+ */
+ Header getVersionHeader();
+
+}
diff --git a/src/org/apache/http/cookie/CookieSpecFactory.java b/src/org/apache/http/cookie/CookieSpecFactory.java
new file mode 100644
index 0000000..9d5c21d
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieSpecFactory.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieSpecFactory.java $
+ * $Revision: 489636 $
+ * $Date: 2006-12-22 04:34:57 -0800 (Fri, 22 Dec 2006) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public interface CookieSpecFactory {
+
+ CookieSpec newInstance(HttpParams params);
+
+}
diff --git a/src/org/apache/http/cookie/CookieSpecRegistry.java b/src/org/apache/http/cookie/CookieSpecRegistry.java
new file mode 100644
index 0000000..64b9c8b
--- /dev/null
+++ b/src/org/apache/http/cookie/CookieSpecRegistry.java
@@ -0,0 +1,160 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/CookieSpecRegistry.java $
+ * $Revision: 652950 $
+ * $Date: 2008-05-02 16:49:48 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * Cookie specification registry that can be used to obtain the corresponding
+ * cookie specification implementation for a given type of type or version of
+ * cookie.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @since 4.0
+ */
+public final class CookieSpecRegistry {
+
+ private final Map<String,CookieSpecFactory> registeredSpecs;
+
+ public CookieSpecRegistry() {
+ super();
+ this.registeredSpecs = new LinkedHashMap<String,CookieSpecFactory>();
+ }
+
+ /**
+ * 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 synchronized void register(final String name, final CookieSpecFactory factory) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ if (factory == null) {
+ throw new IllegalArgumentException("Cookie spec factory may not be null");
+ }
+ 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 synchronized void unregister(final String id) {
+ if (id == null) {
+ throw new IllegalArgumentException("Id may not be null");
+ }
+ 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 synchronized CookieSpec getCookieSpec(final String name, final HttpParams params)
+ throws IllegalStateException {
+
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ 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 synchronized CookieSpec getCookieSpec(final String name)
+ throws IllegalStateException {
+ return getCookieSpec(name, null);
+ }
+
+ /**
+ * Obtains a list containing names of all registered {@link CookieSpec cookie
+ * specs} in their default order.
+ *
+ * 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 synchronized List<String> getSpecNames(){
+ return new ArrayList<String>(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 synchronized void setItems(final Map<String, CookieSpecFactory> map) {
+ if (map == null) {
+ return;
+ }
+ registeredSpecs.clear();
+ registeredSpecs.putAll(map);
+ }
+
+}
diff --git a/src/org/apache/http/cookie/MalformedCookieException.java b/src/org/apache/http/cookie/MalformedCookieException.java
new file mode 100644
index 0000000..e3f30a9
--- /dev/null
+++ b/src/org/apache/http/cookie/MalformedCookieException.java
@@ -0,0 +1,74 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/MalformedCookieException.java $
+ * $Revision: 508891 $
+ * $Date: 2007-02-18 02:08:48 -0800 (Sun, 18 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import org.apache.http.ProtocolException;
+
+/**
+ * Signals that a cookie is in some way invalid or illegal in a given
+ * context
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class MalformedCookieException extends ProtocolException {
+
+ private static final long serialVersionUID = -6695462944287282185L;
+
+ /**
+ * Creates a new MalformedCookieException with a <tt>null</tt> detail message.
+ */
+ public MalformedCookieException() {
+ super();
+ }
+
+ /**
+ * Creates a new MalformedCookieException with a specified message string.
+ *
+ * @param message The exception detail message
+ */
+ public MalformedCookieException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new MalformedCookieException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public MalformedCookieException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/cookie/SM.java b/src/org/apache/http/cookie/SM.java
new file mode 100644
index 0000000..a7047d5
--- /dev/null
+++ b/src/org/apache/http/cookie/SM.java
@@ -0,0 +1,48 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/SM.java $
+ * $Revision: 582602 $
+ * $Date: 2007-10-07 02:35:48 -0700 (Sun, 07 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+/**
+ * Constants and static helpers related to the HTTP state management.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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/src/org/apache/http/cookie/SetCookie.java b/src/org/apache/http/cookie/SetCookie.java
new file mode 100644
index 0000000..d207c48
--- /dev/null
+++ b/src/org/apache/http/cookie/SetCookie.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/SetCookie.java $
+ * $Revision: 617193 $
+ * $Date: 2008-01-31 11:26:47 -0800 (Thu, 31 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+import java.util.Date;
+
+/**
+ * This interface represents a <code>SetCookie</code> response header sent by the
+ * origin server to the HTTP agent in order to maintain a conversational state.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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.
+ * <p><strong>Note:</strong> 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.</p>
+ *
+ * @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.
+ * <p>
+ * When <tt>true</tt> 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/src/org/apache/http/cookie/SetCookie2.java b/src/org/apache/http/cookie/SetCookie2.java
new file mode 100644
index 0000000..cd0420e
--- /dev/null
+++ b/src/org/apache/http/cookie/SetCookie2.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/SetCookie2.java $
+ * $Revision: 578408 $
+ * $Date: 2007-09-22 04:53:57 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie;
+
+/**
+ * This interface represents a <code>SetCookie2</code> response header sent by the
+ * origin server to the HTTP agent in order to maintain a conversational state.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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: <tt>Discard</tt> attribute overrides <tt>Max-age</tt>.
+ *
+ * @see #isPersistent()
+ */
+ void setDiscard(boolean discard);
+
+}
+
diff --git a/src/org/apache/http/cookie/package.html b/src/org/apache/http/cookie/package.html
new file mode 100644
index 0000000..891d9c3
--- /dev/null
+++ b/src/org/apache/http/cookie/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The API for client-side state management via cookies,
+commonly referred to as <i>HttpCookie</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/cookie/params/CookieSpecPNames.java b/src/org/apache/http/cookie/params/CookieSpecPNames.java
new file mode 100644
index 0000000..6a6f6d0
--- /dev/null
+++ b/src/org/apache/http/cookie/params/CookieSpecPNames.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/params/CookieSpecPNames.java $
+ * $Revision: 578403 $
+ * $Date: 2007-09-22 03:56:04 -0700 (Sat, 22 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie.params;
+
+/**
+ * Parameter names for cookie specifications in HttpCookie.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 578403 $
+ *
+ * @since 4.0
+ */
+public interface CookieSpecPNames {
+
+ /**
+ * Parameter for the date patterns used for parsing.
+ * <p>
+ * This parameter expects a value of type {@link java.util.Collection}.
+ * The collection elements are of type {@link String}
+ * and must be compatible with the syntax of
+ * {@link java.text.SimpleDateFormat}.
+ * </p>
+ */
+ public static final String DATE_PATTERNS = "http.protocol.cookie-datepatterns";
+
+ /**
+ * Parameter for Cookie header formatting.
+ * Defines whether {@link org.apache.http.cookie.Cookie cookies}
+ * should be put on
+ * a single {@link org.apache.http.Header request header}.
+ * If not, each cookie is formatted in a seperate Cookie header.
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ public static final String SINGLE_COOKIE_HEADER = "http.protocol.single-cookie-header";
+
+}
diff --git a/src/org/apache/http/cookie/params/CookieSpecParamBean.java b/src/org/apache/http/cookie/params/CookieSpecParamBean.java
new file mode 100644
index 0000000..6016022
--- /dev/null
+++ b/src/org/apache/http/cookie/params/CookieSpecParamBean.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/params/CookieSpecParamBean.java $
+ * $Revision: 632313 $
+ * $Date: 2008-02-29 05:19:50 -0800 (Fri, 29 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.cookie.params;
+
+import java.util.Collection;
+
+import org.apache.http.params.HttpAbstractParamBean;
+import org.apache.http.params.HttpParams;
+
+public class CookieSpecParamBean extends HttpAbstractParamBean {
+
+ public CookieSpecParamBean (final HttpParams params) {
+ super(params);
+ }
+
+ public void setDatePatterns (final Collection <String> patterns) {
+ params.setParameter(CookieSpecPNames.DATE_PATTERNS, patterns);
+ }
+
+ public void setSingleHeader (final boolean singleHeader) {
+ params.setBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, singleHeader);
+ }
+
+}
diff --git a/src/org/apache/http/cookie/params/package.html b/src/org/apache/http/cookie/params/package.html
new file mode 100644
index 0000000..e6fb7cd
--- /dev/null
+++ b/src/org/apache/http/cookie/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/cookie/params/package.html $
+ * $Revision: 555193 $
+ * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Parameters for configuring <i>HttpCookie</i>.
+
+</body>
+</html>
diff --git a/src/org/apache/http/entity/AbstractHttpEntity.java b/src/org/apache/http/entity/AbstractHttpEntity.java
new file mode 100644
index 0000000..0fce6eb
--- /dev/null
+++ b/src/org/apache/http/entity/AbstractHttpEntity.java
@@ -0,0 +1,213 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/AbstractHttpEntity.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Abstract base class for entities.
+ * Provides the commonly used attributes for streamed and self-contained
+ * implementations of {@link HttpEntity HttpEntity}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractHttpEntity implements HttpEntity {
+
+ /**
+ * The Content-Type header.
+ * Returned by {@link #getContentType getContentType},
+ * unless that method is overridden.
+ */
+ protected Header contentType;
+
+ /**
+ * The Content-Encoding header.
+ * Returned by {@link #getContentEncoding getContentEncoding},
+ * unless that method is overridden.
+ */
+ protected Header contentEncoding;
+
+ /**
+ * The 'chunked' flag.
+ * Returned by {@link #isChunked isChunked},
+ * unless that method is overridden.
+ */
+ protected boolean chunked;
+
+
+ /**
+ * Protected default constructor.
+ * The attributes of the created object remain
+ * <code>null</code> and <code>false</code>, 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 <code>null</code>
+ */
+ 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 <code>null</code>
+ */
+ 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
+ * <code>null</code> 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
+ * <code>null</code> 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
+ * <code>null</code> 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
+ * <code>null</code> 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.
+ * The default implementation sets the value of the
+ * {@link #chunked chunked} attribute.
+ *
+ * @param b the new 'chunked' flag
+ */
+ public void setChunked(boolean b) {
+ this.chunked = b;
+ }
+
+
+ /**
+ * Does not consume anything.
+ * The default implementation does nothing if
+ * {@link HttpEntity#isStreaming isStreaming}
+ * returns <code>false</code>, and throws an exception
+ * if it returns <code>true</code>.
+ * This removes the burden of implementing
+ * an empty method for non-streaming entities.
+ *
+ * @throws IOException in case of an I/O problem
+ * @throws UnsupportedOperationException
+ * if a streaming subclass does not override this method
+ */
+ public void consumeContent()
+ throws IOException, UnsupportedOperationException{
+ if (isStreaming()) {
+ throw new UnsupportedOperationException
+ ("streaming entity does not implement consumeContent()");
+ }
+ } // consumeContent
+
+
+} // class AbstractHttpEntity
diff --git a/src/org/apache/http/entity/BasicHttpEntity.java b/src/org/apache/http/entity/BasicHttpEntity.java
new file mode 100644
index 0000000..df3c07c
--- /dev/null
+++ b/src/org/apache/http/entity/BasicHttpEntity.java
@@ -0,0 +1,146 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/BasicHttpEntity.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A generic streamed entity being received on a connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public class BasicHttpEntity extends AbstractHttpEntity {
+
+ private InputStream content;
+ private boolean contentObtained;
+ 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;
+ }
+
+ // non-javadoc, see interface HttpEntity
+ 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 been obtained before, or
+ * has not yet been provided
+ */
+ public InputStream getContent()
+ throws IllegalStateException {
+ if (this.content == null) {
+ throw new IllegalStateException("Content has not been provided");
+ }
+ if (this.contentObtained) {
+ throw new IllegalStateException("Content has been consumed");
+ }
+ this.contentObtained = true;
+ return this.content;
+
+ } // getContent
+
+ /**
+ * Tells that this entity is not repeatable.
+ *
+ * @return <code>false</code>
+ */
+ 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(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;
+ this.contentObtained = false;
+ }
+
+ // non-javadoc, see interface HttpEntity
+ public void writeTo(final OutputStream outstream) throws IOException {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ InputStream instream = getContent();
+ int l;
+ byte[] tmp = new byte[2048];
+ while ((l = instream.read(tmp)) != -1) {
+ outstream.write(tmp, 0, l);
+ }
+ }
+
+ // non-javadoc, see interface HttpEntity
+ public boolean isStreaming() {
+ return !this.contentObtained && this.content != null;
+ }
+
+ // non-javadoc, see interface HttpEntity
+ public void consumeContent() throws IOException {
+ if (content != null) {
+ content.close(); // reads to the end of the entity
+ }
+ }
+
+} // class BasicHttpEntity
diff --git a/src/org/apache/http/entity/BufferedHttpEntity.java b/src/org/apache/http/entity/BufferedHttpEntity.java
new file mode 100644
index 0000000..9888797
--- /dev/null
+++ b/src/org/apache/http/entity/BufferedHttpEntity.java
@@ -0,0 +1,120 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/BufferedHttpEntity.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.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.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public class BufferedHttpEntity extends HttpEntityWrapper {
+
+ private final byte[] buffer;
+
+ public BufferedHttpEntity(final HttpEntity entity) throws IOException {
+ super(entity);
+ if (!entity.isRepeatable() || entity.getContentLength() < 0) {
+ this.buffer = EntityUtils.toByteArray(entity);
+ } else {
+ this.buffer = null;
+ }
+ }
+
+ public long getContentLength() {
+ if (this.buffer != null) {
+ return this.buffer.length;
+ } else {
+ return wrappedEntity.getContentLength();
+ }
+ }
+
+ public InputStream getContent() throws IOException {
+ if (this.buffer != null) {
+ return new ByteArrayInputStream(this.buffer);
+ } else {
+ return wrappedEntity.getContent();
+ }
+ }
+
+ /**
+ * Tells that this entity does not have to be chunked.
+ *
+ * @return <code>false</code>
+ */
+ public boolean isChunked() {
+ return (buffer == null) && wrappedEntity.isChunked();
+ }
+
+ /**
+ * Tells that this entity is repeatable.
+ *
+ * @return <code>true</code>
+ */
+ public boolean isRepeatable() {
+ return true;
+ }
+
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ if (this.buffer != null) {
+ outstream.write(this.buffer);
+ } else {
+ wrappedEntity.writeTo(outstream);
+ }
+ }
+
+
+ // non-javadoc, see interface HttpEntity
+ public boolean isStreaming() {
+ return (buffer == null) && wrappedEntity.isStreaming();
+ }
+
+} // class BufferedHttpEntity
diff --git a/src/org/apache/http/entity/ByteArrayEntity.java b/src/org/apache/http/entity/ByteArrayEntity.java
new file mode 100644
index 0000000..c7257f7
--- /dev/null
+++ b/src/org/apache/http/entity/ByteArrayEntity.java
@@ -0,0 +1,94 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/ByteArrayEntity.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * An entity whose content is retrieved from a byte array.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 604625 $
+ *
+ * @since 4.0
+ */
+public class ByteArrayEntity extends AbstractHttpEntity implements Cloneable {
+
+ protected final byte[] content;
+
+ public ByteArrayEntity(final byte[] b) {
+ super();
+ if (b == null) {
+ throw new IllegalArgumentException("Source byte array may not be null");
+ }
+ this.content = b;
+ }
+
+ public boolean isRepeatable() {
+ return true;
+ }
+
+ public long getContentLength() {
+ return this.content.length;
+ }
+
+ public InputStream getContent() {
+ return new ByteArrayInputStream(this.content);
+ }
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ outstream.write(this.content);
+ outstream.flush();
+ }
+
+
+ /**
+ * Tells that this entity is not streaming.
+ *
+ * @return <code>false</code>
+ */
+ public boolean isStreaming() {
+ return false;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+} // class ByteArrayEntity
diff --git a/src/org/apache/http/entity/ContentLengthStrategy.java b/src/org/apache/http/entity/ContentLengthStrategy.java
new file mode 100644
index 0000000..cc4ab7d
--- /dev/null
+++ b/src/org/apache/http/entity/ContentLengthStrategy.java
@@ -0,0 +1,54 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/ContentLengthStrategy.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+
+/**
+ * Represents a strategy to determine the content length based on the properties
+ * of an HTTP message.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ *
+ * @since 4.0
+ */
+public interface ContentLengthStrategy {
+
+ public static final int IDENTITY = -1;
+ public static final int CHUNKED = -2;
+
+ long determineLength(HttpMessage message) throws HttpException;
+
+}
diff --git a/src/org/apache/http/entity/ContentProducer.java b/src/org/apache/http/entity/ContentProducer.java
new file mode 100644
index 0000000..456eae3
--- /dev/null
+++ b/src/org/apache/http/entity/ContentProducer.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/ContentProducer.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An abstract entity content producer.
+ *
+ *<p>Content producers are expected to be able to produce their
+ * content multiple times</p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public interface ContentProducer {
+
+ void writeTo(OutputStream outstream) throws IOException;
+
+}
diff --git a/src/org/apache/http/entity/EntityTemplate.java b/src/org/apache/http/entity/EntityTemplate.java
new file mode 100644
index 0000000..0c6002e
--- /dev/null
+++ b/src/org/apache/http/entity/EntityTemplate.java
@@ -0,0 +1,86 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/EntityTemplate.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Entity that delegates the process of content generation to an abstract
+ * content producer.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public class EntityTemplate extends AbstractHttpEntity {
+
+ private final ContentProducer contentproducer;
+
+ public EntityTemplate(final ContentProducer contentproducer) {
+ super();
+ if (contentproducer == null) {
+ throw new IllegalArgumentException("Content producer may not be null");
+ }
+ this.contentproducer = contentproducer;
+ }
+
+ public long getContentLength() {
+ return -1;
+ }
+
+ public InputStream getContent() {
+ throw new UnsupportedOperationException("Entity template does not implement getContent()");
+ }
+
+ public boolean isRepeatable() {
+ return true;
+ }
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ this.contentproducer.writeTo(outstream);
+ }
+
+ public boolean isStreaming() {
+ return true;
+ }
+
+ public void consumeContent() throws IOException {
+ }
+
+}
diff --git a/src/org/apache/http/entity/FileEntity.java b/src/org/apache/http/entity/FileEntity.java
new file mode 100644
index 0000000..a991058
--- /dev/null
+++ b/src/org/apache/http/entity/FileEntity.java
@@ -0,0 +1,106 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/FileEntity.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * An entity whose content is retrieved from a file.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 604625 $
+ *
+ * @since 4.0
+ */
+public class FileEntity extends AbstractHttpEntity implements Cloneable {
+
+ protected final File file;
+
+ public FileEntity(final File file, final String contentType) {
+ super();
+ if (file == null) {
+ throw new IllegalArgumentException("File may not be null");
+ }
+ this.file = file;
+ setContentType(contentType);
+ }
+
+ 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 {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ InputStream instream = new FileInputStream(this.file);
+ try {
+ byte[] tmp = new byte[4096];
+ 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 <code>false</code>
+ */
+ public boolean isStreaming() {
+ return false;
+ }
+
+ 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/src/org/apache/http/entity/HttpEntityWrapper.java b/src/org/apache/http/entity/HttpEntityWrapper.java
new file mode 100644
index 0000000..17a4149
--- /dev/null
+++ b/src/org/apache/http/entity/HttpEntityWrapper.java
@@ -0,0 +1,113 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/HttpEntityWrapper.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+
+/**
+ * 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.
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public class HttpEntityWrapper implements HttpEntity {
+
+ /** The wrapped entity. */
+ protected HttpEntity wrappedEntity;
+
+ /**
+ * Creates a new entity wrapper.
+ *
+ * @param wrapped the entity to wrap
+ */
+ public HttpEntityWrapper(HttpEntity wrapped) {
+ super();
+
+ if (wrapped == null) {
+ throw new IllegalArgumentException
+ ("wrapped entity must not be null");
+ }
+ wrappedEntity = wrapped;
+
+ } // 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(OutputStream outstream)
+ throws IOException {
+ wrappedEntity.writeTo(outstream);
+ }
+
+ public boolean isStreaming() {
+ return wrappedEntity.isStreaming();
+ }
+
+ public void consumeContent()
+ throws IOException {
+ wrappedEntity.consumeContent();
+ }
+
+} // class HttpEntityWrapper
diff --git a/src/org/apache/http/entity/InputStreamEntity.java b/src/org/apache/http/entity/InputStreamEntity.java
new file mode 100644
index 0000000..6d33fe4
--- /dev/null
+++ b/src/org/apache/http/entity/InputStreamEntity.java
@@ -0,0 +1,116 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/InputStreamEntity.java $
+ * $Revision: 617591 $
+ * $Date: 2008-02-01 10:21:17 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A streamed entity obtaining content from an {@link InputStream InputStream}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 617591 $
+ *
+ * @since 4.0
+ */
+public class InputStreamEntity extends AbstractHttpEntity {
+
+ private final static int BUFFER_SIZE = 2048;
+
+ private final InputStream content;
+ private final long length;
+ private boolean consumed = false;
+
+ public InputStreamEntity(final InputStream instream, long length) {
+ super();
+ if (instream == null) {
+ throw new IllegalArgumentException("Source input stream may not be null");
+ }
+ this.content = instream;
+ this.length = length;
+ }
+
+ public boolean isRepeatable() {
+ return false;
+ }
+
+ public long getContentLength() {
+ return this.length;
+ }
+
+ public InputStream getContent() throws IOException {
+ return this.content;
+ }
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ InputStream instream = this.content;
+ byte[] buffer = new byte[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(BUFFER_SIZE, remaining));
+ if (l == -1) {
+ break;
+ }
+ outstream.write(buffer, 0, l);
+ remaining -= l;
+ }
+ }
+ this.consumed = true;
+ }
+
+ // non-javadoc, see interface HttpEntity
+ public boolean isStreaming() {
+ return !this.consumed;
+ }
+
+ // non-javadoc, see interface HttpEntity
+ public void consumeContent() throws IOException {
+ this.consumed = true;
+ // If the input stream is from a connection, closing it will read to
+ // the end of the content. Otherwise, we don't care what it does.
+ this.content.close();
+ }
+
+} // class InputStreamEntity
diff --git a/src/org/apache/http/entity/SerializableEntity.java b/src/org/apache/http/entity/SerializableEntity.java
new file mode 100644
index 0000000..171977b
--- /dev/null
+++ b/src/org/apache/http/entity/SerializableEntity.java
@@ -0,0 +1,107 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/SerializableEntity.java $
+ * $Revision: 647816 $
+ * $Date: 2008-04-14 07:37:13 -0700 (Mon, 14 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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;
+
+public class SerializableEntity extends AbstractHttpEntity {
+
+ private byte[] objSer;
+
+ private Serializable objRef;
+
+ public SerializableEntity(Serializable ser, boolean bufferize) throws IOException {
+ super();
+ if (ser == null) {
+ throw new IllegalArgumentException("Source object may not be null");
+ }
+
+ if (bufferize) {
+ createBytes(ser);
+ } else {
+ this.objRef = ser;
+ }
+ }
+
+ private void createBytes(Serializable ser) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ 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(OutputStream outstream) throws IOException {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+
+ if (this.objSer == null) {
+ ObjectOutputStream out = new ObjectOutputStream(outstream);
+ out.writeObject(this.objRef);
+ out.flush();
+ } else {
+ outstream.write(this.objSer);
+ outstream.flush();
+ }
+ }
+
+}
diff --git a/src/org/apache/http/entity/StringEntity.java b/src/org/apache/http/entity/StringEntity.java
new file mode 100644
index 0000000..cbc382b
--- /dev/null
+++ b/src/org/apache/http/entity/StringEntity.java
@@ -0,0 +1,106 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/StringEntity.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.http.protocol.HTTP;
+
+/**
+ * An entity whose content is retrieved from a string.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618367 $
+ *
+ * @since 4.0
+ */
+public class StringEntity extends AbstractHttpEntity implements Cloneable {
+
+ protected final byte[] content;
+
+ public StringEntity(final String s, String charset)
+ throws UnsupportedEncodingException {
+ super();
+ if (s == null) {
+ throw new IllegalArgumentException("Source string may not be null");
+ }
+ if (charset == null) {
+ charset = HTTP.DEFAULT_CONTENT_CHARSET;
+ }
+ this.content = s.getBytes(charset);
+ setContentType(HTTP.PLAIN_TEXT_TYPE + HTTP.CHARSET_PARAM + charset);
+ }
+
+ public StringEntity(final String s)
+ throws UnsupportedEncodingException {
+ this(s, null);
+ }
+
+ 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 {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ outstream.write(this.content);
+ outstream.flush();
+ }
+
+ /**
+ * Tells that this entity is not streaming.
+ *
+ * @return <code>false</code>
+ */
+ public boolean isStreaming() {
+ return false;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+} // class StringEntity
diff --git a/src/org/apache/http/entity/package.html b/src/org/apache/http/entity/package.html
new file mode 100644
index 0000000..11d491e
--- /dev/null
+++ b/src/org/apache/http/entity/package.html
@@ -0,0 +1,58 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/entity/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Representations for HTTP message entities.
+
+An {@link org.apache.http.HttpEntity entity} is the optional content of a
+{@link org.apache.http.HttpMessage message}.
+You'll find a basic selection of entity implementations here.
+If you need to send an entity, you can provide it for example as a
+{@link org.apache.http.entity.ByteArrayEntity byte array},
+{@link org.apache.http.entity.StringEntity string},
+{@link org.apache.http.entity.FileEntity file}, or through an arbitrary
+{@link org.apache.http.entity.InputStreamEntity input stream}.
+If you receive a message with an entity, you typically get that as a
+{@link org.apache.http.entity.BasicHttpEntity basic} entity.
+Entity implementations can be
+{@link org.apache.http.entity.HttpEntityWrapper wrapped},
+for example to
+{@link org.apache.http.entity.BufferedHttpEntity buffer}
+the content in memory.
+
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/AbstractHttpClientConnection.java b/src/org/apache/http/impl/AbstractHttpClientConnection.java
new file mode 100644
index 0000000..ebfaabb
--- /dev/null
+++ b/src/org/apache/http/impl/AbstractHttpClientConnection.java
@@ -0,0 +1,212 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/AbstractHttpClientConnection.java $
+ * $Revision: 627457 $
+ * $Date: 2008-02-13 07:14:19 -0800 (Wed, 13 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.impl.entity.EntityDeserializer;
+import org.apache.http.impl.entity.EntitySerializer;
+import org.apache.http.impl.entity.LaxContentLengthStrategy;
+import org.apache.http.impl.entity.StrictContentLengthStrategy;
+import org.apache.http.impl.io.HttpRequestWriter;
+import org.apache.http.impl.io.HttpResponseParser;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.HttpMessageWriter;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Abstract client-side HTTP connection capable of transmitting and receiving data
+ * using arbitrary {@link SessionInputBuffer} and {@link SessionOutputBuffer}
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 627457 $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractHttpClientConnection implements HttpClientConnection {
+
+ private final EntitySerializer entityserializer;
+ private final EntityDeserializer entitydeserializer;
+
+ private SessionInputBuffer inbuffer = null;
+ private SessionOutputBuffer outbuffer = null;
+ private HttpMessageParser responseParser = null;
+ private HttpMessageWriter requestWriter = null;
+ private HttpConnectionMetricsImpl metrics = null;
+
+
+
+ public AbstractHttpClientConnection() {
+ super();
+ this.entityserializer = createEntitySerializer();
+ this.entitydeserializer = createEntityDeserializer();
+ }
+
+ protected abstract void assertOpen() throws IllegalStateException;
+
+ protected EntityDeserializer createEntityDeserializer() {
+ return new EntityDeserializer(new LaxContentLengthStrategy());
+ }
+
+ protected EntitySerializer createEntitySerializer() {
+ return new EntitySerializer(new StrictContentLengthStrategy());
+ }
+
+ protected HttpResponseFactory createHttpResponseFactory() {
+ return new DefaultHttpResponseFactory();
+ }
+
+ protected HttpMessageParser createResponseParser(
+ final SessionInputBuffer buffer,
+ final HttpResponseFactory responseFactory,
+ final HttpParams params) {
+ // override in derived class to specify a line parser
+ return new HttpResponseParser(buffer, null, responseFactory, params);
+ }
+
+ protected HttpMessageWriter createRequestWriter(
+ final SessionOutputBuffer buffer,
+ final HttpParams params) {
+ // override in derived class to specify a line formatter
+ return new HttpRequestWriter(buffer, null, params);
+ }
+
+ protected void init(
+ final SessionInputBuffer inbuffer,
+ final SessionOutputBuffer outbuffer,
+ final HttpParams params) {
+ if (inbuffer == null) {
+ throw new IllegalArgumentException("Input session buffer may not be null");
+ }
+ if (outbuffer == null) {
+ throw new IllegalArgumentException("Output session buffer may not be null");
+ }
+ this.inbuffer = inbuffer;
+ this.outbuffer = outbuffer;
+ this.responseParser = createResponseParser(
+ inbuffer,
+ createHttpResponseFactory(),
+ params);
+ this.requestWriter = createRequestWriter(
+ outbuffer, params);
+ this.metrics = new HttpConnectionMetricsImpl(
+ inbuffer.getMetrics(),
+ outbuffer.getMetrics());
+ }
+
+ public boolean isResponseAvailable(int timeout) throws IOException {
+ assertOpen();
+ return this.inbuffer.isDataAvailable(timeout);
+ }
+
+ public void sendRequestHeader(final HttpRequest request)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ assertOpen();
+ this.requestWriter.write(request);
+ this.metrics.incrementRequestCount();
+ }
+
+ public void sendRequestEntity(final HttpEntityEnclosingRequest request)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ 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();
+ HttpResponse response = (HttpResponse) this.responseParser.parse();
+ if (response.getStatusLine().getStatusCode() >= 200) {
+ this.metrics.incrementResponseCount();
+ }
+ return response;
+ }
+
+ public void receiveResponseEntity(final HttpResponse response)
+ throws HttpException, IOException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ assertOpen();
+ HttpEntity entity = this.entitydeserializer.deserialize(this.inbuffer, response);
+ response.setEntity(entity);
+ }
+
+ public boolean isStale() {
+ if (!isOpen()) {
+ return true;
+ }
+ try {
+ this.inbuffer.isDataAvailable(1);
+ return false;
+ } catch (IOException ex) {
+ return true;
+ }
+ }
+
+ public HttpConnectionMetrics getMetrics() {
+ return this.metrics;
+ }
+
+}
diff --git a/src/org/apache/http/impl/AbstractHttpServerConnection.java b/src/org/apache/http/impl/AbstractHttpServerConnection.java
new file mode 100644
index 0000000..ef68ed3
--- /dev/null
+++ b/src/org/apache/http/impl/AbstractHttpServerConnection.java
@@ -0,0 +1,202 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/AbstractHttpServerConnection.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestFactory;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.impl.entity.EntityDeserializer;
+import org.apache.http.impl.entity.EntitySerializer;
+import org.apache.http.impl.entity.LaxContentLengthStrategy;
+import org.apache.http.impl.entity.StrictContentLengthStrategy;
+import org.apache.http.impl.io.HttpRequestParser;
+import org.apache.http.impl.io.HttpResponseWriter;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.HttpMessageWriter;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Abstract server-side HTTP connection capable of transmitting and receiving data
+ * using arbitrary {@link SessionInputBuffer} and {@link SessionOutputBuffer}
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractHttpServerConnection implements HttpServerConnection {
+
+ private final EntitySerializer entityserializer;
+ private final EntityDeserializer entitydeserializer;
+
+ private SessionInputBuffer inbuffer = null;
+ private SessionOutputBuffer outbuffer = null;
+ private HttpMessageParser requestParser = null;
+ private HttpMessageWriter responseWriter = null;
+ private HttpConnectionMetricsImpl metrics = null;
+
+
+
+ public AbstractHttpServerConnection() {
+ super();
+ this.entityserializer = createEntitySerializer();
+ this.entitydeserializer = createEntityDeserializer();
+ }
+
+ protected abstract void assertOpen() throws IllegalStateException;
+
+ protected EntityDeserializer createEntityDeserializer() {
+ return new EntityDeserializer(new LaxContentLengthStrategy());
+ }
+
+ protected EntitySerializer createEntitySerializer() {
+ return new EntitySerializer(new StrictContentLengthStrategy());
+ }
+
+ protected HttpRequestFactory createHttpRequestFactory() {
+ return new DefaultHttpRequestFactory();
+ }
+
+ protected HttpMessageParser createRequestParser(
+ final SessionInputBuffer buffer,
+ final HttpRequestFactory requestFactory,
+ final HttpParams params) {
+ // override in derived class to specify a line parser
+ return new HttpRequestParser(buffer, null, requestFactory, params);
+ }
+
+ protected HttpMessageWriter createResponseWriter(
+ final SessionOutputBuffer buffer,
+ final HttpParams params) {
+ // override in derived class to specify a line formatter
+ return new HttpResponseWriter(buffer, null, params);
+ }
+
+
+ protected void init(
+ final SessionInputBuffer inbuffer,
+ final SessionOutputBuffer outbuffer,
+ final HttpParams params) {
+ if (inbuffer == null) {
+ throw new IllegalArgumentException("Input session buffer may not be null");
+ }
+ if (outbuffer == null) {
+ throw new IllegalArgumentException("Output session buffer may not be null");
+ }
+ this.inbuffer = inbuffer;
+ this.outbuffer = outbuffer;
+ this.requestParser = createRequestParser(
+ inbuffer,
+ createHttpRequestFactory(),
+ params);
+ this.responseWriter = createResponseWriter(
+ outbuffer, params);
+ this.metrics = new HttpConnectionMetricsImpl(
+ inbuffer.getMetrics(),
+ outbuffer.getMetrics());
+ }
+
+ public HttpRequest receiveRequestHeader()
+ throws HttpException, IOException {
+ assertOpen();
+ HttpRequest request = (HttpRequest) this.requestParser.parse();
+ this.metrics.incrementRequestCount();
+ return request;
+ }
+
+ public void receiveRequestEntity(final HttpEntityEnclosingRequest request)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ assertOpen();
+ 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 {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ 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());
+ }
+
+ public boolean isStale() {
+ assertOpen();
+ try {
+ this.inbuffer.isDataAvailable(1);
+ return false;
+ } catch (IOException ex) {
+ return true;
+ }
+ }
+
+ public HttpConnectionMetrics getMetrics() {
+ return this.metrics;
+ }
+
+}
diff --git a/src/org/apache/http/impl/DefaultConnectionReuseStrategy.java b/src/org/apache/http/impl/DefaultConnectionReuseStrategy.java
new file mode 100644
index 0000000..da1d5fd
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultConnectionReuseStrategy.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultConnectionReuseStrategy.java $
+ * $Revision: 602537 $
+ * $Date: 2007-12-08 11:42:06 -0800 (Sat, 08 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpConnection;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.TokenIterator;
+import org.apache.http.message.BasicTokenIterator;
+
+/**
+ * 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.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 602537 $
+ *
+ * @since 4.0
+ */
+public class DefaultConnectionReuseStrategy
+ implements ConnectionReuseStrategy {
+
+ public DefaultConnectionReuseStrategy() {
+ super();
+ }
+
+ // see interface ConnectionReuseStrategy
+ public boolean keepAlive(final HttpResponse response,
+ final HttpContext context) {
+ if (response == null) {
+ throw new IllegalArgumentException
+ ("HTTP response may not be null.");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException
+ ("HTTP context may not be null.");
+ }
+
+ HttpConnection conn = (HttpConnection)
+ context.getAttribute(ExecutionContext.HTTP_CONNECTION);
+
+ if (conn != null && !conn.isOpen())
+ return false;
+ // do NOT check for stale connection, that is an expensive operation
+
+ // Check for a self-terminating entity. If the end of the entity will
+ // be indicated by closing the connection, there is no keep-alive.
+ HttpEntity entity = response.getEntity();
+ ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
+ if (entity != null) {
+ if (entity.getContentLength() < 0) {
+ if (!entity.isChunked() ||
+ ver.lessEquals(HttpVersion.HTTP_1_0)) {
+ // if the content length is not known and is not chunk
+ // encoded, the connection cannot be reused
+ 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 {
+ 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 (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(HeaderIterator hit) {
+ return new BasicTokenIterator(hit);
+ }
+}
diff --git a/src/org/apache/http/impl/DefaultHttpClientConnection.java b/src/org/apache/http/impl/DefaultHttpClientConnection.java
new file mode 100644
index 0000000..c0a96f5
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpClientConnection.java
@@ -0,0 +1,87 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpClientConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Default implementation of a client-side HTTP connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ *
+ * @since 4.0
+ */
+public class DefaultHttpClientConnection extends SocketHttpClientConnection {
+
+ public DefaultHttpClientConnection() {
+ super();
+ }
+
+ public void bind(
+ final Socket socket,
+ final HttpParams params) throws IOException {
+ if (socket == null) {
+ throw new IllegalArgumentException("Socket may not be null");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ assertNotOpen();
+ socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
+ socket.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
+
+ int linger = HttpConnectionParams.getLinger(params);
+ if (linger >= 0) {
+ socket.setSoLinger(linger > 0, linger);
+ }
+ super.bind(socket, params);
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("[");
+ if (isOpen()) {
+ buffer.append(getRemotePort());
+ } else {
+ buffer.append("closed");
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+}
diff --git a/src/org/apache/http/impl/DefaultHttpRequestFactory.java b/src/org/apache/http/impl/DefaultHttpRequestFactory.java
new file mode 100644
index 0000000..dee36c9
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpRequestFactory.java
@@ -0,0 +1,113 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpRequestFactory.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestFactory;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.RequestLine;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.message.BasicHttpRequest;
+
+/**
+ * Default implementation of a factory for creating request objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618367 $
+ *
+ * @since 4.0
+ */
+public class DefaultHttpRequestFactory implements HttpRequestFactory {
+
+ 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"
+ };
+
+
+ public DefaultHttpRequestFactory() {
+ super();
+ }
+
+ private static boolean isOneOf(final String[] methods, final String method) {
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].equalsIgnoreCase(method)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public HttpRequest newHttpRequest(final RequestLine requestline)
+ throws MethodNotSupportedException {
+ if (requestline == null) {
+ throw new IllegalArgumentException("Request line may not be null");
+ }
+ 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/src/org/apache/http/impl/DefaultHttpResponseFactory.java b/src/org/apache/http/impl/DefaultHttpResponseFactory.java
new file mode 100644
index 0000000..40a2c9a
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpResponseFactory.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpResponseFactory.java $
+ * $Revision: 618367 $
+ * $Date: 2008-02-04 10:26:06 -0800 (Mon, 04 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.util.Locale;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.ReasonPhraseCatalog;
+import org.apache.http.impl.EnglishReasonPhraseCatalog;
+
+/**
+ * Default implementation of a factory for creating response objects.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618367 $
+ *
+ * @since 4.0
+ */
+public class DefaultHttpResponseFactory implements HttpResponseFactory {
+
+ /** 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(ReasonPhraseCatalog catalog) {
+ if (catalog == null) {
+ throw new IllegalArgumentException
+ ("Reason phrase catalog must not be null.");
+ }
+ this.reasonCatalog = catalog;
+ }
+
+ /**
+ * Creates a new response factory with the default catalog.
+ * The default catalog is
+ * {@link EnglishReasonPhraseCatalog EnglishReasonPhraseCatalog}.
+ */
+ public DefaultHttpResponseFactory() {
+ this(EnglishReasonPhraseCatalog.INSTANCE);
+ }
+
+
+ // non-javadoc, see interface HttpResponseFactory
+ public HttpResponse newHttpResponse(final ProtocolVersion ver,
+ final int status,
+ HttpContext context) {
+ if (ver == null) {
+ throw new IllegalArgumentException("HTTP version may not be null");
+ }
+ final Locale loc = determineLocale(context);
+ final String reason = reasonCatalog.getReason(status, loc);
+ StatusLine statusline = new BasicStatusLine(ver, status, reason);
+ return new BasicHttpResponse(statusline, reasonCatalog, loc);
+ }
+
+
+ // non-javadoc, see interface HttpResponseFactory
+ public HttpResponse newHttpResponse(final StatusLine statusline,
+ HttpContext context) {
+ if (statusline == null) {
+ throw new IllegalArgumentException("Status line may not be null");
+ }
+ final Locale loc = determineLocale(context);
+ return new BasicHttpResponse(statusline, reasonCatalog, loc);
+ }
+
+
+ /**
+ * 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
+ * <code>null</code> to use the default locale
+ *
+ * @return the locale for the response, never <code>null</code>
+ */
+ protected Locale determineLocale(HttpContext context) {
+ return Locale.getDefault();
+ }
+}
diff --git a/src/org/apache/http/impl/DefaultHttpServerConnection.java b/src/org/apache/http/impl/DefaultHttpServerConnection.java
new file mode 100644
index 0000000..d296fc8
--- /dev/null
+++ b/src/org/apache/http/impl/DefaultHttpServerConnection.java
@@ -0,0 +1,85 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/DefaultHttpServerConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Default implementation of a server-side HTTP connection.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ *
+ * @since 4.0
+ */
+public class DefaultHttpServerConnection extends SocketHttpServerConnection {
+
+ public DefaultHttpServerConnection() {
+ super();
+ }
+
+ public void bind(final Socket socket, final HttpParams params) throws IOException {
+ if (socket == null) {
+ throw new IllegalArgumentException("Socket may not be null");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ assertNotOpen();
+ socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
+ socket.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
+
+ int linger = HttpConnectionParams.getLinger(params);
+ if (linger >= 0) {
+ socket.setSoLinger(linger > 0, linger);
+ }
+ super.bind(socket, params);
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("[");
+ if (isOpen()) {
+ buffer.append(getRemotePort());
+ } else {
+ buffer.append("closed");
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+}
diff --git a/src/org/apache/http/impl/EnglishReasonPhraseCatalog.java b/src/org/apache/http/impl/EnglishReasonPhraseCatalog.java
new file mode 100644
index 0000000..f1aeee1
--- /dev/null
+++ b/src/org/apache/http/impl/EnglishReasonPhraseCatalog.java
@@ -0,0 +1,234 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/EnglishReasonPhraseCatalog.java $
+ * $Revision: 505744 $
+ * $Date: 2007-02-10 10:58:45 -0800 (Sat, 10 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.util.Locale;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.ReasonPhraseCatalog;
+
+
+/**
+ * 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.
+ *
+ * @author Unascribed
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ *
+ * @version $Revision: 505744 $
+ */
+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 <code>null</code>
+ */
+ public String getReason(int status, Locale loc) {
+ if ((status < 100) || (status >= 600)) {
+ throw new IllegalArgumentException
+ ("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(int status, 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/src/org/apache/http/impl/HttpConnectionMetricsImpl.java b/src/org/apache/http/impl/HttpConnectionMetricsImpl.java
new file mode 100644
index 0000000..4f4eacf
--- /dev/null
+++ b/src/org/apache/http/impl/HttpConnectionMetricsImpl.java
@@ -0,0 +1,146 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/HttpConnectionMetricsImpl.java $
+ * $Revision: 548031 $
+ * $Date: 2007-06-17 04:28:38 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.util.HashMap;
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.io.HttpTransportMetrics;
+
+/**
+ * Implementation of the metrics interface.
+ */
+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 HashMap 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 = new Long(requestCount);
+ } else if (RESPONSE_COUNT.equals(metricName)) {
+ value = new Long(responseCount);
+ } else if (RECEIVED_BYTES_COUNT.equals(metricName)) {
+ if (this.inTransportMetric != null) {
+ return new Long(this.inTransportMetric.getBytesTransferred());
+ } else {
+ return null;
+ }
+ } else if (SENT_BYTES_COUNT.equals(metricName)) {
+ if (this.outTransportMetric != null) {
+ return new Long(this.outTransportMetric.getBytesTransferred());
+ } else {
+ return null;
+ }
+ }
+ }
+ return value;
+ }
+
+ public void setMetric(final String metricName, 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/src/org/apache/http/impl/NoConnectionReuseStrategy.java b/src/org/apache/http/impl/NoConnectionReuseStrategy.java
new file mode 100644
index 0000000..c7a5f73
--- /dev/null
+++ b/src/org/apache/http/impl/NoConnectionReuseStrategy.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/NoConnectionReuseStrategy.java $
+ * $Revision: 502684 $
+ * $Date: 2007-02-02 10:25:38 -0800 (Fri, 02 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpResponse;
+import org.apache.http.protocol.HttpContext;
+
+
+/**
+ * A strategy that never re-uses a connection.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 502684 $
+ *
+ * @since 4.0
+ */
+public class NoConnectionReuseStrategy implements ConnectionReuseStrategy {
+
+ // default constructor
+
+
+ // non-JavaDoc, see interface ConnectionReuseStrategy
+ public boolean keepAlive(final HttpResponse response, final HttpContext context) {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/org/apache/http/impl/SocketHttpClientConnection.java b/src/org/apache/http/impl/SocketHttpClientConnection.java
new file mode 100644
index 0000000..1e551e0
--- /dev/null
+++ b/src/org/apache/http/impl/SocketHttpClientConnection.java
@@ -0,0 +1,208 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/SocketHttpClientConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+import org.apache.http.HttpInetConnection;
+import org.apache.http.impl.io.SocketInputBuffer;
+import org.apache.http.impl.io.SocketOutputBuffer;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Implementation of a client-side HTTP connection that can be bound to a
+ * network Socket in order to receive and transmit data.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ *
+ * @since 4.0
+ */
+public class SocketHttpClientConnection
+ extends AbstractHttpClientConnection implements HttpInetConnection {
+
+ private volatile boolean open;
+ private Socket socket = null;
+
+ public SocketHttpClientConnection() {
+ super();
+ }
+
+ protected void assertNotOpen() {
+ if (this.open) {
+ throw new IllegalStateException("Connection is already open");
+ }
+ }
+
+ protected void assertOpen() {
+ if (!this.open) {
+ throw new IllegalStateException("Connection is not open");
+ }
+ }
+
+ protected SessionInputBuffer createSessionInputBuffer(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ return new SocketInputBuffer(socket, buffersize, params);
+ }
+
+ protected SessionOutputBuffer createSessionOutputBuffer(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ return new SocketOutputBuffer(socket, buffersize, params);
+ }
+
+ protected void bind(
+ final Socket socket,
+ final HttpParams params) throws IOException {
+ if (socket == null) {
+ throw new IllegalArgumentException("Socket may not be null");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.socket = socket;
+
+ int buffersize = HttpConnectionParams.getSocketBufferSize(params);
+
+ 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(int timeout) {
+ assertOpen();
+ if (this.socket != null) {
+ try {
+ this.socket.setSoTimeout(timeout);
+ } catch (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 (SocketException ignore) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ public void shutdown() throws IOException {
+ this.open = false;
+ Socket tmpsocket = this.socket;
+ if (tmpsocket != null) {
+ tmpsocket.close();
+ }
+ }
+
+ public void close() throws IOException {
+ if (!this.open) {
+ return;
+ }
+ this.open = false;
+ doFlush();
+ try {
+ try {
+ this.socket.shutdownOutput();
+ } catch (IOException ignore) {
+ }
+ try {
+ this.socket.shutdownInput();
+ } catch (IOException ignore) {
+ }
+ } catch (UnsupportedOperationException ignore) {
+ // if one isn't supported, the other one isn't either
+ }
+ this.socket.close();
+ }
+
+}
diff --git a/src/org/apache/http/impl/SocketHttpServerConnection.java b/src/org/apache/http/impl/SocketHttpServerConnection.java
new file mode 100644
index 0000000..cfa2bf9
--- /dev/null
+++ b/src/org/apache/http/impl/SocketHttpServerConnection.java
@@ -0,0 +1,202 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/SocketHttpServerConnection.java $
+ * $Revision: 561083 $
+ * $Date: 2007-07-30 11:31:17 -0700 (Mon, 30 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+import org.apache.http.HttpInetConnection;
+import org.apache.http.impl.io.SocketInputBuffer;
+import org.apache.http.impl.io.SocketOutputBuffer;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Implementation of a server-side HTTP connection that can be bound to a
+ * network Socket in order to receive and transmit data.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 561083 $
+ *
+ * @since 4.0
+ */
+public class SocketHttpServerConnection extends
+ AbstractHttpServerConnection implements HttpInetConnection {
+
+ private volatile boolean open;
+ private Socket socket = null;
+
+ public SocketHttpServerConnection() {
+ super();
+ }
+
+ protected void assertNotOpen() {
+ if (this.open) {
+ throw new IllegalStateException("Connection is already open");
+ }
+ }
+
+ protected void assertOpen() {
+ if (!this.open) {
+ throw new IllegalStateException("Connection is not open");
+ }
+ }
+
+ protected SessionInputBuffer createHttpDataReceiver(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ return new SocketInputBuffer(socket, buffersize, params);
+ }
+
+ protected SessionOutputBuffer createHttpDataTransmitter(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ return new SocketOutputBuffer(socket, buffersize, params);
+ }
+
+ protected void bind(final Socket socket, final HttpParams params) throws IOException {
+ if (socket == null) {
+ throw new IllegalArgumentException("Socket may not be null");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.socket = socket;
+
+ int buffersize = HttpConnectionParams.getSocketBufferSize(params);
+
+ init(
+ createHttpDataReceiver(socket, buffersize, params),
+ createHttpDataTransmitter(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(int timeout) {
+ assertOpen();
+ if (this.socket != null) {
+ try {
+ this.socket.setSoTimeout(timeout);
+ } catch (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 (SocketException ignore) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ public void shutdown() throws IOException {
+ this.open = false;
+ Socket tmpsocket = this.socket;
+ if (tmpsocket != null) {
+ tmpsocket.close();
+ }
+ }
+
+ public void close() throws IOException {
+ if (!this.open) {
+ return;
+ }
+ this.open = false;
+ doFlush();
+ try {
+ this.socket.shutdownOutput();
+ } catch (IOException ignore) {
+ }
+ try {
+ this.socket.shutdownInput();
+ } catch (IOException ignore) {
+ }
+ this.socket.close();
+ }
+
+}
diff --git a/src/org/apache/http/impl/auth/AuthSchemeBase.java b/src/org/apache/http/impl/auth/AuthSchemeBase.java
new file mode 100644
index 0000000..689ce5d
--- /dev/null
+++ b/src/org/apache/http/impl/auth/AuthSchemeBase.java
@@ -0,0 +1,128 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java $
+ * $Revision: 653867 $
+ * $Date: 2008-05-06 11:17:29 -0700 (Tue, 06 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.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.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+*/
+public abstract class AuthSchemeBase implements AuthScheme {
+
+ /**
+ * Flag whether authenticating against a proxy.
+ */
+ private boolean proxy;
+
+ 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 {
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null");
+ }
+ String authheader = header.getName();
+ if (authheader.equalsIgnoreCase(AUTH.WWW_AUTH)) {
+ this.proxy = false;
+ } else if (authheader.equalsIgnoreCase(AUTH.PROXY_AUTH)) {
+ this.proxy = true;
+ } else {
+ throw new MalformedChallengeException("Unexpected header name: " + authheader);
+ }
+
+ CharArrayBuffer buffer;
+ int pos;
+ if (header instanceof FormattedHeader) {
+ buffer = ((FormattedHeader) header).getBuffer();
+ pos = ((FormattedHeader) header).getValuePos();
+ } else {
+ 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++;
+ }
+ int beginIndex = pos;
+ while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
+ pos++;
+ }
+ int endIndex = pos;
+ String s = buffer.substring(beginIndex, endIndex);
+ if (!s.equalsIgnoreCase(getSchemeName())) {
+ throw new MalformedChallengeException("Invalid scheme identifier: " + s);
+ }
+
+ parseChallenge(buffer, pos, buffer.length());
+ }
+
+ protected abstract void parseChallenge(
+ CharArrayBuffer buffer, int pos, int len) throws MalformedChallengeException;
+
+ /**
+ * Returns <code>true</code> if authenticating against a proxy, <code>false</code>
+ * otherwise.
+ *
+ * @return <code>true</code> if authenticating against a proxy, <code>false</code>
+ * otherwise
+ */
+ public boolean isProxy() {
+ return this.proxy;
+ }
+
+}
diff --git a/src/org/apache/http/impl/auth/BasicScheme.java b/src/org/apache/http/impl/auth/BasicScheme.java
new file mode 100644
index 0000000..88ea110
--- /dev/null
+++ b/src/org/apache/http/impl/auth/BasicScheme.java
@@ -0,0 +1,185 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/BasicScheme.java $
+ * $Revision: 658430 $
+ * $Date: 2008-05-20 14:04:27 -0700 (Tue, 20 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.auth.params.AuthParams;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.EncodingUtils;
+
+/**
+ * <p>
+ * Basic authentication scheme as defined in RFC 2617.
+ * </p>
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Rodney Waldhoff
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author Ortwin Glueck
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+
+public class BasicScheme extends RFC2617Scheme {
+
+ /** Whether the basic authentication process is complete */
+ private boolean complete;
+
+ /**
+ * Default constructor for the basic authetication scheme.
+ */
+ public BasicScheme() {
+ super();
+ this.complete = false;
+ }
+
+ /**
+ * Returns textual designation of the basic authentication scheme.
+ *
+ * @return <code>basic</code>
+ */
+ 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 <tt>true</tt> if Basic authorization has been processed,
+ * <tt>false</tt> otherwise.
+ */
+ public boolean isComplete() {
+ return this.complete;
+ }
+
+ /**
+ * Returns <tt>false</tt>. Basic authentication scheme is request based.
+ *
+ * @return <tt>false</tt>.
+ */
+ public boolean isConnectionBased() {
+ return false;
+ }
+
+ /**
+ * Produces basic authorization header 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 org.apache.http.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
+ */
+ public Header authenticate(
+ final Credentials credentials,
+ final HttpRequest request) throws AuthenticationException {
+
+ if (credentials == null) {
+ throw new IllegalArgumentException("Credentials may not be null");
+ }
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+
+ String charset = AuthParams.getCredentialCharset(request.getParams());
+ return authenticate(credentials, charset, isProxy());
+ }
+
+ /**
+ * Returns a basic <tt>Authorization</tt> 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
+ */
+ public static Header authenticate(
+ final Credentials credentials,
+ final String charset,
+ boolean proxy) {
+ if (credentials == null) {
+ throw new IllegalArgumentException("Credentials may not be null");
+ }
+ if (charset == null) {
+ throw new IllegalArgumentException("charset may not be null");
+ }
+
+ StringBuilder tmp = new StringBuilder();
+ tmp.append(credentials.getUserPrincipal().getName());
+ tmp.append(":");
+ tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword());
+
+ byte[] base64password = Base64.encodeBase64(
+ EncodingUtils.getBytes(tmp.toString(), charset));
+
+ 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/src/org/apache/http/impl/auth/BasicSchemeFactory.java b/src/org/apache/http/impl/auth/BasicSchemeFactory.java
new file mode 100644
index 0000000..c5d28b0
--- /dev/null
+++ b/src/org/apache/http/impl/auth/BasicSchemeFactory.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/BasicSchemeFactory.java $
+ * $Revision: 534839 $
+ * $Date: 2007-05-03 06:03:41 -0700 (Thu, 03 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BasicSchemeFactory implements AuthSchemeFactory {
+
+ public AuthScheme newInstance(final HttpParams params) {
+ return new BasicScheme();
+ }
+
+}
diff --git a/src/org/apache/http/impl/auth/DigestScheme.java b/src/org/apache/http/impl/auth/DigestScheme.java
new file mode 100644
index 0000000..803807b
--- /dev/null
+++ b/src/org/apache/http/impl/auth/DigestScheme.java
@@ -0,0 +1,484 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/DigestScheme.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.auth.params.AuthParams;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.message.BasicHeaderValueFormatter;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.EncodingUtils;
+
+/**
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * Credential charset is configured via the
+ * {@link org.apache.http.auth.params.AuthPNames#CREDENTIAL_CHARSET
+ * credential charset} parameter.
+ * Since the digest username is included as clear text in the generated
+ * Authentication header, the charset of the username must be compatible
+ * with the
+ * {@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET
+ * http element charset}.
+ * </p>
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Rodney Waldhoff
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author Ortwin Glueck
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+
+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;
+
+ //TODO: supply a real nonce-count, currently a server will interprete a repeated request as a replay
+ private static final String NC = "00000001"; //nonce-count is always 1
+ private static final int QOP_MISSING = 0;
+ private static final int QOP_AUTH_INT = 1;
+ private static final int QOP_AUTH = 2;
+
+ private int qopVariant = QOP_MISSING;
+ private String cnonce;
+
+ /**
+ * Default constructor for the digest authetication scheme.
+ */
+ public DigestScheme() {
+ super();
+ this.complete = false;
+ }
+
+ /**
+ * 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);
+
+ if (getParameter("realm") == null) {
+ throw new MalformedChallengeException("missing realm in challange");
+ }
+ if (getParameter("nonce") == null) {
+ throw new MalformedChallengeException("missing nonce in challange");
+ }
+
+ boolean unsupportedQop = false;
+ // qop parsing
+ String qop = getParameter("qop");
+ if (qop != null) {
+ StringTokenizer tok = new StringTokenizer(qop,",");
+ while (tok.hasMoreTokens()) {
+ String variant = tok.nextToken().trim();
+ if (variant.equals("auth")) {
+ qopVariant = QOP_AUTH;
+ break; //that's our favourite, because auth-int is unsupported
+ } else if (variant.equals("auth-int")) {
+ qopVariant = QOP_AUTH_INT;
+ } else {
+ unsupportedQop = true;
+ }
+ }
+ }
+
+ if (unsupportedQop && (qopVariant == QOP_MISSING)) {
+ throw new MalformedChallengeException("None of the qop methods is supported");
+ }
+ // Reset cnonce
+ this.cnonce = null;
+ this.complete = true;
+ }
+
+ /**
+ * Tests if the Digest authentication process has been completed.
+ *
+ * @return <tt>true</tt> if Digest authorization has been processed,
+ * <tt>false</tt> otherwise.
+ */
+ public boolean isComplete() {
+ String s = getParameter("stale");
+ if ("true".equalsIgnoreCase(s)) {
+ return false;
+ } else {
+ return this.complete;
+ }
+ }
+
+ /**
+ * Returns textual designation of the digest authentication scheme.
+ *
+ * @return <code>digest</code>
+ */
+ public String getSchemeName() {
+ return "digest";
+ }
+
+ /**
+ * Returns <tt>false</tt>. Digest authentication scheme is request based.
+ *
+ * @return <tt>false</tt>.
+ */
+ public boolean isConnectionBased() {
+ return false;
+ }
+
+ public void overrideParamter(final String name, final String value) {
+ getParameters().put(name, value);
+ }
+
+ private String getCnonce() {
+ if (this.cnonce == null) {
+ this.cnonce = createCnonce();
+ }
+ return this.cnonce;
+ }
+
+ /**
+ * 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 org.apache.http.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
+ */
+ public Header authenticate(
+ final Credentials credentials,
+ final HttpRequest request) throws AuthenticationException {
+
+ if (credentials == null) {
+ throw new IllegalArgumentException("Credentials may not be null");
+ }
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+
+ // Add method name and request-URI to the parameter map
+ getParameters().put("methodname", request.getRequestLine().getMethod());
+ getParameters().put("uri", request.getRequestLine().getUri());
+ String charset = getParameter("charset");
+ if (charset == null) {
+ charset = AuthParams.getCredentialCharset(request.getParams());
+ getParameters().put("charset", charset);
+ }
+ String digest = createDigest(credentials);
+ return createDigestHeader(credentials, digest);
+ }
+
+ private static MessageDigest createMessageDigest(
+ final String digAlg) throws UnsupportedDigestAlgorithmException {
+ try {
+ return MessageDigest.getInstance(digAlg);
+ } catch (Exception e) {
+ throw new UnsupportedDigestAlgorithmException(
+ "Unsupported algorithm in HTTP Digest authentication: "
+ + digAlg);
+ }
+ }
+
+ /**
+ * Creates an MD5 response digest.
+ *
+ * @return The created digest as string. This will be the response tag's
+ * value in the Authentication HTTP header.
+ * @throws AuthenticationException when MD5 is an unsupported algorithm
+ */
+ private String createDigest(final Credentials credentials) throws AuthenticationException {
+ // Collecting required tokens
+ String uri = getParameter("uri");
+ String realm = getParameter("realm");
+ String nonce = getParameter("nonce");
+ String method = getParameter("methodname");
+ String algorithm = getParameter("algorithm");
+ if (uri == null) {
+ throw new IllegalStateException("URI may not be null");
+ }
+ if (realm == null) {
+ throw new IllegalStateException("Realm may not be null");
+ }
+ if (nonce == null) {
+ throw new IllegalStateException("Nonce may not be null");
+ }
+ // If an algorithm is not specified, default to MD5.
+ if (algorithm == null) {
+ algorithm = "MD5";
+ }
+ // If an charset is not specified, default to ISO-8859-1.
+ String charset = getParameter("charset");
+ if (charset == null) {
+ charset = "ISO-8859-1";
+ }
+
+ if (qopVariant == QOP_AUTH_INT) {
+ throw new AuthenticationException(
+ "Unsupported qop in HTTP Digest authentication");
+ }
+
+ MessageDigest md5Helper = createMessageDigest("MD5");
+
+ String uname = credentials.getUserPrincipal().getName();
+ String pwd = credentials.getPassword();
+
+ // 3.2.2.2: Calculating digest
+ StringBuilder tmp = new StringBuilder(uname.length() + realm.length() + pwd.length() + 2);
+ tmp.append(uname);
+ tmp.append(':');
+ tmp.append(realm);
+ tmp.append(':');
+ tmp.append(pwd);
+ // unq(username-value) ":" unq(realm-value) ":" passwd
+ String a1 = tmp.toString();
+
+ //a1 is suitable for MD5 algorithm
+ if(algorithm.equals("MD5-sess")) {
+ // H( unq(username-value) ":" unq(realm-value) ":" passwd )
+ // ":" unq(nonce-value)
+ // ":" unq(cnonce-value)
+
+ String cnonce = getCnonce();
+
+ String tmp2=encode(md5Helper.digest(EncodingUtils.getBytes(a1, charset)));
+ StringBuilder tmp3 = new StringBuilder(tmp2.length() + nonce.length() + cnonce.length() + 2);
+ tmp3.append(tmp2);
+ tmp3.append(':');
+ tmp3.append(nonce);
+ tmp3.append(':');
+ tmp3.append(cnonce);
+ a1 = tmp3.toString();
+ } else if (!algorithm.equals("MD5")) {
+ throw new AuthenticationException("Unhandled algorithm " + algorithm + " requested");
+ }
+ String md5a1 = encode(md5Helper.digest(EncodingUtils.getBytes(a1, charset)));
+
+ String a2 = null;
+ if (qopVariant == QOP_AUTH_INT) {
+ // Unhandled qop auth-int
+ //we do not have access to the entity-body or its hash
+ //TODO: add Method ":" digest-uri-value ":" H(entity-body)
+ } else {
+ a2 = method + ':' + uri;
+ }
+ String md5a2 = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(a2)));
+
+ // 3.2.2.1
+ String serverDigestValue;
+ if (qopVariant == QOP_MISSING) {
+ StringBuilder tmp2 = new StringBuilder(md5a1.length() + nonce.length() + md5a2.length());
+ tmp2.append(md5a1);
+ tmp2.append(':');
+ tmp2.append(nonce);
+ tmp2.append(':');
+ tmp2.append(md5a2);
+ serverDigestValue = tmp2.toString();
+ } else {
+ String qopOption = getQopVariantString();
+ String cnonce = getCnonce();
+
+ StringBuilder tmp2 = new StringBuilder(md5a1.length() + nonce.length()
+ + NC.length() + cnonce.length() + qopOption.length() + md5a2.length() + 5);
+ tmp2.append(md5a1);
+ tmp2.append(':');
+ tmp2.append(nonce);
+ tmp2.append(':');
+ tmp2.append(NC);
+ tmp2.append(':');
+ tmp2.append(cnonce);
+ tmp2.append(':');
+ tmp2.append(qopOption);
+ tmp2.append(':');
+ tmp2.append(md5a2);
+ serverDigestValue = tmp2.toString();
+ }
+
+ String serverDigest =
+ encode(md5Helper.digest(EncodingUtils.getAsciiBytes(serverDigestValue)));
+
+ return serverDigest;
+ }
+
+ /**
+ * Creates digest-response header as defined in RFC2617.
+ *
+ * @param credentials User credentials
+ * @param digest The response tag's value as String.
+ *
+ * @return The digest-response as String.
+ */
+ private Header createDigestHeader(
+ final Credentials credentials,
+ final String digest) throws AuthenticationException {
+
+ CharArrayBuffer buffer = new CharArrayBuffer(128);
+ if (isProxy()) {
+ buffer.append(AUTH.PROXY_AUTH_RESP);
+ } else {
+ buffer.append(AUTH.WWW_AUTH_RESP);
+ }
+ buffer.append(": Digest ");
+
+ String uri = getParameter("uri");
+ String realm = getParameter("realm");
+ String nonce = getParameter("nonce");
+ String opaque = getParameter("opaque");
+ String response = digest;
+ String algorithm = getParameter("algorithm");
+
+ String uname = credentials.getUserPrincipal().getName();
+
+ List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>(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", response));
+
+ if (qopVariant != QOP_MISSING) {
+ params.add(new BasicNameValuePair("qop", getQopVariantString()));
+ params.add(new BasicNameValuePair("nc", NC));
+ params.add(new BasicNameValuePair("cnonce", getCnonce()));
+ }
+ if (algorithm != null) {
+ params.add(new BasicNameValuePair("algorithm", algorithm));
+ }
+ if (opaque != null) {
+ params.add(new BasicNameValuePair("opaque", opaque));
+ }
+
+ for (int i = 0; i < params.size(); i++) {
+ BasicNameValuePair param = params.get(i);
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ boolean noQuotes = "nc".equals(param.getName()) ||
+ "qop".equals(param.getName());
+ BasicHeaderValueFormatter.DEFAULT
+ .formatNameValuePair(buffer, param, !noQuotes);
+ }
+ return new BufferedHeader(buffer);
+ }
+
+ private String getQopVariantString() {
+ String qopOption;
+ if (qopVariant == QOP_AUTH_INT) {
+ qopOption = "auth-int";
+ } else {
+ qopOption = "auth";
+ }
+ return qopOption;
+ }
+
+ /**
+ * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long
+ * <CODE>String</CODE> according to RFC 2617.
+ *
+ * @param binaryData array containing the digest
+ * @return encoded MD5, or <CODE>null</CODE> if encoding failed
+ */
+ private static String encode(byte[] binaryData) {
+ if (binaryData.length != 16) {
+ return null;
+ }
+
+ char[] buffer = new char[32];
+ for (int i = 0; i < 16; i++) {
+ int low = (binaryData[i] & 0x0f);
+ 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.
+ * @throws UnsupportedDigestAlgorithmException if MD5 algorithm is not supported.
+ */
+ public static String createCnonce() {
+ String cnonce;
+
+ MessageDigest md5Helper = createMessageDigest("MD5");
+
+ cnonce = Long.toString(System.currentTimeMillis());
+ cnonce = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(cnonce)));
+
+ return cnonce;
+ }
+}
diff --git a/src/org/apache/http/impl/auth/DigestSchemeFactory.java b/src/org/apache/http/impl/auth/DigestSchemeFactory.java
new file mode 100644
index 0000000..38f2e12
--- /dev/null
+++ b/src/org/apache/http/impl/auth/DigestSchemeFactory.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java $
+ * $Revision: 534839 $
+ * $Date: 2007-05-03 06:03:41 -0700 (Thu, 03 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class DigestSchemeFactory implements AuthSchemeFactory {
+
+ public AuthScheme newInstance(final HttpParams params) {
+ return new DigestScheme();
+ }
+
+}
diff --git a/src/org/apache/http/impl/auth/NTLMEngine.java b/src/org/apache/http/impl/auth/NTLMEngine.java
new file mode 100644
index 0000000..7b6bf42
--- /dev/null
+++ b/src/org/apache/http/impl/auth/NTLMEngine.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/NTLMEngine.java $
+ * $Revision: 659788 $
+ * $Date: 2008-05-24 03:42:23 -0700 (Sat, 24 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+/**
+ * Abstract NTLM authentication engine. The engine can be used to
+ * generate Type1 messages and Type3 messages in response to a
+ * Type2 challenge.
+ * <p/>
+ * For details see <a href="http://davenport.sourceforge.net/ntlm.html">this resource</a>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+*/
+public interface NTLMEngine {
+
+ /**
+ * Generates a Type1 message given the domain and workstation.
+ *
+ * @param domain Optional Windows domain name. Can be <code>null</code>.
+ * @param workstation Optional Windows workstation name. Can be
+ * <code>null</code>.
+ * @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/src/org/apache/http/impl/auth/NTLMEngineException.java b/src/org/apache/http/impl/auth/NTLMEngineException.java
new file mode 100644
index 0000000..73baabc
--- /dev/null
+++ b/src/org/apache/http/impl/auth/NTLMEngineException.java
@@ -0,0 +1,70 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/NTLMEngineException.java $
+ * $Revision: 655048 $
+ * $Date: 2008-05-10 04:22:12 -0700 (Sat, 10 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.auth.AuthenticationException;
+
+/**
+ * Signals NTLM protocol failure.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+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(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new NTLMEngineException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public NTLMEngineException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/org/apache/http/impl/auth/NTLMScheme.java b/src/org/apache/http/impl/auth/NTLMScheme.java
new file mode 100644
index 0000000..8dfdbba
--- /dev/null
+++ b/src/org/apache/http/impl/auth/NTLMScheme.java
@@ -0,0 +1,149 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/NTLMScheme.java $
+ * $Revision: 655048 $
+ * $Date: 2008-05-10 04:22:12 -0700 (Sat, 10 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.InvalidCredentialsException;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.auth.NTCredentials;
+import org.apache.http.impl.auth.AuthSchemeBase;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+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();
+ if (engine == null) {
+ throw new IllegalArgumentException("NTLM engine may not be null");
+ }
+ this.engine = engine;
+ this.state = State.UNINITIATED;
+ this.challenge = null;
+ }
+
+ public String getSchemeName() {
+ return "ntlm";
+ }
+
+ public String getParameter(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, int pos, int len) throws MalformedChallengeException {
+ String challenge = buffer.substringTrimmed(pos, len);
+ if (challenge.length() == 0) {
+ if (this.state == State.UNINITIATED) {
+ this.state = State.CHALLENGE_RECEIVED;
+ } else {
+ this.state = State.FAILED;
+ }
+ this.challenge = null;
+ } else {
+ this.state = State.MSG_TYPE2_RECEVIED;
+ this.challenge = challenge;
+ }
+ }
+
+ public Header authenticate(
+ final Credentials credentials,
+ final HttpRequest request) throws AuthenticationException {
+ NTCredentials ntcredentials = null;
+ try {
+ ntcredentials = (NTCredentials) credentials;
+ } catch (ClassCastException e) {
+ throw new InvalidCredentialsException(
+ "Credentials cannot be used for NTLM authentication: "
+ + credentials.getClass().getName());
+ }
+ String response = null;
+ if (this.state == State.CHALLENGE_RECEIVED || this.state == State.FAILED) {
+ 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);
+ }
+ 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/src/org/apache/http/impl/auth/RFC2617Scheme.java b/src/org/apache/http/impl/auth/RFC2617Scheme.java
new file mode 100644
index 0000000..0ed0a28
--- /dev/null
+++ b/src/org/apache/http/impl/auth/RFC2617Scheme.java
@@ -0,0 +1,119 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java $
+ * $Revision: 659595 $
+ * $Date: 2008-05-23 09:47:14 -0700 (Fri, 23 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.message.BasicHeaderValueParser;
+import org.apache.http.message.HeaderValueParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Abstract authentication scheme class that lays foundation for all
+ * RFC 2617 compliant authetication schemes and provides capabilities common
+ * to all authentication schemes defined in RFC 2617.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+*/
+public abstract class RFC2617Scheme extends AuthSchemeBase {
+
+ /**
+ * Authentication parameter map.
+ */
+ private Map<String, String> params;
+
+ /**
+ * Default constructor for RFC2617 compliant authetication schemes.
+ */
+ public RFC2617Scheme() {
+ super();
+ }
+
+ @Override
+ protected void parseChallenge(
+ final CharArrayBuffer buffer, int pos, int len) throws MalformedChallengeException {
+ HeaderValueParser parser = BasicHeaderValueParser.DEFAULT;
+ ParserCursor cursor = new ParserCursor(pos, buffer.length());
+ HeaderElement[] elements = parser.parseElements(buffer, cursor);
+ if (elements.length == 0) {
+ throw new MalformedChallengeException("Authentication challenge is empty");
+ }
+
+ this.params = new HashMap<String, String>(elements.length);
+ for (HeaderElement element : elements) {
+ this.params.put(element.getName(), element.getValue());
+ }
+ }
+
+ /**
+ * Returns authentication parameters map. Keys in the map are lower-cased.
+ *
+ * @return the map of authentication parameters
+ */
+ protected Map<String, String> getParameters() {
+ if (this.params == null) {
+ this.params = new HashMap<String, String>();
+ }
+ 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) {
+ throw new IllegalArgumentException("Parameter name may not be null");
+ }
+ if (this.params == 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/src/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java b/src/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java
new file mode 100644
index 0000000..abd0a66
--- /dev/null
+++ b/src/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/auth/UnsupportedDigestAlgorithmException.java $
+ * $Revision: 527479 $
+ * $Date: 2007-04-11 05:55:12 -0700 (Wed, 11 Apr 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+/**
+ * Authentication credentials required to respond to a authentication
+ * challenge are invalid
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class UnsupportedDigestAlgorithmException extends RuntimeException {
+
+ private static final long serialVersionUID = 319558534317118022L;
+
+ /**
+ * Creates a new UnsupportedAuthAlgoritmException with a <tt>null</tt> detail message.
+ */
+ public UnsupportedDigestAlgorithmException() {
+ super();
+ }
+
+ /**
+ * Creates a new UnsupportedAuthAlgoritmException with the specified message.
+ *
+ * @param message the exception detail message
+ */
+ public UnsupportedDigestAlgorithmException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new UnsupportedAuthAlgoritmException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
+ * if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
+ */
+ public UnsupportedDigestAlgorithmException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/org/apache/http/impl/auth/package.html b/src/org/apache/http/impl/auth/package.html
new file mode 100644
index 0000000..e301283
--- /dev/null
+++ b/src/org/apache/http/impl/auth/package.html
@@ -0,0 +1,4 @@
+<body>
+
+</body>
+
diff --git a/src/org/apache/http/impl/client/AbstractAuthenticationHandler.java b/src/org/apache/http/impl/client/AbstractAuthenticationHandler.java
new file mode 100644
index 0000000..57699d5
--- /dev/null
+++ b/src/org/apache/http/impl/client/AbstractAuthenticationHandler.java
@@ -0,0 +1,165 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public abstract class AbstractAuthenticationHandler implements AuthenticationHandler {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ private static final List<String> DEFAULT_SCHEME_PRIORITY = Arrays.asList(new String[] {
+ "ntlm",
+ "digest",
+ "basic"
+ });
+
+ public AbstractAuthenticationHandler() {
+ super();
+ }
+
+ protected Map<String, Header> parseChallenges(
+ final Header[] headers) throws MalformedChallengeException {
+
+ Map<String, Header> map = new HashMap<String, Header>(headers.length);
+ for (Header header : headers) {
+ CharArrayBuffer buffer;
+ int pos;
+ if (header instanceof FormattedHeader) {
+ buffer = ((FormattedHeader) header).getBuffer();
+ pos = ((FormattedHeader) header).getValuePos();
+ } else {
+ 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++;
+ }
+ int beginIndex = pos;
+ while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
+ pos++;
+ }
+ int endIndex = pos;
+ String s = buffer.substring(beginIndex, endIndex);
+ map.put(s.toLowerCase(Locale.ENGLISH), header);
+ }
+ return map;
+ }
+
+ protected List<String> getAuthPreferences() {
+ return DEFAULT_SCHEME_PRIORITY;
+ }
+
+ public AuthScheme selectScheme(
+ final Map<String, Header> challenges,
+ final HttpResponse response,
+ final HttpContext context) throws AuthenticationException {
+
+ AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute(
+ ClientContext.AUTHSCHEME_REGISTRY);
+ if (registry == null) {
+ throw new IllegalStateException("AuthScheme registry not set in HTTP context");
+ }
+
+ List<?> authPrefs = (List<?>) context.getAttribute(
+ ClientContext.AUTH_SCHEME_PREF);
+ if (authPrefs == null) {
+ authPrefs = getAuthPreferences();
+ }
+
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("Authentication schemes in the order of preference: "
+ + authPrefs);
+ }
+
+ AuthScheme authScheme = null;
+ for (int i = 0; i < authPrefs.size(); i++) {
+ String id = (String) authPrefs.get(i);
+ 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 (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/src/org/apache/http/impl/client/AbstractHttpClient.java b/src/org/apache/http/impl/client/AbstractHttpClient.java
new file mode 100644
index 0000000..3a1b838
--- /dev/null
+++ b/src/org/apache/http/impl/client/AbstractHttpClient.java
@@ -0,0 +1,697 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java $
+ * $Revision: 677250 $
+ * $Date: 2008-07-16 04:45:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.lang.reflect.UndeclaredThrowableException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpEntity;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.RequestDirector;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.DefaultedHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestExecutor;
+
+/**
+ * Convenience base class for HTTP client implementations.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 677250 $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractHttpClient implements HttpClient {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** The parameters. */
+ private HttpParams defaultParams;
+
+ /** The request executor. */
+ private HttpRequestExecutor requestExec;
+
+ /** The connection manager. */
+ private ClientConnectionManager connManager;
+
+ /** The connection re-use strategy. */
+ private ConnectionReuseStrategy reuseStrategy;
+
+ /** The connection keep-alive strategy. */
+ private ConnectionKeepAliveStrategy keepAliveStrategy;
+
+ /** The cookie spec registry. */
+ private CookieSpecRegistry supportedCookieSpecs;
+
+ /** The authentication scheme registry. */
+ private AuthSchemeRegistry supportedAuthSchemes;
+
+ /** The HTTP processor. */
+ private BasicHttpProcessor httpProcessor;
+
+ /** The request retry handler. */
+ private HttpRequestRetryHandler retryHandler;
+
+ /** The redirect handler. */
+ private RedirectHandler redirectHandler;
+
+ /** The target authentication handler. */
+ private AuthenticationHandler targetAuthHandler;
+
+ /** The proxy authentication handler. */
+ private AuthenticationHandler proxyAuthHandler;
+
+ /** The cookie store. */
+ private CookieStore cookieStore;
+
+ /** The credentials provider. */
+ private CredentialsProvider credsProvider;
+
+ /** The route planner. */
+ private HttpRoutePlanner routePlanner;
+
+ /** The user token handler. */
+ private UserTokenHandler userTokenHandler;
+
+
+ /**
+ * Creates a new HTTP client.
+ *
+ * @param conman the connection manager
+ * @param params the parameters
+ */
+ protected AbstractHttpClient(
+ final ClientConnectionManager conman,
+ final HttpParams params) {
+ defaultParams = params;
+ connManager = conman;
+ } // constructor
+
+ protected abstract HttpParams createHttpParams();
+
+
+ protected abstract HttpContext createHttpContext();
+
+
+ protected abstract HttpRequestExecutor createRequestExecutor();
+
+
+ protected abstract ClientConnectionManager createClientConnectionManager();
+
+
+ protected abstract AuthSchemeRegistry createAuthSchemeRegistry();
+
+
+ protected abstract CookieSpecRegistry createCookieSpecRegistry();
+
+
+ protected abstract ConnectionReuseStrategy createConnectionReuseStrategy();
+
+
+ protected abstract ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy();
+
+
+ protected abstract BasicHttpProcessor createHttpProcessor();
+
+
+ protected abstract HttpRequestRetryHandler createHttpRequestRetryHandler();
+
+
+ protected abstract RedirectHandler createRedirectHandler();
+
+
+ protected abstract AuthenticationHandler createTargetAuthenticationHandler();
+
+
+ protected abstract AuthenticationHandler createProxyAuthenticationHandler();
+
+
+ protected abstract CookieStore createCookieStore();
+
+
+ protected abstract CredentialsProvider createCredentialsProvider();
+
+
+ protected abstract HttpRoutePlanner createHttpRoutePlanner();
+
+
+ protected abstract UserTokenHandler createUserTokenHandler();
+
+
+ // 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(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 authSchemeRegistry) {
+ supportedAuthSchemes = authSchemeRegistry;
+ }
+
+
+ public synchronized final CookieSpecRegistry getCookieSpecs() {
+ if (supportedCookieSpecs == null) {
+ supportedCookieSpecs = createCookieSpecRegistry();
+ }
+ return supportedCookieSpecs;
+ }
+
+
+ public synchronized void setCookieSpecs(final CookieSpecRegistry cookieSpecRegistry) {
+ supportedCookieSpecs = cookieSpecRegistry;
+ }
+
+
+ public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
+ if (reuseStrategy == null) {
+ reuseStrategy = createConnectionReuseStrategy();
+ }
+ return reuseStrategy;
+ }
+
+
+ public synchronized void setReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
+ this.reuseStrategy = reuseStrategy;
+ }
+
+
+ public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
+ if (keepAliveStrategy == null) {
+ keepAliveStrategy = createConnectionKeepAliveStrategy();
+ }
+ return keepAliveStrategy;
+ }
+
+
+ public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
+ this.keepAliveStrategy = keepAliveStrategy;
+ }
+
+
+ public synchronized final HttpRequestRetryHandler getHttpRequestRetryHandler() {
+ if (retryHandler == null) {
+ retryHandler = createHttpRequestRetryHandler();
+ }
+ return retryHandler;
+ }
+
+
+ public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler retryHandler) {
+ this.retryHandler = retryHandler;
+ }
+
+
+ public synchronized final RedirectHandler getRedirectHandler() {
+ if (redirectHandler == null) {
+ redirectHandler = createRedirectHandler();
+ }
+ return redirectHandler;
+ }
+
+
+ public synchronized void setRedirectHandler(final RedirectHandler redirectHandler) {
+ this.redirectHandler = redirectHandler;
+ }
+
+
+ public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
+ if (targetAuthHandler == null) {
+ targetAuthHandler = createTargetAuthenticationHandler();
+ }
+ return targetAuthHandler;
+ }
+
+
+ public synchronized void setTargetAuthenticationHandler(
+ final AuthenticationHandler targetAuthHandler) {
+ this.targetAuthHandler = targetAuthHandler;
+ }
+
+
+ public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
+ if (proxyAuthHandler == null) {
+ proxyAuthHandler = createProxyAuthenticationHandler();
+ }
+ return proxyAuthHandler;
+ }
+
+
+ public synchronized void setProxyAuthenticationHandler(
+ final AuthenticationHandler proxyAuthHandler) {
+ this.proxyAuthHandler = proxyAuthHandler;
+ }
+
+
+ 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 userTokenHandler) {
+ this.userTokenHandler = userTokenHandler;
+ }
+
+
+ protected synchronized final BasicHttpProcessor getHttpProcessor() {
+ if (httpProcessor == null) {
+ httpProcessor = createHttpProcessor();
+ }
+ return httpProcessor;
+ }
+
+
+ public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) {
+ getHttpProcessor().addInterceptor(itcp);
+ }
+
+
+ public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, int index) {
+ getHttpProcessor().addInterceptor(itcp, index);
+ }
+
+
+ public synchronized HttpResponseInterceptor getResponseInterceptor(int index) {
+ return getHttpProcessor().getResponseInterceptor(index);
+ }
+
+
+ public synchronized int getResponseInterceptorCount() {
+ return getHttpProcessor().getResponseInterceptorCount();
+ }
+
+
+ public synchronized void clearResponseInterceptors() {
+ getHttpProcessor().clearResponseInterceptors();
+ }
+
+
+ public void removeResponseInterceptorByClass(Class<? extends HttpResponseInterceptor> clazz) {
+ getHttpProcessor().removeResponseInterceptorByClass(clazz);
+ }
+
+
+ public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) {
+ getHttpProcessor().addInterceptor(itcp);
+ }
+
+
+ public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, int index) {
+ getHttpProcessor().addInterceptor(itcp, index);
+ }
+
+
+ public synchronized HttpRequestInterceptor getRequestInterceptor(int index) {
+ return getHttpProcessor().getRequestInterceptor(index);
+ }
+
+
+ public synchronized int getRequestInterceptorCount() {
+ return getHttpProcessor().getRequestInterceptorCount();
+ }
+
+
+ public synchronized void clearRequestInterceptors() {
+ getHttpProcessor().clearRequestInterceptors();
+ }
+
+
+ public void removeRequestInterceptorByClass(Class<? extends HttpRequestInterceptor> clazz) {
+ getHttpProcessor().removeRequestInterceptorByClass(clazz);
+ }
+
+
+ // non-javadoc, see interface HttpClient
+ public final HttpResponse execute(HttpUriRequest request)
+ throws IOException, ClientProtocolException {
+
+ return execute(request, (HttpContext) null);
+ }
+
+
+ /**
+ * Maps to {@link HttpClient#execute(HttpHost,HttpRequest,HttpContext)
+ * execute(target, request, context)}.
+ * The target is determined from the URI of the request.
+ *
+ * @param request the request to execute
+ * @param context the request-specific execution context,
+ * or <code>null</code> to use a default context
+ */
+ public final HttpResponse execute(HttpUriRequest request,
+ HttpContext context)
+ throws IOException, ClientProtocolException {
+
+ if (request == null) {
+ throw new IllegalArgumentException
+ ("Request must not be null.");
+ }
+
+ return execute(determineTarget(request), request, context);
+ }
+
+ private HttpHost determineTarget(HttpUriRequest request) {
+ // A null target may be acceptable if there is a default target.
+ // Otherwise, the null target is detected in the director.
+ HttpHost target = null;
+
+ URI requestURI = request.getURI();
+ if (requestURI.isAbsolute()) {
+ target = new HttpHost(
+ requestURI.getHost(),
+ requestURI.getPort(),
+ requestURI.getScheme());
+ }
+ return target;
+ }
+
+ // non-javadoc, see interface HttpClient
+ public final HttpResponse execute(HttpHost target, HttpRequest request)
+ throws IOException, ClientProtocolException {
+
+ return execute(target, request, (HttpContext) null);
+ }
+
+
+ // non-javadoc, see interface HttpClient
+ public final HttpResponse execute(HttpHost target, HttpRequest request,
+ HttpContext context)
+ throws IOException, ClientProtocolException {
+
+ if (request == null) {
+ throw new IllegalArgumentException
+ ("Request must not be null.");
+ }
+ // 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;
+
+ // Initialize the request execution context making copies of
+ // all shared objects that are potentially threading unsafe.
+ synchronized (this) {
+
+ HttpContext defaultContext = createHttpContext();
+ if (context == null) {
+ execContext = defaultContext;
+ } else {
+ execContext = new DefaultedHttpContext(context, defaultContext);
+ }
+ // Create a director for this request
+ director = createClientRequestDirector(
+ getRequestExecutor(),
+ getConnectionManager(),
+ getConnectionReuseStrategy(),
+ getConnectionKeepAliveStrategy(),
+ getRoutePlanner(),
+ getHttpProcessor().copy(),
+ getHttpRequestRetryHandler(),
+ getRedirectHandler(),
+ getTargetAuthenticationHandler(),
+ getProxyAuthenticationHandler(),
+ getUserTokenHandler(),
+ determineParams(request));
+ }
+
+ try {
+ return director.execute(target, request, execContext);
+ } catch(HttpException httpException) {
+ throw new ClientProtocolException(httpException);
+ }
+ } // execute
+
+
+ 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 stateHandler,
+ final HttpParams params) {
+ return new DefaultRequestDirector(
+ requestExec,
+ conman,
+ reustrat,
+ kastrat,
+ rouplan,
+ httpProcessor,
+ retryHandler,
+ redirectHandler,
+ targetAuthHandler,
+ proxyAuthHandler,
+ stateHandler,
+ 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.
+ * <br/>
+ * 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(HttpRequest req) {
+ return new ClientParamsStack
+ (null, getParams(), req.getParams(), null);
+ }
+
+
+ // non-javadoc, see interface HttpClient
+ public <T> T execute(
+ final HttpUriRequest request,
+ final ResponseHandler<? extends T> responseHandler)
+ throws IOException, ClientProtocolException {
+ return execute(request, responseHandler, null);
+ }
+
+
+ // non-javadoc, see interface HttpClient
+ public <T> T execute(
+ final HttpUriRequest request,
+ final ResponseHandler<? extends T> responseHandler,
+ final HttpContext context)
+ throws IOException, ClientProtocolException {
+ HttpHost target = determineTarget(request);
+ return execute(target, request, responseHandler, context);
+ }
+
+
+ // non-javadoc, see interface HttpClient
+ public <T> T execute(
+ final HttpHost target,
+ final HttpRequest request,
+ final ResponseHandler<? extends T> responseHandler)
+ throws IOException, ClientProtocolException {
+ return execute(target, request, responseHandler, null);
+ }
+
+
+ // non-javadoc, see interface HttpClient
+ public <T> T execute(
+ final HttpHost target,
+ final HttpRequest request,
+ final ResponseHandler<? extends T> responseHandler,
+ final HttpContext context)
+ throws IOException, ClientProtocolException {
+ if (responseHandler == null) {
+ throw new IllegalArgumentException
+ ("Response handler must not be null.");
+ }
+
+ HttpResponse response = execute(target, request, context);
+
+ T result;
+ try {
+ result = responseHandler.handleResponse(response);
+ } catch (Throwable t) {
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ try {
+ entity.consumeContent();
+ } catch (Throwable 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 Error) {
+ throw (Error) t;
+ }
+
+ 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.
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ // Let this exception go to the caller.
+ entity.consumeContent();
+ }
+
+ return result;
+ }
+
+
+} // class AbstractHttpClient
diff --git a/src/org/apache/http/impl/client/BasicCookieStore.java b/src/org/apache/http/impl/client/BasicCookieStore.java
new file mode 100644
index 0000000..9970961
--- /dev/null
+++ b/src/org/apache/http/impl/client/BasicCookieStore.java
@@ -0,0 +1,162 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/BasicCookieStore.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.http.client.CookieStore;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieIdentityComparator;
+
+/**
+ * Default implementation of {@link CookieStore}
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Rodney Waldhoff
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
+ *
+ * @since 4.0
+ */
+public class BasicCookieStore implements CookieStore {
+
+ private final ArrayList<Cookie> cookies;
+
+ private final Comparator<Cookie> cookieComparator;
+
+ // -------------------------------------------------------- Class Variables
+
+ /**
+ * Default constructor.
+ */
+ public BasicCookieStore() {
+ super();
+ this.cookies = new ArrayList<Cookie>();
+ this.cookieComparator = 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(Cookie cookie) {
+ if (cookie != null) {
+ // first remove any old cookie that is equivalent
+ for (Iterator<Cookie> it = cookies.iterator(); it.hasNext();) {
+ if (cookieComparator.compare(cookie, it.next()) == 0) {
+ it.remove();
+ break;
+ }
+ }
+ 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(Cookie[] cookies) {
+ if (cookies != null) {
+ for (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<Cookie> getCookies() {
+ return Collections.unmodifiableList(this.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 (Iterator<Cookie> it = cookies.iterator(); it.hasNext();) {
+ if (it.next().isExpired(date)) {
+ it.remove();
+ removed = true;
+ }
+ }
+ return removed;
+ }
+
+ @Override
+ public String toString() {
+ return cookies.toString();
+ }
+
+ /**
+ * Clears all cookies.
+ */
+ public synchronized void clear() {
+ cookies.clear();
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/BasicCredentialsProvider.java b/src/org/apache/http/impl/client/BasicCredentialsProvider.java
new file mode 100644
index 0000000..02427ea
--- /dev/null
+++ b/src/org/apache/http/impl/client/BasicCredentialsProvider.java
@@ -0,0 +1,143 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/BasicCredentialsProvider.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.HashMap;
+
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.CredentialsProvider;
+
+/**
+ * Default implementation of {@link CredentialsProvider}
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Rodney Waldhoff
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
+ *
+ * @since 4.0
+ */
+public class BasicCredentialsProvider implements CredentialsProvider {
+
+ private final HashMap<AuthScope, Credentials> credMap;
+
+ /**
+ * Default constructor.
+ */
+ public BasicCredentialsProvider() {
+ super();
+ this.credMap = new HashMap<AuthScope, Credentials>();
+ }
+
+ /**
+ * 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)
+ */
+ public synchronized void setCredentials(
+ final AuthScope authscope,
+ final Credentials credentials) {
+ if (authscope == null) {
+ throw new IllegalArgumentException("Authentication scope may not be null");
+ }
+ 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 HashMap<AuthScope, Credentials> 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 (AuthScope current: map.keySet()) {
+ int factor = authscope.match(current);
+ if (factor > bestMatchFactor) {
+ bestMatchFactor = factor;
+ bestMatch = current;
+ }
+ }
+ if (bestMatch != null) {
+ creds = map.get(bestMatch);
+ }
+ }
+ return creds;
+ }
+
+ /**
+ * Get the {@link Credentials credentials} for the given authentication scope.
+ *
+ * @param authscope the {@link AuthScope authentication scope}
+ * @return the credentials
+ *
+ * @see #setCredentials(AuthScope, Credentials)
+ */
+ public synchronized Credentials getCredentials(final AuthScope authscope) {
+ if (authscope == null) {
+ throw new IllegalArgumentException("Authentication scope may not be null");
+ }
+ return matchCredentials(this.credMap, authscope);
+ }
+
+ @Override
+ public String toString() {
+ return credMap.toString();
+ }
+
+ /**
+ * Clears all credentials.
+ */
+ public synchronized void clear() {
+ this.credMap.clear();
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/BasicResponseHandler.java b/src/org/apache/http/impl/client/BasicResponseHandler.java
new file mode 100644
index 0000000..f17d30d
--- /dev/null
+++ b/src/org/apache/http/impl/client/BasicResponseHandler.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/BasicResponseHandler.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.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 org.apache.http.client.HttpClient#execute(
+ * org.apache.http.client.methods.HttpUriRequest, ResponseHandler),
+ * HttpClient may handle redirects (3xx responses) internally.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 677240 $
+ *
+ * @since 4.0
+ */
+public class BasicResponseHandler implements ResponseHandler<String> {
+
+ /**
+ * 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 {
+ StatusLine statusLine = response.getStatusLine();
+ if (statusLine.getStatusCode() >= 300) {
+ throw new HttpResponseException(statusLine.getStatusCode(),
+ statusLine.getReasonPhrase());
+ }
+
+ HttpEntity entity = response.getEntity();
+ return entity == null ? null : EntityUtils.toString(entity);
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/ClientParamsStack.java b/src/org/apache/http/impl/client/ClientParamsStack.java
new file mode 100644
index 0000000..a017e5d
--- /dev/null
+++ b/src/org/apache/http/impl/client/ClientParamsStack.java
@@ -0,0 +1,282 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/ClientParamsStack.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.AbstractHttpParams;
+
+
+/**
+ * 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:
+ * <ol>
+ * <li>Application parameters:
+ * expected to be the same for all clients used by an application.
+ * These provide "global", that is application-wide, defaults.
+ * </li>
+ * <li>Client parameters:
+ * specific to an instance of
+ * {@link org.apache.http.client.HttpClient HttpClient}.
+ * These provide client specific defaults.
+ * </li>
+ * <li>Request parameters:
+ * specific to a single request execution.
+ * For overriding client and global defaults.
+ * </li>
+ * <li>Override parameters:
+ * specific to an instance of
+ * {@link org.apache.http.client.HttpClient HttpClient}.
+ * These can be used to set parameters that cannot be overridden
+ * on a per-request basis.
+ * </li>
+ * </ol>
+ * Each stack entry may be <code>null</code>. That is preferable over
+ * an empty params collection, since it avoids searching the empty collection
+ * when looking up parameters.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * @version $Revision: 673450 $
+ */
+public class ClientParamsStack extends AbstractHttpParams {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** The application parameter collection, or <code>null</code>. */
+ protected final HttpParams applicationParams;
+
+ /** The client parameter collection, or <code>null</code>. */
+ protected final HttpParams clientParams;
+
+ /** The request parameter collection, or <code>null</code>. */
+ protected final HttpParams requestParams;
+
+ /** The override parameter collection, or <code>null</code>. */
+ 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 <code>null</code>
+ * @param cparams client parameters, or <code>null</code>
+ * @param rparams request parameters, or <code>null</code>
+ * @param oparams override parameters, or <code>null</code>
+ */
+ public ClientParamsStack(HttpParams aparams, HttpParams cparams,
+ HttpParams rparams, 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(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 <code>null</code>,
+ * 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 <code>null</code>
+ * @param cparams client parameters, or <code>null</code>
+ * @param rparams request parameters, or <code>null</code>
+ * @param oparams override parameters, or <code>null</code>
+ */
+ public ClientParamsStack(ClientParamsStack stack,
+ HttpParams aparams, HttpParams cparams,
+ HttpParams rparams, 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 <code>null</code>
+ */
+ public final HttpParams getApplicationParams() {
+ return applicationParams;
+ }
+
+ /**
+ * Obtains the client parameters of this stack.
+ *
+ * @return the client parameters, or <code>null</code>
+ */
+ public final HttpParams getClientParams() {
+ return clientParams;
+ }
+
+ /**
+ * Obtains the request parameters of this stack.
+ *
+ * @return the request parameters, or <code>null</code>
+ */
+ public final HttpParams getRequestParams() {
+ return requestParams;
+ }
+
+ /**
+ * Obtains the override parameters of this stack.
+ *
+ * @return the override parameters, or <code>null</code>
+ */
+ 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
+ * <code>null</code> if it is not set anywhere in this stack
+ */
+ public Object getParameter(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException
+ ("Parameter name must not be null.");
+ }
+
+ 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);
+ }
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("'" + name + "': " + result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Does <i>not</i> 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(String name, Object value)
+ throws UnsupportedOperationException {
+
+ throw new UnsupportedOperationException
+ ("Setting parameters in a stack is not supported.");
+ }
+
+
+ /**
+ * Does <i>not</i> 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(String name) {
+ throw new UnsupportedOperationException
+ ("Removing parameters in a stack is not supported.");
+ }
+
+
+ /**
+ * Does <i>not</i> 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.
+ * <br/>
+ * Derived classes may change this behavior.
+ *
+ * @return <code>this</code> parameter stack
+ */
+ public HttpParams copy() {
+ return this;
+ }
+
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java b/src/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java
new file mode 100644
index 0000000..c7641d2
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java
@@ -0,0 +1,76 @@
+/*
+ * $HeadURL: $
+ * $Revision: $
+ * $Date: $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderElementIterator;
+import org.apache.http.HttpResponse;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.message.BasicHeaderElementIterator;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * 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.
+ *
+ * @author <a href="mailto:sberlin at gmail.com">Sam Berlin</a>
+ *
+ * @version $Revision: $
+ *
+ * @since 4.0
+ */
+public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
+
+ public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ HeaderElementIterator it = new BasicHeaderElementIterator(
+ response.headerIterator(HTTP.CONN_KEEP_ALIVE));
+ while (it.hasNext()) {
+ HeaderElement he = it.nextElement();
+ String param = he.getName();
+ String value = he.getValue();
+ if (value != null && param.equalsIgnoreCase("timeout")) {
+ try {
+ return Long.parseLong(value) * 1000;
+ } catch(NumberFormatException ignore) {
+ }
+ }
+ }
+ return -1;
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultHttpClient.java b/src/org/apache/http/impl/client/DefaultHttpClient.java
new file mode 100644
index 0000000..f0b694e
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultHttpClient.java
@@ -0,0 +1,332 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java $
+ * $Revision: 677250 $
+ * $Date: 2008-07-16 04:45:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpVersion;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.CookiePolicy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.protocol.RequestAddCookies;
+import org.apache.http.client.protocol.RequestDefaultHeaders;
+import org.apache.http.client.protocol.RequestProxyAuthentication;
+import org.apache.http.client.protocol.RequestTargetAuthentication;
+import org.apache.http.client.protocol.ResponseProcessCookies;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionManagerFactory;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.cookie.CookieSpecRegistry;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.auth.BasicSchemeFactory;
+import org.apache.http.impl.auth.DigestSchemeFactory;
+import org.apache.http.impl.conn.DefaultHttpRoutePlanner;
+import org.apache.http.impl.conn.SingleClientConnManager;
+import org.apache.http.impl.cookie.BestMatchSpecFactory;
+import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
+import org.apache.http.impl.cookie.NetscapeDraftSpecFactory;
+import org.apache.http.impl.cookie.RFC2109SpecFactory;
+import org.apache.http.impl.cookie.RFC2965SpecFactory;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestExecutor;
+import org.apache.http.protocol.RequestConnControl;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestExpectContinue;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.VersionInfo;
+
+
+
+/**
+ * Default implementation of an HTTP client.
+ * <br/>
+ * This class replaces <code>HttpClient</code> in HttpClient 3.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 677250 $
+ *
+ * @since 4.0
+ */
+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);
+ }
+
+
+ public DefaultHttpClient(final HttpParams params) {
+ super(null, params);
+ }
+
+
+ public DefaultHttpClient() {
+ super(null, null);
+ }
+
+
+ @Override
+ protected HttpParams createHttpParams() {
+ HttpParams params = new BasicHttpParams();
+ HttpProtocolParams.setVersion(params,
+ HttpVersion.HTTP_1_1);
+ HttpProtocolParams.setContentCharset(params,
+ HTTP.DEFAULT_CONTENT_CHARSET);
+ HttpProtocolParams.setUseExpectContinue(params,
+ true);
+
+ // determine the release version from packaged version info
+ final VersionInfo vi = VersionInfo.loadVersionInfo
+ ("org.apache.http.client", getClass().getClassLoader());
+ final String release = (vi != null) ?
+ vi.getRelease() : VersionInfo.UNAVAILABLE;
+ HttpProtocolParams.setUserAgent(params,
+ "Apache-HttpClient/" + release + " (java 1.4)");
+
+ return params;
+ }
+
+
+ @Override
+ protected HttpRequestExecutor createRequestExecutor() {
+ return new HttpRequestExecutor();
+ }
+
+
+ @Override
+ protected ClientConnectionManager createClientConnectionManager() {
+ SchemeRegistry registry = new SchemeRegistry();
+ registry.register(
+ new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+ registry.register(
+ new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
+
+ ClientConnectionManager connManager = null;
+ HttpParams params = getParams();
+
+ ClientConnectionManagerFactory factory = null;
+
+ // Try first getting the factory directly as an object.
+ factory = (ClientConnectionManagerFactory) params
+ .getParameter(ClientPNames.CONNECTION_MANAGER_FACTORY);
+ if (factory == null) { // then try getting its class name.
+ String className = (String) params.getParameter(
+ ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME);
+ if (className != null) {
+ try {
+ Class<?> clazz = Class.forName(className);
+ factory = (ClientConnectionManagerFactory) clazz.newInstance();
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Invalid class name: " + className);
+ } catch (IllegalAccessException ex) {
+ throw new IllegalAccessError(ex.getMessage());
+ } catch (InstantiationException ex) {
+ throw new InstantiationError(ex.getMessage());
+ }
+ }
+ }
+
+ if(factory != null) {
+ connManager = factory.newInstance(params, registry);
+ } else {
+ connManager = new SingleClientConnManager(getParams(), registry);
+ }
+
+ return connManager;
+ }
+
+
+ @Override
+ protected HttpContext createHttpContext() {
+ HttpContext context = new BasicHttpContext();
+ 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;
+ }
+
+
+ @Override
+ protected ConnectionReuseStrategy createConnectionReuseStrategy() {
+ return new DefaultConnectionReuseStrategy();
+ }
+
+ @Override
+ protected ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() {
+ return new DefaultConnectionKeepAliveStrategy();
+ }
+
+
+ @Override
+ protected AuthSchemeRegistry createAuthSchemeRegistry() {
+ AuthSchemeRegistry registry = new AuthSchemeRegistry();
+ registry.register(
+ AuthPolicy.BASIC,
+ new BasicSchemeFactory());
+ registry.register(
+ AuthPolicy.DIGEST,
+ new DigestSchemeFactory());
+ return registry;
+ }
+
+
+ @Override
+ protected CookieSpecRegistry createCookieSpecRegistry() {
+ 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());
+ return registry;
+ }
+
+
+ @Override
+ protected BasicHttpProcessor createHttpProcessor() {
+ 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 RequestConnControl());
+ 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 RequestTargetAuthentication());
+ httpproc.addInterceptor(new RequestProxyAuthentication());
+ return httpproc;
+ }
+
+
+ @Override
+ protected HttpRequestRetryHandler createHttpRequestRetryHandler() {
+ return new DefaultHttpRequestRetryHandler();
+ }
+
+
+ @Override
+ protected RedirectHandler createRedirectHandler() {
+ return new DefaultRedirectHandler();
+ }
+
+
+ @Override
+ protected AuthenticationHandler createTargetAuthenticationHandler() {
+ return new DefaultTargetAuthenticationHandler();
+ }
+
+
+ @Override
+ protected AuthenticationHandler createProxyAuthenticationHandler() {
+ return new DefaultProxyAuthenticationHandler();
+ }
+
+
+ @Override
+ protected CookieStore createCookieStore() {
+ return new BasicCookieStore();
+ }
+
+
+ @Override
+ protected CredentialsProvider createCredentialsProvider() {
+ return new BasicCredentialsProvider();
+ }
+
+
+ @Override
+ protected HttpRoutePlanner createHttpRoutePlanner() {
+ return new DefaultHttpRoutePlanner
+ (getConnectionManager().getSchemeRegistry());
+ }
+
+
+ @Override
+ protected UserTokenHandler createUserTokenHandler() {
+ return new DefaultUserTokenHandler();
+ }
+
+} // class DefaultHttpClient
diff --git a/src/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java b/src/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
new file mode 100644
index 0000000..7f66990
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
@@ -0,0 +1,134 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java $
+ * $Revision: 652726 $
+ * $Date: 2008-05-01 18:16:51 -0700 (Thu, 01 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.UnknownHostException;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+
+/**
+ * The default {@link HttpRequestRetryHandler} used by request executors.
+ *
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
+
+ /** 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;
+
+ /**
+ * Default constructor
+ */
+ public DefaultHttpRequestRetryHandler(int retryCount, boolean requestSentRetryEnabled) {
+ super();
+ this.retryCount = retryCount;
+ this.requestSentRetryEnabled = requestSentRetryEnabled;
+ }
+
+ /**
+ * Default constructor
+ */
+ public DefaultHttpRequestRetryHandler() {
+ this(3, false);
+ }
+ /**
+ * Used <code>retryCount</code> and <code>requestSentRetryEnabled</code> to determine
+ * if the given method should be retried.
+ */
+ public boolean retryRequest(
+ final IOException exception,
+ int executionCount,
+ final HttpContext context) {
+ if (exception == null) {
+ throw new IllegalArgumentException("Exception parameter may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+ if (executionCount > this.retryCount) {
+ // Do not retry if over max retry count
+ return false;
+ }
+ if (exception instanceof NoHttpResponseException) {
+ // Retry if the server dropped connection on us
+ return true;
+ }
+ if (exception instanceof InterruptedIOException) {
+ // Timeout
+ return false;
+ }
+ if (exception instanceof UnknownHostException) {
+ // Unknown host
+ return false;
+ }
+ if (exception instanceof SSLHandshakeException) {
+ // SSL handshake exception
+ return false;
+ }
+ Boolean b = (Boolean)
+ context.getAttribute(ExecutionContext.HTTP_REQ_SENT);
+ boolean sent = (b != null && b.booleanValue());
+ if (!sent || 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 <code>true</code> if this handler will retry methods that have
+ * successfully sent their request, <code>false</code> otherwise
+ */
+ public boolean isRequestSentRetryEnabled() {
+ return requestSentRetryEnabled;
+ }
+
+ /**
+ * @return the maximum number of times a method will be retried
+ */
+ public int getRetryCount() {
+ return retryCount;
+ }
+}
diff --git a/src/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java b/src/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java
new file mode 100644
index 0000000..c188da8
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java $
+ * $Revision: 603615 $
+ * $Date: 2007-12-12 06:03:21 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHandler {
+
+ public DefaultProxyAuthenticationHandler() {
+ super();
+ }
+
+ public boolean isAuthenticationRequested(
+ final HttpResponse response,
+ final HttpContext context) {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ int status = response.getStatusLine().getStatusCode();
+ return status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
+ }
+
+ public Map<String, Header> getChallenges(
+ final HttpResponse response,
+ final HttpContext context) throws MalformedChallengeException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ Header[] headers = response.getHeaders(AUTH.PROXY_AUTH);
+ return parseChallenges(headers);
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultRedirectHandler.java b/src/org/apache/http/impl/client/DefaultRedirectHandler.java
new file mode 100644
index 0000000..0811b28
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultRedirectHandler.java
@@ -0,0 +1,183 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultRedirectHandler.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolException;
+import org.apache.http.client.CircularRedirectException;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+
+
+/**
+ * Default implementation of {@link RedirectHandler}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class DefaultRedirectHandler implements RedirectHandler {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ private static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
+
+ public DefaultRedirectHandler() {
+ super();
+ }
+
+ public boolean isRedirectRequested(
+ final HttpResponse response,
+ final HttpContext context) {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ int statusCode = response.getStatusLine().getStatusCode();
+ switch (statusCode) {
+ case HttpStatus.SC_MOVED_TEMPORARILY:
+ case HttpStatus.SC_MOVED_PERMANENTLY:
+ case HttpStatus.SC_SEE_OTHER:
+ case HttpStatus.SC_TEMPORARY_REDIRECT:
+ return true;
+ default:
+ return false;
+ } //end of switch
+ }
+
+ public URI getLocationURI(
+ final HttpResponse response,
+ final HttpContext context) throws ProtocolException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ //get the location header to find out where to redirect to
+ 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");
+ }
+ String location = locationHeader.getValue();
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("Redirect requested to location '" + location + "'");
+ }
+
+ URI uri;
+ try {
+ uri = new URI(location);
+ } catch (URISyntaxException ex) {
+ throw new ProtocolException("Invalid redirect URI: " + location, ex);
+ }
+
+ 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
+ HttpHost target = (HttpHost) context.getAttribute(
+ ExecutionContext.HTTP_TARGET_HOST);
+ if (target == null) {
+ throw new IllegalStateException("Target host not available " +
+ "in the HTTP context");
+ }
+
+ HttpRequest request = (HttpRequest) context.getAttribute(
+ ExecutionContext.HTTP_REQUEST);
+
+ try {
+ URI requestURI = new URI(request.getRequestLine().getUri());
+ URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true);
+ uri = URIUtils.resolve(absoluteRequestURI, uri);
+ } catch (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);
+ }
+
+ URI redirectURI;
+ if (uri.getFragment() != null) {
+ try {
+ HttpHost target = new HttpHost(
+ uri.getHost(),
+ uri.getPort(),
+ uri.getScheme());
+ redirectURI = URIUtils.rewriteURI(uri, target, true);
+ } catch (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/src/org/apache/http/impl/client/DefaultRequestDirector.java b/src/org/apache/http/impl/client/DefaultRequestDirector.java
new file mode 100644
index 0000000..511f8a0
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultRequestDirector.java
@@ -0,0 +1,1088 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java $
+ * $Revision: 676023 $
+ * $Date: 2008-07-11 09:40:56 -0700 (Fri, 11 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.RequestDirector;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.NonRepeatableRequestException;
+import org.apache.http.client.RedirectException;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.AbortableHttpRequest;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.conn.BasicManagedEntity;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.params.ConnManagerParams;
+import org.apache.http.conn.routing.BasicRouteDirector;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRouteDirector;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestExecutor;
+
+/**
+ * Default implementation of {@link RequestDirector}.
+ * <br/>
+ * This class replaces the <code>HttpMethodDirector</code> in HttpClient 3.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 676023 $
+ *
+ * @since 4.0
+ */
+public class DefaultRequestDirector implements RequestDirector {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** 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. */
+ protected final RedirectHandler redirectHandler;
+
+ /** The target authentication handler. */
+ private final AuthenticationHandler targetAuthHandler;
+
+ /** The proxy authentication handler. */
+ private final AuthenticationHandler proxyAuthHandler;
+
+ /** The user token handler. */
+ private final UserTokenHandler userTokenHandler;
+
+ /** The HTTP parameters. */
+ protected final HttpParams params;
+
+ /** The currently allocated connection. */
+ protected ManagedClientConnection managedConn;
+
+ private int redirectCount;
+
+ private int maxRedirects;
+
+ private final AuthState targetAuthState;
+
+ private final AuthState proxyAuthState;
+
+ 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) {
+
+ if (requestExec == null) {
+ throw new IllegalArgumentException
+ ("Request executor may not be null.");
+ }
+ if (conman == null) {
+ throw new IllegalArgumentException
+ ("Client connection manager may not be null.");
+ }
+ if (reustrat == null) {
+ throw new IllegalArgumentException
+ ("Connection reuse strategy may not be null.");
+ }
+ if (kastrat == null) {
+ throw new IllegalArgumentException
+ ("Connection keep alive strategy may not be null.");
+ }
+ if (rouplan == null) {
+ throw new IllegalArgumentException
+ ("Route planner may not be null.");
+ }
+ if (httpProcessor == null) {
+ throw new IllegalArgumentException
+ ("HTTP protocol processor may not be null.");
+ }
+ if (retryHandler == null) {
+ throw new IllegalArgumentException
+ ("HTTP request retry handler may not be null.");
+ }
+ if (redirectHandler == null) {
+ throw new IllegalArgumentException
+ ("Redirect handler may not be null.");
+ }
+ if (targetAuthHandler == null) {
+ throw new IllegalArgumentException
+ ("Target authentication handler may not be null.");
+ }
+ if (proxyAuthHandler == null) {
+ throw new IllegalArgumentException
+ ("Proxy authentication handler may not be null.");
+ }
+ if (userTokenHandler == null) {
+ throw new IllegalArgumentException
+ ("User token handler may not be null.");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("HTTP parameters may not be null");
+ }
+ this.requestExec = requestExec;
+ this.connManager = conman;
+ this.reuseStrategy = reustrat;
+ this.keepAliveStrategy = kastrat;
+ this.routePlanner = rouplan;
+ this.httpProcessor = httpProcessor;
+ this.retryHandler = retryHandler;
+ this.redirectHandler = redirectHandler;
+ this.targetAuthHandler = targetAuthHandler;
+ this.proxyAuthHandler = proxyAuthHandler;
+ this.userTokenHandler = userTokenHandler;
+ this.params = params;
+
+ this.managedConn = null;
+
+ this.redirectCount = 0;
+ this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
+ this.targetAuthState = new AuthState();
+ this.proxyAuthState = new AuthState();
+ } // constructor
+
+
+ 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()) {
+ HttpHost target = route.getTargetHost();
+ uri = URIUtils.rewriteURI(uri, target);
+ request.setURI(uri);
+ }
+ } else {
+ // Make sure the request URI is relative
+ if (uri.isAbsolute()) {
+ uri = URIUtils.rewriteURI(uri, null);
+ request.setURI(uri);
+ }
+ }
+
+ } catch (URISyntaxException ex) {
+ throw new ProtocolException("Invalid URI: " +
+ request.getRequestLine().getUri(), ex);
+ }
+ }
+
+
+ // non-javadoc, see interface ClientRequestDirector
+ public HttpResponse execute(HttpHost target, HttpRequest request,
+ HttpContext context)
+ throws HttpException, IOException {
+
+ HttpRequest orig = request;
+ RequestWrapper origWrapper = wrapRequest(orig);
+ origWrapper.setParams(params);
+ HttpRoute origRoute = determineRoute(target, origWrapper, context);
+
+ RoutedRequest roureq = new RoutedRequest(origWrapper, origRoute);
+
+ long timeout = ConnManagerParams.getTimeout(params);
+
+ int execCount = 0;
+
+ boolean reuse = false;
+ HttpResponse response = null;
+ boolean done = false;
+ try {
+ 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'.
+
+ RequestWrapper wrapper = roureq.getRequest();
+ HttpRoute route = roureq.getRoute();
+
+ // 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) {
+ ClientConnectionRequest connRequest = connManager.requestConnection(
+ route, userToken);
+ if (orig instanceof AbortableHttpRequest) {
+ ((AbortableHttpRequest) orig).setConnectionRequest(connRequest);
+ }
+
+ try {
+ managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS);
+ } catch(InterruptedException interrupted) {
+ InterruptedIOException iox = new InterruptedIOException();
+ iox.initCause(interrupted);
+ throw iox;
+ }
+
+ if (HttpConnectionParams.isStaleCheckingEnabled(params)) {
+ // validate connection
+ 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);
+ }
+
+ // Reopen connection if needed
+ if (!managedConn.isOpen()) {
+ managedConn.open(route, context, params);
+ }
+
+ try {
+ establishRoute(route, context);
+ } catch (TunnelRefusedException ex) {
+ if (this.log.isDebugEnabled()) {
+ this.log.debug(ex.getMessage());
+ }
+ response = ex.getResponse();
+ break;
+ }
+
+ // Reset headers on the request wrapper
+ wrapper.resetHeaders();
+
+ // Re-write request URI if needed
+ rewriteRequestURI(wrapper, route);
+
+ // Use virtual host if set
+ target = (HttpHost) wrapper.getParams().getParameter(
+ ClientPNames.VIRTUAL_HOST);
+
+ if (target == null) {
+ target = route.getTargetHost();
+ }
+
+ HttpHost proxy = route.getProxyHost();
+
+ // Populate the execution context
+ context.setAttribute(ExecutionContext.HTTP_TARGET_HOST,
+ target);
+ context.setAttribute(ExecutionContext.HTTP_PROXY_HOST,
+ proxy);
+ context.setAttribute(ExecutionContext.HTTP_CONNECTION,
+ managedConn);
+ context.setAttribute(ClientContext.TARGET_AUTH_STATE,
+ targetAuthState);
+ context.setAttribute(ClientContext.PROXY_AUTH_STATE,
+ proxyAuthState);
+
+ // Run request protocol interceptors
+ requestExec.preProcess(wrapper, httpProcessor, context);
+
+ context.setAttribute(ExecutionContext.HTTP_REQUEST,
+ wrapper);
+
+ boolean retrying = true;
+ while (retrying) {
+ // Increment total exec count (with redirects)
+ execCount++;
+ // Increment exec count for this particular request
+ wrapper.incrementExecCount();
+ if (wrapper.getExecCount() > 1 && !wrapper.isRepeatable()) {
+ throw new NonRepeatableRequestException("Cannot retry request " +
+ "with a non-repeatable request entity");
+ }
+
+ try {
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("Attempt " + execCount + " to execute request");
+ }
+ response = requestExec.execute(wrapper, managedConn, context);
+ retrying = false;
+
+ } catch (IOException ex) {
+ this.log.debug("Closing the connection.");
+ managedConn.close();
+ if (retryHandler.retryRequest(ex, execCount, context)) {
+ if (this.log.isInfoEnabled()) {
+ this.log.info("I/O exception ("+ ex.getClass().getName() +
+ ") caught when processing request: "
+ + ex.getMessage());
+ }
+ if (this.log.isDebugEnabled()) {
+ this.log.debug(ex.getMessage(), ex);
+ }
+ this.log.info("Retrying request");
+ } else {
+ throw ex;
+ }
+
+ // If we have a direct route to the target host
+ // just re-open connection and re-try the request
+ if (route.getHopCount() == 1) {
+ this.log.debug("Reopening the direct connection.");
+ managedConn.open(route, context, params);
+ } else {
+ // otherwise give up
+ retrying = false;
+ }
+
+ }
+
+ }
+
+ // 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
+ long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
+ managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS);
+ }
+
+ RoutedRequest followup = handleResponse(roureq, response, context);
+ if (followup == null) {
+ done = true;
+ } else {
+ if (reuse) {
+ this.log.debug("Connection kept alive");
+ // Make sure the response body is fully consumed, if present
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ // entity consumed above is not an auto-release entity,
+ // need to mark the connection re-usable explicitly
+ managedConn.markReusable();
+ } else {
+ managedConn.close();
+ }
+ // check if we can use the same connection for the followup
+ if (!followup.getRoute().equals(roureq.getRoute())) {
+ releaseConnection();
+ }
+ roureq = followup;
+ }
+
+ userToken = this.userTokenHandler.getUserToken(context);
+ context.setAttribute(ClientContext.USER_TOKEN, userToken);
+ if (managedConn != 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 (HttpException ex) {
+ abortConnection();
+ throw ex;
+ } catch (IOException ex) {
+ abortConnection();
+ throw ex;
+ } catch (RuntimeException ex) {
+ abortConnection();
+ throw ex;
+ }
+ } // execute
+
+ /**
+ * 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(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 target the target host for the request.
+ * Implementations may accept <code>null</code>
+ * 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 <code>null</code>
+ *
+ * @return the route the request should take
+ *
+ * @throws HttpException in case of a problem
+ */
+ protected HttpRoute determineRoute(HttpHost target,
+ HttpRequest request,
+ HttpContext context)
+ throws HttpException {
+
+ if (target == null) {
+ target = (HttpHost) request.getParams().getParameter(
+ ClientPNames.DEFAULT_HOST);
+ }
+ if (target == null) {
+ throw new IllegalStateException
+ ("Target host must not be null, or set in parameters.");
+ }
+
+ return this.routePlanner.determineRoute(target, 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(HttpRoute route, HttpContext context)
+ throws HttpException, IOException {
+
+ //@@@ how to handle CONNECT requests for tunnelling?
+ //@@@ refuse to send external CONNECT via director? special handling?
+
+ //@@@ should the request parameters already be used below?
+ //@@@ probably yes, but they're not linked yet
+ //@@@ will linking above cause problems with linking in reqExec?
+ //@@@ probably not, because the parent is replaced
+ //@@@ just make sure we don't link parameters to themselves
+
+ HttpRouteDirector rowdy = new BasicRouteDirector();
+ int step;
+ do {
+ 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: {
+ 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
+ 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 IllegalStateException
+ ("Unable to establish route." +
+ "\nplanned = " + route +
+ "\ncurrent = " + fact);
+
+ case HttpRouteDirector.COMPLETE:
+ // do nothing
+ break;
+
+ default:
+ throw new IllegalStateException
+ ("Unknown step indicator "+step+" from RouteDirector.");
+ } // switch
+
+ } 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 <i>not</i> 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 <code>true</code> if the tunnelled route is secure,
+ * <code>false</code> otherwise.
+ * The implementation here always returns <code>false</code>,
+ * but derived classes may override.
+ *
+ * @throws HttpException in case of a problem
+ * @throws IOException in case of an IO problem
+ */
+ protected boolean createTunnelToTarget(HttpRoute route,
+ HttpContext context)
+ throws HttpException, IOException {
+
+ HttpHost proxy = route.getProxyHost();
+ HttpHost target = route.getTargetHost();
+ HttpResponse response = null;
+
+ boolean done = false;
+ while (!done) {
+
+ done = true;
+
+ if (!this.managedConn.isOpen()) {
+ this.managedConn.open(route, context, this.params);
+ }
+
+ HttpRequest connect = createConnectRequest(route, context);
+
+ String agent = HttpProtocolParams.getUserAgent(params);
+ if (agent != null) {
+ connect.addHeader(HTTP.USER_AGENT, agent);
+ }
+ connect.addHeader(HTTP.TARGET_HOST, target.toHostString());
+
+ AuthScheme authScheme = this.proxyAuthState.getAuthScheme();
+ AuthScope authScope = this.proxyAuthState.getAuthScope();
+ Credentials creds = this.proxyAuthState.getCredentials();
+ if (creds != null) {
+ if (authScope != null || !authScheme.isConnectionBased()) {
+ try {
+ connect.addHeader(authScheme.authenticate(creds, connect));
+ } catch (AuthenticationException ex) {
+ if (this.log.isErrorEnabled()) {
+ this.log.error("Proxy authentication error: " + ex.getMessage());
+ }
+ }
+ }
+ }
+
+ response = requestExec.execute(connect, this.managedConn, context);
+
+ int status = response.getStatusLine().getStatusCode();
+ if (status < 200) {
+ throw new HttpException("Unexpected response to CONNECT request: " +
+ response.getStatusLine());
+ }
+
+ CredentialsProvider credsProvider = (CredentialsProvider)
+ context.getAttribute(ClientContext.CREDS_PROVIDER);
+
+ if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
+ if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
+
+ this.log.debug("Proxy requested authentication");
+ Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(
+ response, context);
+ try {
+ processChallenges(
+ challenges, this.proxyAuthState, this.proxyAuthHandler,
+ response, context);
+ } catch (AuthenticationException ex) {
+ if (this.log.isWarnEnabled()) {
+ this.log.warn("Authentication error: " + ex.getMessage());
+ break;
+ }
+ }
+ updateAuthState(this.proxyAuthState, proxy, credsProvider);
+
+ if (this.proxyAuthState.getCredentials() != null) {
+ done = false;
+
+ // Retry request
+ if (this.reuseStrategy.keepAlive(response, context)) {
+ this.log.debug("Connection kept alive");
+ // Consume response content
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ } else {
+ this.managedConn.close();
+ }
+
+ }
+
+ } else {
+ // Reset proxy auth scope
+ this.proxyAuthState.setAuthScope(null);
+ }
+ }
+ }
+
+ int status = response.getStatusLine().getStatusCode();
+
+ if (status > 299) {
+
+ // Buffer response content
+ 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 <i>not</i> 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.
+ * <code>route.getHopTarget(hop)</code>
+ * will return the proxy to tunnel to.
+ * @param context the context for request execution
+ *
+ * @return <code>true</code> if the partially tunnelled connection
+ * is secure, <code>false</code> otherwise.
+ *
+ * @throws HttpException in case of a problem
+ * @throws IOException in case of an IO problem
+ */
+ protected boolean createTunnelToProxy(HttpRoute route, int hop,
+ 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 UnsupportedOperationException
+ ("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(HttpRoute route,
+ HttpContext context) {
+ // see RFC 2817, section 5.2 and
+ // INTERNET-DRAFT: Tunneling TCP based protocols through
+ // Web proxy servers
+
+ HttpHost target = route.getTargetHost();
+
+ String host = target.getHostName();
+ int port = target.getPort();
+ if (port < 0) {
+ Scheme scheme = connManager.getSchemeRegistry().
+ getScheme(target.getSchemeName());
+ port = scheme.getDefaultPort();
+ }
+
+ StringBuilder buffer = new StringBuilder(host.length() + 6);
+ buffer.append(host);
+ buffer.append(':');
+ buffer.append(Integer.toString(port));
+
+ String authority = buffer.toString();
+ ProtocolVersion ver = HttpProtocolParams.getVersion(params);
+ 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
+ * <code>null</code> 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(RoutedRequest roureq,
+ HttpResponse response,
+ HttpContext context)
+ throws HttpException, IOException {
+
+ HttpRoute route = roureq.getRoute();
+ HttpHost proxy = route.getProxyHost();
+ RequestWrapper request = roureq.getRequest();
+
+ HttpParams params = request.getParams();
+ if (HttpClientParams.isRedirecting(params) &&
+ this.redirectHandler.isRedirectRequested(response, context)) {
+
+ if (redirectCount >= maxRedirects) {
+ throw new RedirectException("Maximum redirects ("
+ + maxRedirects + ") exceeded");
+ }
+ redirectCount++;
+
+ URI uri = this.redirectHandler.getLocationURI(response, context);
+
+ HttpHost newTarget = new HttpHost(
+ uri.getHost(),
+ uri.getPort(),
+ uri.getScheme());
+
+ HttpGet redirect = new HttpGet(uri);
+
+ HttpRequest orig = request.getOriginal();
+ redirect.setHeaders(orig.getAllHeaders());
+
+ RequestWrapper wrapper = new RequestWrapper(redirect);
+ wrapper.setParams(params);
+
+ HttpRoute newRoute = determineRoute(newTarget, wrapper, context);
+ RoutedRequest newRequest = new RoutedRequest(wrapper, newRoute);
+
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("Redirecting to '" + uri + "' via " + newRoute);
+ }
+
+ return newRequest;
+ }
+
+ CredentialsProvider credsProvider = (CredentialsProvider)
+ context.getAttribute(ClientContext.CREDS_PROVIDER);
+
+ if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
+
+ if (this.targetAuthHandler.isAuthenticationRequested(response, context)) {
+
+ HttpHost target = (HttpHost)
+ context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+ if (target == null) {
+ target = route.getTargetHost();
+ }
+
+ this.log.debug("Target requested authentication");
+ Map<String, Header> challenges = this.targetAuthHandler.getChallenges(
+ response, context);
+ try {
+ processChallenges(challenges,
+ this.targetAuthState, this.targetAuthHandler,
+ response, context);
+ } catch (AuthenticationException ex) {
+ if (this.log.isWarnEnabled()) {
+ this.log.warn("Authentication error: " + ex.getMessage());
+ return null;
+ }
+ }
+ updateAuthState(this.targetAuthState, target, credsProvider);
+
+ if (this.targetAuthState.getCredentials() != null) {
+ // Re-try the same request via the same route
+ return roureq;
+ } else {
+ return null;
+ }
+ } else {
+ // Reset target auth scope
+ this.targetAuthState.setAuthScope(null);
+ }
+
+ if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
+
+ this.log.debug("Proxy requested authentication");
+ Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(
+ response, context);
+ try {
+ processChallenges(challenges,
+ this.proxyAuthState, this.proxyAuthHandler,
+ response, context);
+ } catch (AuthenticationException ex) {
+ if (this.log.isWarnEnabled()) {
+ this.log.warn("Authentication error: " + ex.getMessage());
+ return null;
+ }
+ }
+ updateAuthState(this.proxyAuthState, proxy, credsProvider);
+
+ if (this.proxyAuthState.getCredentials() != null) {
+ // Re-try the same request via the same route
+ return roureq;
+ } else {
+ return null;
+ }
+ } else {
+ // Reset proxy auth scope
+ this.proxyAuthState.setAuthScope(null);
+ }
+ }
+ return null;
+ } // handleResponse
+
+
+ /**
+ * Shuts down the connection.
+ * This method is called from a <code>catch</code> block in
+ * {@link #execute execute} during exception handling.
+ */
+ private void abortConnection() {
+ 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 (IOException ex) {
+ if (this.log.isDebugEnabled()) {
+ this.log.debug(ex.getMessage(), ex);
+ }
+ }
+ // ensure the connection manager properly releases this connection
+ try {
+ mcc.releaseConnection();
+ } catch(IOException ignored) {
+ this.log.debug("Error releasing connection", ignored);
+ }
+ }
+ } // abortConnection
+
+
+ private void processChallenges(
+ final Map<String, Header> challenges,
+ final AuthState authState,
+ final AuthenticationHandler authHandler,
+ final HttpResponse response,
+ final HttpContext context)
+ throws MalformedChallengeException, AuthenticationException {
+
+ AuthScheme authScheme = authState.getAuthScheme();
+ if (authScheme == null) {
+ // Authentication not attempted before
+ authScheme = authHandler.selectScheme(challenges, response, context);
+ authState.setAuthScheme(authScheme);
+ }
+ String id = authScheme.getSchemeName();
+
+ Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH));
+ if (challenge == null) {
+ throw new AuthenticationException(id +
+ " authorization challenge expected, but not found");
+ }
+ authScheme.processChallenge(challenge);
+ this.log.debug("Authorization challenge processed");
+ }
+
+
+ private void updateAuthState(
+ final AuthState authState,
+ final HttpHost host,
+ final CredentialsProvider credsProvider) {
+
+ if (!authState.isValid()) {
+ return;
+ }
+
+ String hostname = host.getHostName();
+ int port = host.getPort();
+ if (port < 0) {
+ Scheme scheme = connManager.getSchemeRegistry().getScheme(host);
+ port = scheme.getDefaultPort();
+ }
+
+ AuthScheme authScheme = authState.getAuthScheme();
+ AuthScope authScope = new AuthScope(
+ hostname,
+ port,
+ authScheme.getRealm(),
+ authScheme.getSchemeName());
+
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("Authentication scope: " + authScope);
+ }
+ Credentials creds = authState.getCredentials();
+ if (creds == null) {
+ creds = credsProvider.getCredentials(authScope);
+ if (this.log.isDebugEnabled()) {
+ if (creds != null) {
+ this.log.debug("Found credentials");
+ } else {
+ this.log.debug("Credentials not found");
+ }
+ }
+ } else {
+ if (authScheme.isComplete()) {
+ this.log.debug("Authentication failed");
+ creds = null;
+ }
+ }
+ authState.setAuthScope(authScope);
+ authState.setCredentials(creds);
+ }
+
+} // class DefaultClientRequestDirector
diff --git a/src/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java b/src/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java
new file mode 100644
index 0000000..5794549
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java $
+ * $Revision: 603615 $
+ * $Date: 2007-12-12 06:03:21 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHandler {
+
+ public DefaultTargetAuthenticationHandler() {
+ super();
+ }
+
+ public boolean isAuthenticationRequested(
+ final HttpResponse response,
+ final HttpContext context) {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ int status = response.getStatusLine().getStatusCode();
+ return status == HttpStatus.SC_UNAUTHORIZED;
+ }
+
+ public Map<String, Header> getChallenges(
+ final HttpResponse response,
+ final HttpContext context) throws MalformedChallengeException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ Header[] headers = response.getHeaders(AUTH.WWW_AUTH);
+ return parseChallenges(headers);
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/DefaultUserTokenHandler.java b/src/org/apache/http/impl/client/DefaultUserTokenHandler.java
new file mode 100644
index 0000000..c8a409f
--- /dev/null
+++ b/src/org/apache/http/impl/client/DefaultUserTokenHandler.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultUserTokenHandler.java $
+ * $Revision: 659971 $
+ * $Date: 2008-05-25 05:01:22 -0700 (Sun, 25 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.security.Principal;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+
+public class DefaultUserTokenHandler implements UserTokenHandler {
+
+ public Object getUserToken(final HttpContext context) {
+
+ Principal userPrincipal = null;
+
+ AuthState targetAuthState = (AuthState) context.getAttribute(
+ ClientContext.TARGET_AUTH_STATE);
+ if (targetAuthState != null) {
+ userPrincipal = getAuthPrincipal(targetAuthState);
+ if (userPrincipal == null) {
+ AuthState proxyAuthState = (AuthState) context.getAttribute(
+ ClientContext.PROXY_AUTH_STATE);
+ userPrincipal = getAuthPrincipal(proxyAuthState);
+ }
+ }
+
+ if (userPrincipal == null) {
+ ManagedClientConnection conn = (ManagedClientConnection) context.getAttribute(
+ ExecutionContext.HTTP_CONNECTION);
+ if (conn.isOpen()) {
+ SSLSession sslsession = conn.getSSLSession();
+ if (sslsession != null) {
+ userPrincipal = sslsession.getLocalPrincipal();
+ }
+ }
+ }
+
+ return userPrincipal;
+ }
+
+ private static Principal getAuthPrincipal(final AuthState authState) {
+ AuthScheme scheme = authState.getAuthScheme();
+ if (scheme != null && scheme.isComplete() && scheme.isConnectionBased()) {
+ Credentials creds = authState.getCredentials();
+ if (creds != null) {
+ return creds.getUserPrincipal();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java b/src/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java
new file mode 100644
index 0000000..05098cf
--- /dev/null
+++ b/src/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.ProtocolException;
+import org.apache.http.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.
+ * </p>
+ * This class is also capable of resetting the request headers to
+ * the state of the original request.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ *
+ * @since 4.0
+ */
+public class EntityEnclosingRequestWrapper extends RequestWrapper
+ implements HttpEntityEnclosingRequest {
+
+ private HttpEntity entity;
+
+ public EntityEnclosingRequestWrapper(final HttpEntityEnclosingRequest request)
+ throws ProtocolException {
+ super(request);
+ this.entity = request.getEntity();
+ }
+
+ public HttpEntity getEntity() {
+ return this.entity;
+ }
+
+ public void setEntity(final HttpEntity entity) {
+ this.entity = entity;
+ }
+
+ public boolean expectContinue() {
+ 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();
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/RedirectLocations.java b/src/org/apache/http/impl/client/RedirectLocations.java
new file mode 100644
index 0000000..d5c47e7
--- /dev/null
+++ b/src/org/apache/http/impl/client/RedirectLocations.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/RedirectLocations.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A collection of URIs that were used as redirects.
+ */
+public class RedirectLocations {
+
+ private final Set<URI> uris;
+
+ public RedirectLocations() {
+ super();
+ this.uris = new HashSet<URI>();
+ }
+
+ /**
+ * Returns true if this collection contains the given URI.
+ */
+ public boolean contains(final URI uri) {
+ return this.uris.contains(uri);
+ }
+
+ /**
+ * Adds a new URI to the list of redirects.
+ */
+ public void add(final URI uri) {
+ this.uris.add(uri);
+ }
+
+ /**
+ * Removes a URI from the list of redirects.
+ */
+ public boolean remove(final URI uri) {
+ return this.uris.remove(uri);
+ }
+
+}
diff --git a/src/org/apache/http/impl/client/RequestWrapper.java b/src/org/apache/http/impl/client/RequestWrapper.java
new file mode 100644
index 0000000..04a641d
--- /dev/null
+++ b/src/org/apache/http/impl/client/RequestWrapper.java
@@ -0,0 +1,170 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/RequestWrapper.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.message.AbstractHttpMessage;
+import org.apache.http.message.BasicRequestLine;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * A wrapper class for {@link HttpRequest}s that can be used to change
+ * properties of the current request without modifying the original
+ * object.
+ * </p>
+ * This class is also capable of resetting the request headers to
+ * the state of the original request.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 674186 $
+ *
+ * @since 4.0
+ */
+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();
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ this.original = request;
+ setParams(request.getParams());
+ // 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 {
+ RequestLine requestLine = request.getRequestLine();
+ try {
+ this.uri = new URI(requestLine.getUri());
+ } catch (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) {
+ if (method == null) {
+ throw new IllegalArgumentException("Method name may not be null");
+ }
+ this.method = method;
+ }
+
+ public ProtocolVersion getProtocolVersion() {
+ if (this.version != null) {
+ return this.version;
+ } else {
+ return HttpProtocolParams.getVersion(getParams());
+ }
+ }
+
+ 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() {
+ String method = getMethod();
+ 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/src/org/apache/http/impl/client/RoutedRequest.java b/src/org/apache/http/impl/client/RoutedRequest.java
new file mode 100644
index 0000000..954ebe5
--- /dev/null
+++ b/src/org/apache/http/impl/client/RoutedRequest.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/RoutedRequest.java $
+ * $Revision: 645846 $
+ * $Date: 2008-04-08 03:53:39 -0700 (Tue, 08 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+
+/**
+ * A request with the route along which it should be sent.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 645846 $
+ *
+ * @since 4.0
+ */
+public class RoutedRequest {
+
+ protected final RequestWrapper request;
+ protected final HttpRoute route;
+
+ /**
+ * 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;
+ }
+
+} // interface RoutedRequest
diff --git a/src/org/apache/http/impl/client/TunnelRefusedException.java b/src/org/apache/http/impl/client/TunnelRefusedException.java
new file mode 100644
index 0000000..601626f
--- /dev/null
+++ b/src/org/apache/http/impl/client/TunnelRefusedException.java
@@ -0,0 +1,52 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/TunnelRefusedException.java $
+ * $Revision: 537650 $
+ * $Date: 2007-05-13 12:58:22 -0700 (Sun, 13 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+
+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/src/org/apache/http/impl/client/package.html b/src/org/apache/http/impl/client/package.html
new file mode 100644
index 0000000..e301283
--- /dev/null
+++ b/src/org/apache/http/impl/client/package.html
@@ -0,0 +1,4 @@
+<body>
+
+</body>
+
diff --git a/src/org/apache/http/impl/conn/AbstractClientConnAdapter.java b/src/org/apache/http/impl/conn/AbstractClientConnAdapter.java
new file mode 100644
index 0000000..5cbe010
--- /dev/null
+++ b/src/org/apache/http/impl/conn/AbstractClientConnAdapter.java
@@ -0,0 +1,399 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractClientConnAdapter.java $
+ * $Revision: 672969 $
+ * $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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.SSLSocket;
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.ClientConnectionManager;
+
+
+/**
+ * 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.
+ * <br/>
+ * 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.
+ * <br/>
+ * 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.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 672969 $ $Date: 2008-06-30 18:09:50 -0700 (Mon, 30 Jun 2008) $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractClientConnAdapter
+ implements ManagedClientConnection {
+
+ /** Thread that requested this connection. */
+ private final Thread executionThread;
+
+ /**
+ * The connection manager, if any.
+ * This attribute MUST NOT be final, so the adapter can be detached
+ * from the connection manager without keeping a hard reference there.
+ */
+ private volatile ClientConnectionManager connManager;
+
+ /** The wrapped connection. */
+ private volatile OperatedClientConnection wrappedConnection;
+
+ /** The reusability marker. */
+ private volatile boolean markedReusable;
+
+ /** True if the connection has been aborted. */
+ private volatile boolean aborted;
+
+ /** The duration this is valid for while idle (in ms). */
+ private volatile long duration;
+
+ /**
+ * Creates a new connection adapter.
+ * The adapter is initially <i>not</i>
+ * {@link #isMarkedReusable marked} as reusable.
+ *
+ * @param mgr the connection manager, or <code>null</code>
+ * @param conn the connection to wrap, or <code>null</code>
+ */
+ protected AbstractClientConnAdapter(ClientConnectionManager mgr,
+ OperatedClientConnection conn) {
+ super();
+ executionThread = Thread.currentThread();
+ connManager = mgr;
+ wrappedConnection = conn;
+ markedReusable = false;
+ aborted = false;
+ duration = Long.MAX_VALUE;
+ } // <constructor>
+
+
+ /**
+ * Detaches this adapter from the wrapped connection.
+ * This adapter becomes useless.
+ */
+ protected void detach() {
+ wrappedConnection = null;
+ connManager = null; // base class attribute
+ duration = Long.MAX_VALUE;
+ }
+
+ protected OperatedClientConnection getWrappedConnection() {
+ return wrappedConnection;
+ }
+
+ protected ClientConnectionManager getManager() {
+ return connManager;
+ }
+
+ /**
+ * Asserts that the connection has not been aborted.
+ *
+ * @throws InterruptedIOException if the connection has been aborted
+ */
+ protected final void assertNotAborted() throws InterruptedIOException {
+ if (aborted) {
+ throw new InterruptedIOException("Connection has been shut down.");
+ }
+ }
+
+ /**
+ * Asserts that there is a wrapped connection to delegate to.
+ *
+ * @throws IllegalStateException if there is no wrapped connection
+ * or connection has been aborted
+ */
+ protected final void assertValid(
+ final OperatedClientConnection wrappedConn) {
+ if (wrappedConn == null) {
+ throw new IllegalStateException("No wrapped connection.");
+ }
+ }
+
+ // non-javadoc, see interface HttpConnection
+ public boolean isOpen() {
+ OperatedClientConnection conn = getWrappedConnection();
+ if (conn == null)
+ return false;
+
+ return conn.isOpen();
+ }
+
+
+ // non-javadoc, see interface HttpConnection
+ public boolean isStale() {
+ if (aborted)
+ return true;
+ OperatedClientConnection conn = getWrappedConnection();
+ if (conn == null)
+ return true;
+
+ return conn.isStale();
+ }
+
+
+ // non-javadoc, see interface HttpConnection
+ public void setSocketTimeout(int timeout) {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ conn.setSocketTimeout(timeout);
+ }
+
+
+ // non-javadoc, see interface HttpConnection
+ public int getSocketTimeout() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ return conn.getSocketTimeout();
+ }
+
+
+ // non-javadoc, see interface HttpConnection
+ public HttpConnectionMetrics getMetrics() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ return conn.getMetrics();
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void flush()
+ throws IOException {
+
+ assertNotAborted();
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+
+ conn.flush();
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public boolean isResponseAvailable(int timeout)
+ throws IOException {
+
+ assertNotAborted();
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+
+ return conn.isResponseAvailable(timeout);
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void receiveResponseEntity(HttpResponse response)
+ throws HttpException, IOException {
+
+ assertNotAborted();
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+
+ unmarkReusable();
+ conn.receiveResponseEntity(response);
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public HttpResponse receiveResponseHeader()
+ throws HttpException, IOException {
+
+ assertNotAborted();
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+
+ unmarkReusable();
+ return conn.receiveResponseHeader();
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void sendRequestEntity(HttpEntityEnclosingRequest request)
+ throws HttpException, IOException {
+
+ assertNotAborted();
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+
+ unmarkReusable();
+ conn.sendRequestEntity(request);
+ }
+
+
+ // non-javadoc, see interface HttpClientConnection
+ public void sendRequestHeader(HttpRequest request)
+ throws HttpException, IOException {
+
+ assertNotAborted();
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+
+ unmarkReusable();
+ conn.sendRequestHeader(request);
+ }
+
+
+ // non-javadoc, see interface HttpInetConnection
+ public InetAddress getLocalAddress() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ return conn.getLocalAddress();
+ }
+
+ // non-javadoc, see interface HttpInetConnection
+ public int getLocalPort() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ return conn.getLocalPort();
+ }
+
+
+ // non-javadoc, see interface HttpInetConnection
+ public InetAddress getRemoteAddress() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ return conn.getRemoteAddress();
+ }
+
+ // non-javadoc, see interface HttpInetConnection
+ public int getRemotePort() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ return conn.getRemotePort();
+ }
+
+ // non-javadoc, see interface ManagedClientConnection
+ public boolean isSecure() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ return conn.isSecure();
+ }
+
+ // non-javadoc, see interface ManagedClientConnection
+ public SSLSession getSSLSession() {
+ OperatedClientConnection conn = getWrappedConnection();
+ assertValid(conn);
+ if (!isOpen())
+ return null;
+
+ SSLSession result = null;
+ Socket sock = conn.getSocket();
+ if (sock instanceof SSLSocket) {
+ result = ((SSLSocket)sock).getSession();
+ }
+ return result;
+ }
+
+ // non-javadoc, see interface ManagedClientConnection
+ public void markReusable() {
+ markedReusable = true;
+ }
+
+ // non-javadoc, see interface ManagedClientConnection
+ public void unmarkReusable() {
+ markedReusable = false;
+ }
+
+ // non-javadoc, see interface ManagedClientConnection
+ public boolean isMarkedReusable() {
+ return markedReusable;
+ }
+
+ public void setIdleDuration(long duration, TimeUnit unit) {
+ if(duration > 0) {
+ this.duration = unit.toMillis(duration);
+ } else {
+ this.duration = -1;
+ }
+ }
+
+ // non-javadoc, see interface ConnectionReleaseTrigger
+ public void releaseConnection() {
+ if (connManager != null) {
+ connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ // non-javadoc, see interface ConnectionReleaseTrigger
+ public void abortConnection() {
+ if (aborted) {
+ return;
+ }
+ aborted = true;
+ unmarkReusable();
+ try {
+ shutdown();
+ } catch (IOException ignore) {
+ }
+ // Usually #abortConnection() is expected to be called from
+ // a helper thread in order to unblock the main execution thread
+ // blocked in an I/O operation. It may be unsafe to call
+ // #releaseConnection() from the helper thread, so we have to rely
+ // on an IOException thrown by the closed socket on the main thread
+ // to trigger the release of the connection back to the
+ // connection manager.
+ //
+ // However, if this method is called from the main execution thread
+ // it should be safe to release the connection immediately. Besides,
+ // this also helps ensure the connection gets released back to the
+ // manager if #abortConnection() is called from the main execution
+ // thread while there is no blocking I/O operation.
+ if (executionThread.equals(Thread.currentThread())) {
+ releaseConnection();
+ }
+ }
+
+} // class AbstractClientConnAdapter
diff --git a/src/org/apache/http/impl/conn/AbstractPoolEntry.java b/src/org/apache/http/impl/conn/AbstractPoolEntry.java
new file mode 100644
index 0000000..0e7d95f
--- /dev/null
+++ b/src/org/apache/http/impl/conn/AbstractPoolEntry.java
@@ -0,0 +1,322 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractPoolEntry.java $
+ * $Revision: 658775 $
+ * $Date: 2008-05-21 10:30:45 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.OperatedClientConnection;
+
+
+
+/**
+ * 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
+ * it's pool entry.
+ * <br/>
+ * 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.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 658775 $
+ *
+ * @since 4.0
+ */
+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 <code>null</code> 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 <code>null</code>
+ */
+ protected AbstractPoolEntry(ClientConnectionOperator connOperator,
+ HttpRoute route) {
+ super();
+ if (connOperator == null) {
+ throw new IllegalArgumentException("Connection operator may not be null");
+ }
+ 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(HttpRoute route,
+ HttpContext context, HttpParams params)
+ throws IOException {
+
+ if (route == null) {
+ throw new IllegalArgumentException
+ ("Route must not be null.");
+ }
+ //@@@ is context allowed to be null? depends on operator?
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+ if ((this.tracker != null) && this.tracker.isConnected()) {
+ throw new IllegalStateException("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.
+
+ //@@@ verify route against planned route?
+
+ this.tracker = new RouteTracker(route);
+ final HttpHost proxy = route.getProxyHost();
+
+ connOperator.openConnection
+ (this.connection,
+ (proxy != null) ? proxy : route.getTargetHost(),
+ route.getLocalAddress(),
+ context, params);
+
+ RouteTracker localTracker = tracker; // capture volatile
+
+ // If this tracker was reset while connecting,
+ // fail early.
+ if (localTracker == null) {
+ throw new IOException("Request aborted");
+ }
+
+ if (proxy == null) {
+ localTracker.connectTarget(this.connection.isSecure());
+ } else {
+ localTracker.connectProxy(proxy, this.connection.isSecure());
+ }
+
+ } // open
+
+
+ /**
+ * 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 <code>true</code> if the tunnel should be
+ * considered secure, <code>false</code> otherwise
+ * @param params the parameters for tunnelling the connection
+ *
+ * @throws IOException in case of a problem
+ */
+ public void tunnelTarget(boolean secure, HttpParams params)
+ throws IOException {
+
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+
+ //@@@ check for proxy in planned route?
+ if ((this.tracker == null) || !this.tracker.isConnected()) {
+ throw new IllegalStateException("Connection not open.");
+ }
+ if (this.tracker.isTunnelled()) {
+ throw new IllegalStateException
+ ("Connection is already tunnelled.");
+ }
+
+ // LOG.debug?
+
+ this.connection.update(null, tracker.getTargetHost(),
+ secure, params);
+ this.tracker.tunnelTarget(secure);
+
+ } // tunnelTarget
+
+
+ /**
+ * 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 org.apache.http.conn.ManagedClientConnection#tunnelProxy
+ * ManagedClientConnection.tunnelProxy}
+ * for details.
+ * @param secure <code>true</code> if the tunnel should be
+ * considered secure, <code>false</code> otherwise
+ * @param params the parameters for tunnelling the connection
+ *
+ * @throws IOException in case of a problem
+ */
+ public void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
+ throws IOException {
+
+ if (next == null) {
+ throw new IllegalArgumentException
+ ("Next proxy must not be null.");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+
+ //@@@ check for proxy in planned route?
+ if ((this.tracker == null) || !this.tracker.isConnected()) {
+ throw new IllegalStateException("Connection not open.");
+ }
+
+ // LOG.debug?
+
+ this.connection.update(null, next, secure, params);
+ this.tracker.tunnelProxy(next, secure);
+
+ } // tunnelProxy
+
+
+ /**
+ * 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(HttpContext context, HttpParams params)
+ throws IOException {
+
+ //@@@ is context allowed to be null? depends on operator?
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+
+ if ((this.tracker == null) || !this.tracker.isConnected()) {
+ throw new IllegalStateException("Connection not open.");
+ }
+ if (!this.tracker.isTunnelled()) {
+ //@@@ allow this?
+ throw new IllegalStateException
+ ("Protocol layering without a tunnel not supported.");
+ }
+ if (this.tracker.isLayered()) {
+ throw new IllegalStateException
+ ("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());
+
+ } // layerProtocol
+
+
+ /**
+ * 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;
+ }
+
+
+} // class AbstractPoolEntry
+
diff --git a/src/org/apache/http/impl/conn/AbstractPooledConnAdapter.java b/src/org/apache/http/impl/conn/AbstractPooledConnAdapter.java
new file mode 100644
index 0000000..2c5fd30
--- /dev/null
+++ b/src/org/apache/http/impl/conn/AbstractPooledConnAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/AbstractPooledConnAdapter.java $
+ * $Revision: 658775 $
+ * $Date: 2008-05-21 10:30:45 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.OperatedClientConnection;
+
+
+
+/**
+ * Abstract adapter from pool {@link AbstractPoolEntry entries} to
+ * {@link org.apache.http.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.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 658775 $ $Date: 2008-05-21 10:30:45 -0700 (Wed, 21 May 2008) $
+ *
+ * @since 4.0
+ */
+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(ClientConnectionManager manager,
+ AbstractPoolEntry entry) {
+ super(manager, entry.connection);
+ this.poolEntry = entry;
+ }
+
+
+ /**
+ * Asserts that this adapter is still attached.
+ *
+ * @throws IllegalStateException
+ * if it is {@link #detach detach}ed
+ */
+ protected final void assertAttached() {
+ if (poolEntry == null) {
+ throw new IllegalStateException("Adapter is detached.");
+ }
+ }
+
+ /**
+ * Detaches this adapter from the wrapped connection.
+ * This adapter becomes useless.
+ */
+ @Override
+ protected void detach() {
+ super.detach();
+ poolEntry = null;
+ }
+
+
+ // non-javadoc, see interface ManagedHttpConnection
+ public HttpRoute getRoute() {
+
+ assertAttached();
+ return (poolEntry.tracker == null) ?
+ null : poolEntry.tracker.toRoute();
+ }
+
+ // non-javadoc, see interface ManagedHttpConnection
+ public void open(HttpRoute route,
+ HttpContext context, HttpParams params)
+ throws IOException {
+
+ assertAttached();
+ poolEntry.open(route, context, params);
+ }
+
+
+ // non-javadoc, see interface ManagedHttpConnection
+ public void tunnelTarget(boolean secure, HttpParams params)
+ throws IOException {
+
+ assertAttached();
+ poolEntry.tunnelTarget(secure, params);
+ }
+
+
+ // non-javadoc, see interface ManagedHttpConnection
+ public void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
+ throws IOException {
+
+ assertAttached();
+ poolEntry.tunnelProxy(next, secure, params);
+ }
+
+
+ // non-javadoc, see interface ManagedHttpConnection
+ public void layerProtocol(HttpContext context, HttpParams params)
+ throws IOException {
+
+ assertAttached();
+ poolEntry.layerProtocol(context, params);
+ }
+
+
+
+ // non-javadoc, see interface HttpConnection
+ public void close() throws IOException {
+ if (poolEntry != null)
+ poolEntry.shutdownEntry();
+
+ OperatedClientConnection conn = getWrappedConnection();
+ if (conn != null) {
+ conn.close();
+ }
+ }
+
+ // non-javadoc, see interface HttpConnection
+ public void shutdown() throws IOException {
+ if (poolEntry != null)
+ poolEntry.shutdownEntry();
+
+ OperatedClientConnection conn = getWrappedConnection();
+ if (conn != null) {
+ conn.shutdown();
+ }
+ }
+
+
+ // non-javadoc, see interface ManagedClientConnection
+ public Object getState() {
+ assertAttached();
+ return poolEntry.getState();
+ }
+
+
+ // non-javadoc, see interface ManagedClientConnection
+ public void setState(final Object state) {
+ assertAttached();
+ poolEntry.setState(state);
+ }
+
+
+} // class AbstractPooledConnAdapter
diff --git a/src/org/apache/http/impl/conn/DefaultClientConnection.java b/src/org/apache/http/impl/conn/DefaultClientConnection.java
new file mode 100644
index 0000000..a41f57a
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultClientConnection.java
@@ -0,0 +1,259 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.params.HttpParams;
+import org.apache.http.impl.SocketHttpClientConnection;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+
+import org.apache.http.conn.OperatedClientConnection;
+
+
+/**
+ * Default implementation of an operated client connection.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 673450 $ $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * @since 4.0
+ */
+public class DefaultClientConnection extends SocketHttpClientConnection
+ implements OperatedClientConnection {
+
+ private final Log log = LogFactory.getLog(getClass());
+ private final Log headerLog = LogFactory.getLog("org.apache.http.headers");
+ private final Log wireLog = LogFactory.getLog("org.apache.http.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;
+
+ public DefaultClientConnection() {
+ super();
+ }
+
+
+ // non-javadoc, see interface OperatedClientConnection
+ public final HttpHost getTargetHost() {
+ return this.targetHost;
+ }
+
+
+ // non-javadoc, see interface OperatedClientConnection
+ public final boolean isSecure() {
+ return this.connSecure;
+ }
+
+
+ @Override
+ public final Socket getSocket() {
+ return this.socket;
+ }
+
+
+ public void opening(Socket sock, 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 IOException("Connection already shutdown");
+ }
+ }
+
+
+ public void openCompleted(boolean secure, HttpParams params) throws IOException {
+ assertNotOpen();
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+ 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 {
+ log.debug("Connection shut down");
+ shutdown = true;
+
+ super.shutdown();
+ Socket sock = this.socket; // copy volatile attribute
+ if (sock != null)
+ sock.close();
+
+ } // shutdown
+
+
+ @Override
+ public void close() throws IOException {
+ log.debug("Connection closed");
+ super.close();
+ }
+
+
+ @Override
+ protected SessionInputBuffer createSessionInputBuffer(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ SessionInputBuffer inbuffer = super.createSessionInputBuffer(
+ socket,
+ buffersize,
+ params);
+ if (wireLog.isDebugEnabled()) {
+ inbuffer = new LoggingSessionInputBuffer(inbuffer, new Wire(wireLog));
+ }
+ return inbuffer;
+ }
+
+
+ @Override
+ protected SessionOutputBuffer createSessionOutputBuffer(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ SessionOutputBuffer outbuffer = super.createSessionOutputBuffer(
+ socket,
+ buffersize,
+ params);
+ if (wireLog.isDebugEnabled()) {
+ outbuffer = new LoggingSessionOutputBuffer(outbuffer, new Wire(wireLog));
+ }
+ 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 DefaultResponseParser
+ (buffer, null, responseFactory, params);
+ }
+
+
+ // non-javadoc, see interface OperatedClientConnection
+ public void update(Socket sock, HttpHost target,
+ boolean secure, HttpParams params)
+ throws IOException {
+
+ assertOpen();
+ if (target == null) {
+ throw new IllegalArgumentException
+ ("Target host must not be null.");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+
+ if (sock != null) {
+ this.socket = sock;
+ bind(sock, params);
+ }
+ targetHost = target;
+ connSecure = secure;
+
+ } // update
+
+
+ @Override
+ public HttpResponse receiveResponseHeader() throws HttpException, IOException {
+ HttpResponse response = super.receiveResponseHeader();
+ if (headerLog.isDebugEnabled()) {
+ headerLog.debug("<< " + response.getStatusLine().toString());
+ Header[] headers = response.getAllHeaders();
+ for (Header header : headers) {
+ headerLog.debug("<< " + header.toString());
+ }
+ }
+ return response;
+ }
+
+
+ @Override
+ public void sendRequestHeader(HttpRequest request) throws HttpException, IOException {
+ super.sendRequestHeader(request);
+ if (headerLog.isDebugEnabled()) {
+ headerLog.debug(">> " + request.getRequestLine().toString());
+ Header[] headers = request.getAllHeaders();
+ for (Header header : headers) {
+ headerLog.debug(">> " + header.toString());
+ }
+ }
+ }
+
+} // class DefaultClientConnection
diff --git a/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java b/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
new file mode 100644
index 0000000..41488e1
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
@@ -0,0 +1,216 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java $
+ * $Revision: 652193 $
+ * $Date: 2008-04-29 17:10:36 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.Socket;
+import java.net.InetAddress;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.HttpHostConnectException;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.scheme.LayeredSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.scheme.SocketFactory;
+
+
+/**
+ * Default implementation of a
+ * {@link ClientConnectionOperator ClientConnectionOperator}.
+ * It uses a {@link SchemeRegistry SchemeRegistry} to look up
+ * {@link SocketFactory SocketFactory} objects.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 652193 $ $Date: 2008-04-29 17:10:36 -0700 (Tue, 29 Apr 2008) $
+ *
+ * @since 4.0
+ */
+public class DefaultClientConnectionOperator
+ implements ClientConnectionOperator {
+
+
+ /** The scheme registry for looking up socket factories. */
+ protected SchemeRegistry schemeRegistry;
+
+
+ /**
+ * Creates a new client connection operator for the given scheme registry.
+ *
+ * @param schemes the scheme registry
+ */
+ public DefaultClientConnectionOperator(SchemeRegistry schemes) {
+ if (schemes == null) {
+ throw new IllegalArgumentException
+ ("Scheme registry must not be null.");
+ }
+ schemeRegistry = schemes;
+ }
+
+
+ // non-javadoc, see interface ClientConnectionOperator
+ public OperatedClientConnection createConnection() {
+ return new DefaultClientConnection();
+ }
+
+
+ // non-javadoc, see interface ClientConnectionOperator
+ public void openConnection(OperatedClientConnection conn,
+ HttpHost target,
+ InetAddress local,
+ HttpContext context,
+ HttpParams params)
+ throws IOException {
+
+ if (conn == null) {
+ throw new IllegalArgumentException
+ ("Connection must not be null.");
+ }
+ if (target == null) {
+ throw new IllegalArgumentException
+ ("Target host must not be null.");
+ }
+ // local address may be null
+ //@@@ is context allowed to be null?
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+ if (conn.isOpen()) {
+ throw new IllegalArgumentException
+ ("Connection must not be open.");
+ }
+
+ final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
+ final SocketFactory sf = schm.getSocketFactory();
+
+ Socket sock = sf.createSocket();
+ conn.opening(sock, target);
+
+ try {
+ sock = sf.connectSocket(sock, target.getHostName(),
+ schm.resolvePort(target.getPort()),
+ local, 0, params);
+ } catch (ConnectException ex) {
+ throw new HttpHostConnectException(target, ex);
+ }
+ prepareSocket(sock, context, params);
+ conn.openCompleted(sf.isSecure(sock), params);
+ } // openConnection
+
+
+ // non-javadoc, see interface ClientConnectionOperator
+ public void updateSecureConnection(OperatedClientConnection conn,
+ HttpHost target,
+ HttpContext context,
+ HttpParams params)
+ throws IOException {
+
+
+ if (conn == null) {
+ throw new IllegalArgumentException
+ ("Connection must not be null.");
+ }
+ if (target == null) {
+ throw new IllegalArgumentException
+ ("Target host must not be null.");
+ }
+ //@@@ is context allowed to be null?
+ if (params == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+ if (!conn.isOpen()) {
+ throw new IllegalArgumentException
+ ("Connection must be open.");
+ }
+
+ final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
+ if (!(schm.getSocketFactory() instanceof LayeredSocketFactory)) {
+ throw new IllegalArgumentException
+ ("Target scheme (" + schm.getName() +
+ ") must have layered socket factory.");
+ }
+
+ final LayeredSocketFactory lsf = (LayeredSocketFactory) schm.getSocketFactory();
+ final Socket sock;
+ try {
+ sock = lsf.createSocket
+ (conn.getSocket(), target.getHostName(), target.getPort(), true);
+ } catch (ConnectException ex) {
+ throw new HttpHostConnectException(target, ex);
+ }
+ prepareSocket(sock, context, params);
+ conn.update(sock, target, lsf.isSecure(sock), params);
+ //@@@ error handling: close the layered socket in case of exception?
+
+ } // updateSecureConnection
+
+
+ /**
+ * 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(Socket sock, HttpContext context,
+ HttpParams params)
+ throws IOException {
+
+ // context currently not used, but derived classes may need it
+ //@@@ is context allowed to be null?
+
+ sock.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
+ sock.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
+
+ int linger = HttpConnectionParams.getLinger(params);
+ if (linger >= 0) {
+ sock.setSoLinger(linger > 0, linger);
+ }
+
+ } // prepareSocket
+
+
+} // class DefaultClientConnectionOperator
+
diff --git a/src/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java b/src/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java
new file mode 100644
index 0000000..90fd55f
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+
+import org.apache.http.conn.params.ConnRouteParams;
+
+
+/**
+ * Default implementation of an {@link HttpRoutePlanner}.
+ * This implementation is based on
+ * {@link org.apache.http.conn.params.ConnRoutePNames parameters}.
+ * It will not make use of any Java system properties,
+ * nor of system or browser proxy settings.
+ */
+public class DefaultHttpRoutePlanner implements HttpRoutePlanner {
+
+ /** The scheme registry. */
+ protected SchemeRegistry schemeRegistry;
+
+
+ /**
+ * Creates a new default route planner.
+ *
+ * @param schreg the scheme registry
+ */
+ public DefaultHttpRoutePlanner(SchemeRegistry schreg) {
+ if (schreg == null) {
+ throw new IllegalArgumentException
+ ("SchemeRegistry must not be null.");
+ }
+ schemeRegistry = schreg;
+ }
+
+
+ // non-javadoc, see interface HttpRoutePlanner
+ public HttpRoute determineRoute(HttpHost target,
+ HttpRequest request,
+ HttpContext context)
+ throws HttpException {
+
+ if (request == null) {
+ throw new IllegalStateException
+ ("Request must not be null.");
+ }
+
+ // 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.
+
+ if (target == null) {
+ throw new IllegalStateException
+ ("Target host must not be null.");
+ }
+
+ final InetAddress local =
+ ConnRouteParams.getLocalAddress(request.getParams());
+ final HttpHost proxy =
+ ConnRouteParams.getDefaultProxy(request.getParams());
+
+ final Scheme schm = 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;
+ }
+
+
+}
diff --git a/src/org/apache/http/impl/conn/DefaultResponseParser.java b/src/org/apache/http/impl/conn/DefaultResponseParser.java
new file mode 100644
index 0000000..f817a10
--- /dev/null
+++ b/src/org/apache/http/impl/conn/DefaultResponseParser.java
@@ -0,0 +1,103 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultResponseParser.java $
+ * $Revision: 617638 $
+ * $Date: 2008-02-01 12:49:26 -0800 (Fri, 01 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.StatusLine;
+import org.apache.http.conn.params.ConnConnectionPNames;
+import org.apache.http.impl.io.AbstractMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class DefaultResponseParser extends AbstractMessageParser {
+
+ 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);
+ if (responseFactory == null) {
+ throw new IllegalArgumentException
+ ("Response factory may not be null");
+ }
+ this.responseFactory = responseFactory;
+ this.lineBuf = new CharArrayBuffer(128);
+ this.maxGarbageLines = params.getIntParameter(
+ ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, Integer.MAX_VALUE);
+ }
+
+
+ @Override
+ protected HttpMessage parseHead(
+ final SessionInputBuffer sessionBuffer) throws IOException, HttpException {
+ // clear the buffer
+ this.lineBuf.clear();
+ //read out the HTTP status string
+ int count = 0;
+ ParserCursor cursor = null;
+ do {
+ 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");
+ }
+ count++;
+ } while(true);
+ //create the status line from the status string
+ StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
+ return this.responseFactory.newHttpResponse(statusline, null);
+ }
+
+}
diff --git a/src/org/apache/http/impl/conn/IdleConnectionHandler.java b/src/org/apache/http/impl/conn/IdleConnectionHandler.java
new file mode 100644
index 0000000..2cacda3
--- /dev/null
+++ b/src/org/apache/http/impl/conn/IdleConnectionHandler.java
@@ -0,0 +1,190 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/IdleConnectionHandler.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpConnection;
+
+
+/**
+ * A helper class for connection managers to track idle connections.
+ *
+ * <p>This class is not synchronized.</p>
+ *
+ * @see org.apache.http.conn.ClientConnectionManager#closeIdleConnections
+ *
+ * @since 4.0
+ */
+public class IdleConnectionHandler {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** Holds connections and the time they were added. */
+ private final Map<HttpConnection,TimeValues> connectionToTimes;
+
+
+ public IdleConnectionHandler() {
+ super();
+ connectionToTimes = new HashMap<HttpConnection,TimeValues>();
+ }
+
+ /**
+ * 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(HttpConnection connection, long validDuration, TimeUnit unit) {
+
+ Long timeAdded = Long.valueOf(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(HttpConnection connection) {
+ 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
+ */
+ //@@@ add TimeUnit argument here?
+ public void closeIdleConnections(long idleTime) {
+
+ // the latest time for which connections will be closed
+ long idleTimeout = System.currentTimeMillis() - idleTime;
+
+ if (log.isDebugEnabled()) {
+ log.debug("Checking for connections, idleTimeout: " + idleTimeout);
+ }
+
+ Iterator<HttpConnection> connectionIter =
+ connectionToTimes.keySet().iterator();
+
+ while (connectionIter.hasNext()) {
+ HttpConnection conn = connectionIter.next();
+ TimeValues times = connectionToTimes.get(conn);
+ Long connectionTime = times.timeAdded;
+ if (connectionTime.longValue() <= idleTimeout) {
+ if (log.isDebugEnabled()) {
+ log.debug("Closing connection, connection time: " + connectionTime);
+ }
+ connectionIter.remove();
+ try {
+ conn.close();
+ } catch (IOException ex) {
+ log.debug("I/O error closing connection", ex);
+ }
+ }
+ }
+ }
+
+
+ public void closeExpiredConnections() {
+ long now = System.currentTimeMillis();
+ if (log.isDebugEnabled()) {
+ log.debug("Checking for expired connections, now: " + now);
+ }
+
+ Iterator<HttpConnection> connectionIter =
+ connectionToTimes.keySet().iterator();
+
+ while (connectionIter.hasNext()) {
+ HttpConnection conn = connectionIter.next();
+ TimeValues times = connectionToTimes.get(conn);
+ if(times.timeExpires <= now) {
+ if (log.isDebugEnabled()) {
+ log.debug("Closing connection, expired @: " + times.timeExpires);
+ }
+ connectionIter.remove();
+ try {
+ conn.close();
+ } catch (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(long now, long validDuration, TimeUnit validUnit) {
+ this.timeAdded = now;
+ if(validDuration > 0) {
+ this.timeExpires = now + validUnit.toMillis(validDuration);
+ } else {
+ this.timeExpires = Long.MAX_VALUE;
+ }
+ }
+ }
+}
diff --git a/src/org/apache/http/impl/conn/LoggingSessionInputBuffer.java b/src/org/apache/http/impl/conn/LoggingSessionInputBuffer.java
new file mode 100644
index 0000000..4f6477e
--- /dev/null
+++ b/src/org/apache/http/impl/conn/LoggingSessionInputBuffer.java
@@ -0,0 +1,117 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/LoggingSessionInputBuffer.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Logs all data read to the wire LOG.
+ *
+ * @author Ortwin Glueck
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class LoggingSessionInputBuffer implements SessionInputBuffer {
+
+ /** Original session input buffer. */
+ private final SessionInputBuffer in;
+
+ /** The wire log to use for writing. */
+ private final Wire wire;
+
+ /**
+ * Create an instance that wraps the specified session input buffer.
+ * @param in The session input buffer.
+ * @param wire The wire log to use.
+ */
+ public LoggingSessionInputBuffer(final SessionInputBuffer in, final Wire wire) {
+ super();
+ this.in = in;
+ this.wire = wire;
+ }
+
+ public boolean isDataAvailable(int timeout) throws IOException {
+ return this.in.isDataAvailable(timeout);
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ 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 {
+ int l = this.in.read();
+ if (this.wire.enabled() && l > 0) {
+ this.wire.input(l);
+ }
+ return l;
+ }
+
+ public int read(byte[] b) throws IOException {
+ 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 {
+ String s = this.in.readLine();
+ if (this.wire.enabled() && s != null) {
+ this.wire.input(s + "[EOL]");
+ }
+ return s;
+ }
+
+ public int readLine(final CharArrayBuffer buffer) throws IOException {
+ int l = this.in.readLine(buffer);
+ if (this.wire.enabled() && l > 0) {
+ int pos = buffer.length() - l;
+ String s = new String(buffer.buffer(), pos, l);
+ this.wire.input(s + "[EOL]");
+ }
+ return l;
+ }
+
+ public HttpTransportMetrics getMetrics() {
+ return this.in.getMetrics();
+ }
+
+}
diff --git a/src/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java b/src/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java
new file mode 100644
index 0000000..1afab7d
--- /dev/null
+++ b/src/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java
@@ -0,0 +1,109 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Logs all data written to the wire LOG.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class LoggingSessionOutputBuffer implements SessionOutputBuffer {
+
+ /** Original data transmitter. */
+ private final SessionOutputBuffer out;
+
+ /** The wire log to use. */
+ private final Wire wire;
+
+ /**
+ * Create an instance that wraps the specified session output buffer.
+ * @param out The session output buffer.
+ * @param wire The Wire log to use.
+ */
+ public LoggingSessionOutputBuffer(final SessionOutputBuffer out, final Wire wire) {
+ super();
+ this.out = out;
+ this.wire = wire;
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ this.out.write(b, off, len);
+ if (this.wire.enabled()) {
+ this.wire.output(b, off, len);
+ }
+ }
+
+ public void write(int b) throws IOException {
+ this.out.write(b);
+ if (this.wire.enabled()) {
+ this.wire.output(b);
+ }
+ }
+
+ public void write(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()) {
+ String s = new String(buffer.buffer(), 0, buffer.length());
+ this.wire.output(s + "[EOL]");
+ }
+ }
+
+ public void writeLine(final String s) throws IOException {
+ this.out.writeLine(s);
+ if (this.wire.enabled()) {
+ this.wire.output(s + "[EOL]");
+ }
+ }
+
+ public HttpTransportMetrics getMetrics() {
+ return this.out.getMetrics();
+ }
+
+}
diff --git a/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java b/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java
new file mode 100644
index 0000000..136caf4
--- /dev/null
+++ b/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java
@@ -0,0 +1,290 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java $
+ * $Revision: 658785 $
+ * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.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 org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.protocol.HttpContext;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+
+import org.apache.http.conn.params.ConnRouteParams;
+
+
+/**
+ * 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 org.apache.http.conn.params.ConnRoutePNames parameters},
+ * though not the {@link
+ * org.apache.http.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}.
+ */
+public class ProxySelectorRoutePlanner implements HttpRoutePlanner {
+
+ /** The scheme registry. */
+ protected SchemeRegistry schemeRegistry;
+
+ /** The proxy selector to use, or <code>null</code> for system default. */
+ protected ProxySelector proxySelector;
+
+
+ /**
+ * Creates a new proxy selector route planner.
+ *
+ * @param schreg the scheme registry
+ * @param prosel the proxy selector, or
+ * <code>null</code> for the system default
+ */
+ public ProxySelectorRoutePlanner(SchemeRegistry schreg,
+ ProxySelector prosel) {
+
+ if (schreg == null) {
+ throw new IllegalArgumentException
+ ("SchemeRegistry must not be null.");
+ }
+ schemeRegistry = schreg;
+ proxySelector = prosel;
+ }
+
+
+ /**
+ * Obtains the proxy selector to use.
+ *
+ * @return the proxy selector, or <code>null</code> for the system default
+ */
+ public ProxySelector getProxySelector() {
+ return this.proxySelector;
+ }
+
+
+ /**
+ * Sets the proxy selector to use.
+ *
+ * @param prosel the proxy selector, or
+ * <code>null</code> to use the system default
+ */
+ public void setProxySelector(ProxySelector prosel) {
+ this.proxySelector = prosel;
+ }
+
+
+
+ // non-javadoc, see interface HttpRoutePlanner
+ public HttpRoute determineRoute(HttpHost target,
+ HttpRequest request,
+ HttpContext context)
+ throws HttpException {
+
+ if (request == null) {
+ throw new IllegalStateException
+ ("Request must not be null.");
+ }
+
+ // 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.
+
+ if (target == null) {
+ throw new IllegalStateException
+ ("Target host must not be null.");
+ }
+
+ 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 <code>null</code>
+ * @param request the request to be sent, never <code>null</code>
+ * @param context the context, or <code>null</code>
+ *
+ * @return the proxy to use, or <code>null</code> for a direct route
+ *
+ * @throws HttpException
+ * in case of system proxy settings that cannot be handled
+ */
+ protected HttpHost determineProxy(HttpHost target,
+ HttpRequest request,
+ 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 (URISyntaxException usx) {
+ throw new HttpException
+ ("Cannot convert host to URI: " + target, usx);
+ }
+ List<Proxy> proxies = psel.select(targetURI);
+
+ 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
+ * <br/>
+ * (TODO: determine format for IPv6 addresses, with or without [brackets])
+ */
+ protected String getHost(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 <code>null</code> or empty
+ * @param target the planned target, never <code>null</code>
+ * @param request the request to be sent, never <code>null</code>
+ * @param context the context, or <code>null</code>
+ *
+ * @return a proxy of type {@link Proxy.Type#DIRECT DIRECT}
+ * or {@link Proxy.Type#HTTP HTTP}, never <code>null</code>
+ */
+ protected Proxy chooseProxy(List<Proxy> proxies,
+ HttpHost target,
+ HttpRequest request,
+ HttpContext context) {
+
+ if ((proxies == null) || proxies.isEmpty()) {
+ throw new IllegalArgumentException
+ ("Proxy list must not be empty.");
+ }
+
+ Proxy result = null;
+
+ // check the list for one we can use
+ for (int i=0; (result == null) && (i < proxies.size()); i++) {
+
+ 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;
+ }
+
+} // class ProxySelectorRoutePlanner
+
diff --git a/src/org/apache/http/impl/conn/SingleClientConnManager.java b/src/org/apache/http/impl/conn/SingleClientConnManager.java
new file mode 100644
index 0000000..7999f3e
--- /dev/null
+++ b/src/org/apache/http/impl/conn/SingleClientConnManager.java
@@ -0,0 +1,444 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * A connection "manager" for a single connection.
+ * This manager is good only for single-threaded use.
+ * Allocation <i>always</i> returns the connection immediately,
+ * even if it has not been released after the previous allocation.
+ * In that case, a {@link #MISUSE_MESSAGE warning} is logged
+ * and the previously issued connection is revoked.
+ * <p>
+ * This class is derived from <code>SimpleHttpConnectionManager</code>
+ * in HttpClient 3. See there for original authors.
+ * </p>
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 673450 $
+ *
+ * @since 4.0
+ */
+public class SingleClientConnManager implements ClientConnectionManager {
+
+ private final Log log = LogFactory.getLog(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 SchemeRegistry schemeRegistry;
+
+ /** The operator for opening and updating connections. */
+ protected ClientConnectionOperator connOperator;
+
+ /** The one and only entry in this pool. */
+ protected PoolEntry uniquePoolEntry;
+
+ /** The currently issued managed connection, if any. */
+ protected ConnAdapter managedConn;
+
+ /** The time of the last connection release, or -1. */
+ protected long lastReleaseTime;
+
+ /** The time the last released connection expires and shouldn't be reused. */
+ protected long connectionExpiresTime;
+
+ /** Whether the connection should be shut down on release. */
+ protected boolean alwaysShutDown;
+
+ /** 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, or
+ * <code>null</code> for the default registry
+ */
+ public SingleClientConnManager(HttpParams params,
+ SchemeRegistry schreg) {
+
+ if (schreg == null) {
+ throw new IllegalArgumentException
+ ("Scheme registry must not be null.");
+ }
+ 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;
+
+ } // <constructor>
+
+
+ @Override
+ protected void finalize() throws Throwable {
+ shutdown();
+ super.finalize();
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ 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 <code>null</code>
+ *
+ * @return the connection operator to use
+ */
+ protected ClientConnectionOperator
+ createConnectionOperator(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 {
+
+ if (this.isShutDown)
+ throw new IllegalStateException("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(
+ long timeout, TimeUnit tunit) {
+ return SingleClientConnManager.this.getConnection(
+ route, state);
+ }
+
+ };
+ }
+
+
+ /**
+ * Obtains a connection.
+ * This method does not block.
+ *
+ * @param route where the connection should point to
+ *
+ * @return a connection that can be used to communicate
+ * along the given route
+ */
+ public ManagedClientConnection getConnection(HttpRoute route, Object state) {
+
+ if (route == null) {
+ throw new IllegalArgumentException("Route may not be null.");
+ }
+ assertStillUp();
+
+ if (log.isDebugEnabled()) {
+ log.debug("Get connection for route " + route);
+ }
+
+ if (managedConn != null)
+ revokeConnection();
+
+ // check re-usability of the connection
+ boolean recreate = false;
+ boolean shutdown = false;
+
+ // Kill the connection if it expired.
+ closeExpiredConnections();
+
+ if (uniquePoolEntry.connection.isOpen()) {
+ 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 (IOException iox) {
+ log.debug("Problem shutting down connection.", iox);
+ }
+ }
+
+ if (recreate)
+ uniquePoolEntry = new PoolEntry();
+
+ managedConn = new ConnAdapter(uniquePoolEntry, route);
+
+ return managedConn;
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
+ assertStillUp();
+
+ if (!(conn instanceof ConnAdapter)) {
+ throw new IllegalArgumentException
+ ("Connection class mismatch, " +
+ "connection not obtained from this manager.");
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Releasing connection " + conn);
+ }
+
+ ConnAdapter sca = (ConnAdapter) conn;
+ if (sca.poolEntry == null)
+ return; // already released
+ ClientConnectionManager manager = sca.getManager();
+ if (manager != null && manager != this) {
+ throw new IllegalArgumentException
+ ("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 (IOException iox) {
+ //@@@ log as warning? let pass?
+ if (log.isDebugEnabled())
+ log.debug("Exception shutting down released connection.",
+ iox);
+ } finally {
+ sca.detach();
+ managedConn = null;
+ lastReleaseTime = System.currentTimeMillis();
+ if(validDuration > 0)
+ connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
+ else
+ connectionExpiresTime = Long.MAX_VALUE;
+ }
+ } // releaseConnection
+
+ public void closeExpiredConnections() {
+ if(System.currentTimeMillis() >= connectionExpiresTime) {
+ closeIdleConnections(0, TimeUnit.MILLISECONDS);
+ }
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public void closeIdleConnections(long idletime, TimeUnit tunit) {
+ assertStillUp();
+
+ // idletime can be 0 or negative, no problem there
+ if (tunit == null) {
+ throw new IllegalArgumentException("Time unit must not be null.");
+ }
+
+ if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
+ final long cutoff =
+ System.currentTimeMillis() - tunit.toMillis(idletime);
+ if (lastReleaseTime <= cutoff) {
+ try {
+ uniquePoolEntry.close();
+ } catch (IOException iox) {
+ // ignore
+ log.debug("Problem closing idle connection.", iox);
+ }
+ }
+ }
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public void shutdown() {
+
+ this.isShutDown = true;
+
+ if (managedConn != null)
+ managedConn.detach();
+
+ try {
+ if (uniquePoolEntry != null) // and connection open?
+ uniquePoolEntry.shutdown();
+ } catch (IOException iox) {
+ // ignore
+ log.debug("Problem while shutting down manager.", iox);
+ } finally {
+ uniquePoolEntry = null;
+ }
+ }
+
+
+ /**
+ * Revokes the currently issued connection.
+ * The adapter gets disconnected, the connection will be shut down.
+ */
+ protected void revokeConnection() {
+ if (managedConn == null)
+ return;
+
+ log.warn(MISUSE_MESSAGE);
+
+ managedConn.detach();
+
+ try {
+ uniquePoolEntry.shutdown();
+ } catch (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();
+ }
+
+ } // class PoolEntry
+
+
+
+ /**
+ * 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(PoolEntry entry, HttpRoute route) {
+ super(SingleClientConnManager.this, entry);
+ markReusable();
+ entry.route = route;
+ }
+
+ }
+
+
+} // class SingleClientConnManager
diff --git a/src/org/apache/http/impl/conn/Wire.java b/src/org/apache/http/impl/conn/Wire.java
new file mode 100644
index 0000000..147b7f5
--- /dev/null
+++ b/src/org/apache/http/impl/conn/Wire.java
@@ -0,0 +1,160 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/Wire.java,v 1.9 2004/06/24 21:39:52 mbecke Exp $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import org.apache.commons.logging.Log;
+
+/**
+ * Logs data to the wire LOG.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class Wire {
+
+ private final Log log;
+
+ public Wire(Log log) {
+ this.log = log;
+ }
+
+ private void wire(String header, InputStream instream)
+ throws IOException {
+ 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(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(buffer.toString());
+ }
+ }
+
+
+ public boolean enabled() {
+ return log.isDebugEnabled();
+ }
+
+ public void output(InputStream outstream)
+ throws IOException {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Output may not be null");
+ }
+ wire(">> ", outstream);
+ }
+
+ public void input(InputStream instream)
+ throws IOException {
+ if (instream == null) {
+ throw new IllegalArgumentException("Input may not be null");
+ }
+ wire("<< ", instream);
+ }
+
+ public void output(byte[] b, int off, int len)
+ throws IOException {
+ if (b == null) {
+ throw new IllegalArgumentException("Output may not be null");
+ }
+ wire(">> ", new ByteArrayInputStream(b, off, len));
+ }
+
+ public void input(byte[] b, int off, int len)
+ throws IOException {
+ if (b == null) {
+ throw new IllegalArgumentException("Input may not be null");
+ }
+ wire("<< ", new ByteArrayInputStream(b, off, len));
+ }
+
+ public void output(byte[] b)
+ throws IOException {
+ if (b == null) {
+ throw new IllegalArgumentException("Output may not be null");
+ }
+ wire(">> ", new ByteArrayInputStream(b));
+ }
+
+ public void input(byte[] b)
+ throws IOException {
+ if (b == null) {
+ throw new IllegalArgumentException("Input may not be null");
+ }
+ wire("<< ", new ByteArrayInputStream(b));
+ }
+
+ public void output(int b)
+ throws IOException {
+ output(new byte[] {(byte) b});
+ }
+
+ public void input(int b)
+ throws IOException {
+ input(new byte[] {(byte) b});
+ }
+
+ public void output(final String s)
+ throws IOException {
+ if (s == null) {
+ throw new IllegalArgumentException("Output may not be null");
+ }
+ output(s.getBytes());
+ }
+
+ public void input(final String s)
+ throws IOException {
+ if (s == null) {
+ throw new IllegalArgumentException("Input may not be null");
+ }
+ input(s.getBytes());
+ }
+}
diff --git a/src/org/apache/http/impl/conn/package.html b/src/org/apache/http/impl/conn/package.html
new file mode 100644
index 0000000..54eb3c2
--- /dev/null
+++ b/src/org/apache/http/impl/conn/package.html
@@ -0,0 +1,5 @@
+<body>
+
+
+</body>
+
diff --git a/src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java b/src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
new file mode 100644
index 0000000..2b37d72
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
@@ -0,0 +1,332 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.conn.IdleConnectionHandler;
+
+
+/**
+ * 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 <code>synchronized</code> for that purpose!
+ */
+public abstract class AbstractConnPool implements RefQueueHandler {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /**
+ * The global lock for this pool.
+ */
+ protected final Lock poolLock;
+
+
+ /**
+ * References to issued connections.
+ * Objects in this set are of class
+ * {@link BasicPoolEntryRef BasicPoolEntryRef},
+ * and point to the pool entry for the issued connection.
+ * GCed connections are detected by the missing pool entries.
+ */
+ protected Set<BasicPoolEntryRef> issuedConnections;
+
+ /** The handler for idle connections. */
+ protected IdleConnectionHandler idleConnHandler;
+
+ /** The current total number of connections. */
+ protected int numConnections;
+
+ /**
+ * A reference queue to track loss of pool entries to GC.
+ * The same queue is used to track loss of the connection manager,
+ * so we cannot specialize the type.
+ */
+ protected ReferenceQueue<Object> refQueue;
+
+ /** A worker (thread) to track loss of pool entries to GC. */
+ private RefQueueWorker refWorker;
+
+
+ /** Indicates whether this pool is shut down. */
+ protected volatile boolean isShutDown;
+
+ /**
+ * Creates a new connection pool.
+ */
+ protected AbstractConnPool() {
+ issuedConnections = new HashSet<BasicPoolEntryRef>();
+ idleConnHandler = new IdleConnectionHandler();
+
+ boolean fair = false; //@@@ check parameters to decide
+ poolLock = new ReentrantLock(fair);
+ }
+
+
+ /**
+ * Enables connection garbage collection (GC).
+ * This method must be called immediately after creating the
+ * connection pool. It is not possible to enable connection GC
+ * after pool entries have been created. Neither is it possible
+ * to disable connection GC.
+ *
+ * @throws IllegalStateException
+ * if connection GC is already enabled, or if it cannot be
+ * enabled because there already are pool entries
+ */
+ public void enableConnectionGC()
+ throws IllegalStateException {
+
+ if (refQueue != null) {
+ throw new IllegalStateException("Connection GC already enabled.");
+ }
+ poolLock.lock();
+ try {
+ if (numConnections > 0) { //@@@ is this check sufficient?
+ throw new IllegalStateException("Pool already in use.");
+ }
+ } finally {
+ poolLock.unlock();
+ }
+
+ refQueue = new ReferenceQueue<Object>();
+ refWorker = new RefQueueWorker(refQueue, this);
+ Thread t = new Thread(refWorker); //@@@ use a thread factory
+ t.setDaemon(true);
+ t.setName("RefQueueWorker@" + this);
+ t.start();
+ }
+
+
+ /**
+ * 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 <code>timeout</code>,
+ * may be <code>null</code> 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(
+ HttpRoute route,
+ Object state,
+ long timeout,
+ 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 <code>true</code> if the entry is deemed
+ * reusable, <code>false</code> 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)
+ ;
+
+
+
+ // non-javadoc, see interface RefQueueHandler
+// BEGIN android-changed
+ public void handleReference(Reference ref) {
+// END android-changed
+ poolLock.lock();
+ try {
+
+ if (ref instanceof BasicPoolEntryRef) {
+ // check if the GCed pool entry was still in use
+ //@@@ find a way to detect this without lookup
+ //@@@ flag in the BasicPoolEntryRef, to be reset when freed?
+ final boolean lost = issuedConnections.remove(ref);
+ if (lost) {
+ final HttpRoute route =
+ ((BasicPoolEntryRef)ref).getRoute();
+ if (log.isDebugEnabled()) {
+ log.debug("Connection garbage collected. " + route);
+ }
+ handleLostEntry(route);
+ }
+ }
+
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+
+ /**
+ * Handles cleaning up for a lost pool entry with the given route.
+ * A lost pool entry corresponds to a connection that was
+ * garbage collected instead of being properly released.
+ *
+ * @param route the route of the pool entry that was lost
+ */
+ 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 <code>idletime</code>
+ */
+ public void closeIdleConnections(long idletime, TimeUnit tunit) {
+
+ // idletime can be 0 or negative, no problem there
+ if (tunit == null) {
+ throw new IllegalArgumentException("Time unit must not be null.");
+ }
+
+ poolLock.lock();
+ try {
+ idleConnHandler.closeIdleConnections(tunit.toMillis(idletime));
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+ public void closeExpiredConnections() {
+ poolLock.lock();
+ try {
+ idleConnHandler.closeExpiredConnections();
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+
+ //@@@ revise this cleanup stuff (closeIdle+deleteClosed), it's not good
+
+ /**
+ * 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;
+
+ // no point in monitoring GC anymore
+ if (refWorker != null)
+ refWorker.shutdown();
+
+ // close all connections that are issued to an application
+ Iterator<BasicPoolEntryRef> iter = issuedConnections.iterator();
+ while (iter.hasNext()) {
+ BasicPoolEntryRef per = iter.next();
+ iter.remove();
+ BasicPoolEntry entry = per.get();
+ if (entry != null) {
+ closeConnection(entry.getConnection());
+ }
+ }
+
+ // remove all references to connections
+ //@@@ use this for shutting them down instead?
+ idleConnHandler.removeAll();
+
+ isShutDown = true;
+
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+
+ /**
+ * Closes a connection from this pool.
+ *
+ * @param conn the connection to close, or <code>null</code>
+ */
+ protected void closeConnection(final OperatedClientConnection conn) {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (IOException ex) {
+ log.debug("I/O error closing connection", ex);
+ }
+ }
+ }
+
+
+
+
+
+} // class AbstractConnPool
+
diff --git a/src/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java
new file mode 100644
index 0000000..dded360
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java $
+ * $Revision: 652721 $
+ * $Date: 2008-05-01 17:32:20 -0700 (Thu, 01 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import java.lang.ref.ReferenceQueue;
+
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.conn.AbstractPoolEntry;
+
+
+
+/**
+ * Basic implementation of a connection pool entry.
+ */
+public class BasicPoolEntry extends AbstractPoolEntry {
+
+ /**
+ * A weak reference to <code>this</code> used to detect GC of entries.
+ * Pool entries can only be GCed when they are allocated by an application
+ * and therefore not referenced with a hard link in the manager.
+ */
+ private final BasicPoolEntryRef reference;
+
+ /**
+ * Creates a new pool entry.
+ *
+ * @param op the connection operator
+ * @param route the planned route for the connection
+ * @param queue the reference queue for tracking GC of this entry,
+ * or <code>null</code>
+ */
+ public BasicPoolEntry(ClientConnectionOperator op,
+ HttpRoute route,
+ ReferenceQueue<Object> queue) {
+ super(op, route);
+ if (route == null) {
+ throw new IllegalArgumentException("HTTP route may not be null");
+ }
+ this.reference = new BasicPoolEntryRef(this, queue);
+ }
+
+ protected final OperatedClientConnection getConnection() {
+ return super.connection;
+ }
+
+ protected final HttpRoute getPlannedRoute() {
+ return super.route;
+ }
+
+ protected final BasicPoolEntryRef getWeakRef() {
+ return this.reference;
+ }
+
+
+} // class BasicPoolEntry
+
+
diff --git a/src/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java
new file mode 100644
index 0000000..32df8a5
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java
@@ -0,0 +1,80 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java $
+ * $Revision: 674186 $
+ * $Date: 2008-07-05 05:18:54 -0700 (Sat, 05 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+
+
+/**
+ * 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.
+ */
+public class BasicPoolEntryRef extends WeakReference<BasicPoolEntry> {
+
+ /** The planned route of the entry. */
+ private final HttpRoute route;
+
+
+ /**
+ * Creates a new reference to a pool entry.
+ *
+ * @param entry the pool entry, must not be <code>null</code>
+ * @param queue the reference queue, or <code>null</code>
+ */
+ public BasicPoolEntryRef(BasicPoolEntry entry,
+ ReferenceQueue<Object> queue) {
+ super(entry, queue);
+ if (entry == null) {
+ throw new IllegalArgumentException
+ ("Pool entry must not be null.");
+ }
+ 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/src/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java b/src/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java
new file mode 100644
index 0000000..29455d0
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java $
+ * $Revision: 653214 $
+ * $Date: 2008-05-04 07:12:13 -0700 (Sun, 04 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.impl.conn.AbstractPoolEntry;
+import org.apache.http.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.
+ */
+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(ThreadSafeClientConnManager tsccm,
+ AbstractPoolEntry entry) {
+ super(tsccm, entry);
+ markReusable();
+ }
+
+
+ @Override
+ protected ClientConnectionManager getManager() {
+ // override needed only to make method visible in this package
+ return super.getManager();
+ }
+
+
+ /**
+ * Obtains the pool entry.
+ *
+ * @return the pool entry, or <code>null</code> if detached
+ */
+ protected AbstractPoolEntry getPoolEntry() {
+ return super.poolEntry;
+ }
+
+
+ // non-javadoc, see base class
+ @Override
+ protected void detach() {
+ // override needed only to make method visible in this package
+ super.detach();
+ }
+}
diff --git a/src/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java b/src/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
new file mode 100644
index 0000000..cf59129
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
@@ -0,0 +1,698 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.params.ConnPerRoute;
+import org.apache.http.conn.params.ConnManagerParams;
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * A connection pool that maintains connections by route.
+ * This class is derived from <code>MultiThreadedHttpConnectionManager</code>
+ * in HttpClient 3.x, see there for original authors. It implements the same
+ * algorithm for connection re-use and connection-per-host enforcement:
+ * <ul>
+ * <li>connections are re-used only for the exact same route</li>
+ * <li>connection limits are enforced per route rather than per host</li>
+ * </ul>
+ * Note that access to the pool datastructures is synchronized via the
+ * {@link AbstractConnPool#poolLock poolLock} in the base class,
+ * not via <code>synchronized</code> methods.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author and others
+ */
+public class ConnPoolByRoute extends AbstractConnPool {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** Connection operator for this pool */
+ protected final ClientConnectionOperator operator;
+
+ /** The list of free connections */
+ protected Queue<BasicPoolEntry> freeConnections;
+
+ /** The list of WaitingThreads waiting for a connection */
+ protected Queue<WaitingThread> waitingThreads;
+
+ /**
+ * A map of route-specific pools.
+ * Keys are of class {@link HttpRoute},
+ * values of class {@link RouteSpecificPool}.
+ */
+ protected final Map<HttpRoute, RouteSpecificPool> routeToPool;
+
+ protected final int maxTotalConnections;
+
+ private final ConnPerRoute connPerRoute;
+
+ /**
+ * Creates a new connection pool, managed by route.
+ */
+ public ConnPoolByRoute(final ClientConnectionOperator operator, final HttpParams params) {
+ super();
+ if (operator == null) {
+ throw new IllegalArgumentException("Connection operator may not be null");
+ }
+ this.operator = operator;
+
+ freeConnections = createFreeConnQueue();
+ waitingThreads = createWaitingThreadQueue();
+ routeToPool = createRouteToPoolMap();
+ maxTotalConnections = ConnManagerParams
+ .getMaxTotalConnections(params);
+ connPerRoute = ConnManagerParams
+ .getMaxConnectionsPerRoute(params);
+ }
+
+
+ /**
+ * Creates the queue for {@link #freeConnections}.
+ * Called once by the constructor.
+ *
+ * @return a queue
+ */
+ protected Queue<BasicPoolEntry> createFreeConnQueue() {
+ return new LinkedList<BasicPoolEntry>();
+ }
+
+ /**
+ * Creates the queue for {@link #waitingThreads}.
+ * Called once by the constructor.
+ *
+ * @return a queue
+ */
+ protected Queue<WaitingThread> createWaitingThreadQueue() {
+ return new LinkedList<WaitingThread>();
+ }
+
+ /**
+ * Creates the map for {@link #routeToPool}.
+ * Called once by the constructor.
+ *
+ * @return a map
+ */
+ protected Map<HttpRoute, RouteSpecificPool> createRouteToPoolMap() {
+ return new HashMap<HttpRoute, RouteSpecificPool>();
+ }
+
+
+ /**
+ * Creates a new route-specific pool.
+ * Called by {@link #getRoutePool} when necessary.
+ *
+ * @param route the route
+ *
+ * @return the new pool
+ */
+ protected RouteSpecificPool newRouteSpecificPool(HttpRoute route) {
+ return new RouteSpecificPool(route, connPerRoute.getMaxForRoute(route));
+ }
+
+
+ /**
+ * 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 <code>null</code>
+ *
+ * @return a waiting thread representation
+ */
+ protected WaitingThread newWaitingThread(Condition cond,
+ RouteSpecificPool rospl) {
+ return new WaitingThread(cond, rospl);
+ }
+
+
+ /**
+ * 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 <code>null</code> if <code>create</code> is <code>true</code>
+ */
+ protected RouteSpecificPool getRoutePool(HttpRoute route,
+ 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;
+ }
+
+
+ //@@@ consider alternatives for gathering statistics
+ public int getConnectionsInPool(HttpRoute route) {
+
+ poolLock.lock();
+ try {
+ // don't allow a pool to be created here!
+ RouteSpecificPool rospl = getRoutePool(route, false);
+ return (rospl != null) ? rospl.getEntryCount() : 0;
+
+ } 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(
+ long timeout,
+ 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 <code>timeout</code>,
+ * may be <code>null</code> 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(
+ HttpRoute route, Object state,
+ long timeout, TimeUnit tunit,
+ 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) {
+
+ if (isShutDown) {
+ throw new IllegalStateException
+ ("Connection pool shut down.");
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Total connections kept alive: " + freeConnections.size());
+ log.debug("Total issued connections: " + issuedConnections.size());
+ log.debug("Total allocated connection: " + 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;
+ }
+
+ 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();
+ 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");
+ }
+ }
+ } // while no entry
+
+ } finally {
+ poolLock.unlock();
+ }
+
+ return entry;
+
+ } // getEntry
+
+
+ // non-javadoc, see base class AbstractConnPool
+ @Override
+ public void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit) {
+
+ HttpRoute route = entry.getPlannedRoute();
+ if (log.isDebugEnabled()) {
+ log.debug("Freeing connection" +
+ " [" + route + "][" + entry.getState() + "]");
+ }
+
+ poolLock.lock();
+ try {
+ if (isShutDown) {
+ // the pool is shut down, release the
+ // connection's resources and get out of here
+ closeConnection(entry.getConnection());
+ return;
+ }
+
+ // no longer issued, we keep a hard reference now
+ issuedConnections.remove(entry.getWeakRef());
+
+ RouteSpecificPool rospl = getRoutePool(route, true);
+
+ if (reusable) {
+ rospl.freeEntry(entry);
+ freeConnections.add(entry);
+ idleConnHandler.add(entry.getConnection(), validDuration, timeUnit);
+ } else {
+ rospl.dropEntry();
+ numConnections--;
+ }
+
+ notifyWaitingThread(rospl);
+
+ } finally {
+ poolLock.unlock();
+ }
+
+ } // freeEntry
+
+
+
+ /**
+ * 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
+ * <code>null</code> if none is available
+ */
+ protected BasicPoolEntry getFreeEntry(RouteSpecificPool rospl, 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);
+ boolean valid = idleConnHandler.remove(entry.getConnection());
+ if(!valid) {
+ // 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.getConnection());
+ // 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 {
+ issuedConnections.add(entry.getWeakRef());
+ 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(RouteSpecificPool rospl,
+ ClientConnectionOperator op) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Creating new connection [" + rospl.getRoute() + "]");
+ }
+
+ // the entry will create the connection when needed
+ BasicPoolEntry entry =
+ new BasicPoolEntry(op, rospl.getRoute(), refQueue);
+
+ poolLock.lock();
+ try {
+
+ rospl.createdEntry(entry);
+ numConnections++;
+
+ issuedConnections.add(entry.getWeakRef());
+
+ } 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.
+ *
+ * <p><b>Note:</b> Does not remove the entry from the freeConnections list.
+ * It is assumed that the caller has already handled this step.</p>
+ * <!-- @@@ is that a good idea? or rather fix it? -->
+ *
+ * @param entry the pool entry for the connection to delete
+ */
+ protected void deleteEntry(BasicPoolEntry entry) {
+
+ HttpRoute route = entry.getPlannedRoute();
+
+ if (log.isDebugEnabled()) {
+ log.debug("Deleting connection"
+ + " [" + route + "][" + entry.getState() + "]");
+ }
+
+ poolLock.lock();
+ try {
+
+ closeConnection(entry.getConnection());
+
+ RouteSpecificPool rospl = getRoutePool(route, true);
+ rospl.deleteEntry(entry);
+ numConnections--;
+ if (rospl.isUnused()) {
+ routeToPool.remove(route);
+ }
+
+ idleConnHandler.remove(entry.getConnection());// not idle, but dead
+
+ } 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() {
+
+ try {
+ poolLock.lock();
+
+ //@@@ with get() instead of remove, we could
+ //@@@ leave the removing to deleteEntry()
+ BasicPoolEntry entry = freeConnections.remove();
+
+ if (entry != null) {
+ deleteEntry(entry);
+ } else if (log.isDebugEnabled()) {
+ log.debug("No free connection to delete.");
+ }
+
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+
+ // non-javadoc, see base class AbstractConnPool
+ @Override
+ protected void handleLostEntry(HttpRoute route) {
+
+ poolLock.lock();
+ try {
+
+ 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 <code>null</code>
+ */
+ protected void notifyWaitingThread(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();
+ }
+ }
+
+
+ //@@@ revise this cleanup stuff
+ //@@@ move method to base class when deleteEntry() is fixed
+ // non-javadoc, see base class AbstractConnPool
+ @Override
+ public void deleteClosedConnections() {
+
+ poolLock.lock();
+ try {
+
+ Iterator<BasicPoolEntry> iter = freeConnections.iterator();
+ while (iter.hasNext()) {
+ BasicPoolEntry entry = iter.next();
+ if (!entry.getConnection().isOpen()) {
+ iter.remove();
+ deleteEntry(entry);
+ }
+ }
+
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+
+ // non-javadoc, see base class AbstractConnPool
+ @Override
+ public void shutdown() {
+
+ poolLock.lock();
+ try {
+
+ super.shutdown();
+
+ // close all free connections
+ //@@@ move this to base class?
+ Iterator<BasicPoolEntry> ibpe = freeConnections.iterator();
+ while (ibpe.hasNext()) {
+ BasicPoolEntry entry = ibpe.next();
+ ibpe.remove();
+ closeConnection(entry.getConnection());
+ }
+
+ // wake up all waiting threads
+ Iterator<WaitingThread> iwth = waitingThreads.iterator();
+ while (iwth.hasNext()) {
+ WaitingThread waiter = iwth.next();
+ iwth.remove();
+ waiter.wakeup();
+ }
+
+ routeToPool.clear();
+
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+
+} // class ConnPoolByRoute
+
diff --git a/src/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java b/src/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
new file mode 100644
index 0000000..faf5e3b
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
@@ -0,0 +1,68 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java $
+ * $Revision: 652020 $
+ * $Date: 2008-04-27 14:23:31 -0700 (Sun, 27 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+
+/**
+ * Encapsulates a request for a {@link BasicPoolEntry}.
+ */
+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 <code>timeout</code>,
+ * may be <code>null</code> 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/src/org/apache/http/impl/conn/tsccm/RefQueueHandler.java b/src/org/apache/http/impl/conn/tsccm/RefQueueHandler.java
new file mode 100644
index 0000000..3af28cc
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/RefQueueHandler.java
@@ -0,0 +1,48 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueHandler.java $
+ * $Revision: 603874 $
+ * $Date: 2007-12-13 02:42:41 -0800 (Thu, 13 Dec 2007) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.lang.ref.Reference;
+
+
+/**
+ * Callback handler for {@link RefQueueWorker RefQueueWorker}.
+ */
+public interface RefQueueHandler {
+
+ /**
+ * Invoked when a reference is found on the queue.
+ *
+ * @param ref the reference to handle
+ */
+ public void handleReference(Reference<?> ref)
+ ;
+}
diff --git a/src/org/apache/http/impl/conn/tsccm/RefQueueWorker.java b/src/org/apache/http/impl/conn/tsccm/RefQueueWorker.java
new file mode 100644
index 0000000..9ad5c77
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/RefQueueWorker.java
@@ -0,0 +1,139 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueWorker.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+
+/**
+ * A worker thread for processing queued references.
+ * {@link Reference Reference}s can be
+ * {@link ReferenceQueue queued}
+ * automatically by the garbage collector.
+ * If that feature is used, a daemon thread should be executing
+ * this worker. It will pick up the queued references and pass them
+ * on to a handler for appropriate processing.
+ */
+public class RefQueueWorker implements Runnable {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** The reference queue to monitor. */
+ protected final ReferenceQueue<?> refQueue;
+
+ /** The handler for the references found. */
+ protected final RefQueueHandler refHandler;
+
+
+ /**
+ * The thread executing this handler.
+ * This attribute is also used as a shutdown indicator.
+ */
+ protected volatile Thread workerThread;
+
+
+ /**
+ * Instantiates a new worker to listen for lost connections.
+ *
+ * @param queue the queue on which to wait for references
+ * @param handler the handler to pass the references to
+ */
+ public RefQueueWorker(ReferenceQueue<?> queue, RefQueueHandler handler) {
+ if (queue == null) {
+ throw new IllegalArgumentException("Queue must not be null.");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("Handler must not be null.");
+ }
+
+ refQueue = queue;
+ refHandler = handler;
+ }
+
+
+ /**
+ * The main loop of this worker.
+ * If initialization succeeds, this method will only return
+ * after {@link #shutdown shutdown()}. Only one thread can
+ * execute the main loop at any time.
+ */
+ public void run() {
+
+ if (this.workerThread == null) {
+ this.workerThread = Thread.currentThread();
+ }
+
+ while (this.workerThread == Thread.currentThread()) {
+ try {
+ // remove the next reference and process it
+ Reference<?> ref = refQueue.remove();
+ refHandler.handleReference(ref);
+ } catch (InterruptedException e) {
+ //@@@ is logging really necessary? this here is the
+ //@@@ only reason for having a log in this class
+ if (log.isDebugEnabled()) {
+ log.debug(this.toString() + " interrupted", e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Shuts down this worker.
+ * It can be re-started afterwards by another call to {@link #run run()}.
+ */
+ public void shutdown() {
+ Thread wt = this.workerThread;
+ if (wt != null) {
+ this.workerThread = null; // indicate shutdown
+ wt.interrupt();
+ }
+ }
+
+
+ /**
+ * Obtains a description of this worker.
+ *
+ * @return a descriptive string for this worker
+ */
+ @Override
+ public String toString() {
+ return "RefQueueWorker::" + this.workerThread;
+ }
+
+} // class RefQueueWorker
+
diff --git a/src/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java b/src/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java
new file mode 100644
index 0000000..5c63933
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java
@@ -0,0 +1,301 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.io.IOException;
+import java.util.ListIterator;
+import java.util.Queue;
+import java.util.LinkedList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.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.
+ */
+public class RouteSpecificPool {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** The route this pool is for. */
+ protected final HttpRoute route;
+
+ /** the maximum number of entries allowed for this pool */
+ protected final int maxEntries;
+
+ /**
+ * 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<BasicPoolEntry> freeEntries;
+
+ /** The list of threads waiting for this pool. */
+ protected final Queue<WaitingThread> waitingThreads;
+
+ /** The number of created entries. */
+ protected int numEntries;
+
+
+ /**
+ * Creates a new route-specific pool.
+ *
+ * @param route the route for which to pool
+ * @param maxEntries the maximum number of entries allowed for this pool
+ */
+ public RouteSpecificPool(HttpRoute route, int maxEntries) {
+ this.route = route;
+ this.maxEntries = maxEntries;
+ this.freeEntries = new LinkedList<BasicPoolEntry>();
+ this.waitingThreads = new LinkedList<WaitingThread>();
+ 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 <code>true</code> if this pool is unused,
+ * <code>false</code> otherwise
+ */
+ public boolean isUnused() {
+ return (numEntries < 1) && waitingThreads.isEmpty();
+ }
+
+
+ /**
+ * Return remaining capacity of this pool
+ *
+ * @return capacity
+ */
+ public int getCapacity() {
+ return maxEntries - 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 <code>null</code> if there is none
+ */
+ public BasicPoolEntry allocEntry(final Object state) {
+ if (!freeEntries.isEmpty()) {
+ ListIterator<BasicPoolEntry> it = freeEntries.listIterator(freeEntries.size());
+ while (it.hasPrevious()) {
+ BasicPoolEntry entry = it.previous();
+ if (LangUtils.equals(state, entry.getState())) {
+ it.remove();
+ return entry;
+ }
+ }
+ }
+ if (!freeEntries.isEmpty()) {
+ BasicPoolEntry entry = freeEntries.remove();
+ entry.setState(null);
+ OperatedClientConnection conn = entry.getConnection();
+ try {
+ conn.close();
+ } catch (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(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 <i>not</i> 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(BasicPoolEntry entry) {
+
+ if (!route.equals(entry.getPlannedRoute())) {
+ throw new IllegalArgumentException
+ ("Entry not planned for this pool." +
+ "\npool: " + route +
+ "\nplan: " + entry.getPlannedRoute());
+ }
+
+ 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 <code>true</code> if the entry was found and deleted, or
+ * <code>false</code> if the entry was not found
+ */
+ public boolean deleteEntry(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() {
+ if (numEntries < 1) {
+ throw new IllegalStateException
+ ("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(WaitingThread wt) {
+ if (wt == null) {
+ throw new IllegalArgumentException
+ ("Waiting thread must not be null.");
+ }
+ this.waitingThreads.add(wt);
+ }
+
+
+ /**
+ * Checks whether there is a waiting thread in this pool.
+ *
+ * @return <code>true</code> if there is a waiting thread,
+ * <code>false</code> otherwise
+ */
+ public boolean hasThread() {
+ return !this.waitingThreads.isEmpty();
+ }
+
+
+ /**
+ * Returns the next thread in the queue.
+ *
+ * @return a waiting thread, or <code>null</code> 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(WaitingThread wt) {
+ if (wt == null)
+ return;
+
+ this.waitingThreads.remove(wt);
+ }
+
+
+} // class RouteSpecificPool
diff --git a/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java b/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
new file mode 100644
index 0000000..0781e05
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
@@ -0,0 +1,282 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java $
+ * $Revision: 673450 $
+ * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.params.HttpParams;
+import org.apache.http.impl.conn.DefaultClientConnectionOperator;
+
+
+
+/**
+ * Manages a pool of {@link OperatedClientConnection client connections}.
+ * <p>
+ * This class is derived from <code>MultiThreadedHttpConnectionManager</code>
+ * in HttpClient 3. See there for original authors.
+ * </p>
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *
+ *
+ * <!-- empty lines to avoid svn diff problems -->
+ * @version $Revision: 673450 $ $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
+ *
+ * @since 4.0
+ */
+public class ThreadSafeClientConnManager implements ClientConnectionManager {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /** The schemes supported by this connection manager. */
+ protected SchemeRegistry schemeRegistry;
+
+ /** The pool of connections being managed. */
+ protected final AbstractConnPool connectionPool;
+
+ /** The operator for opening and updating connections. */
+ protected ClientConnectionOperator connOperator;
+
+
+
+ /**
+ * Creates a new thread safe connection manager.
+ *
+ * @param params the parameters for this manager
+ * @param schreg the scheme registry, or
+ * <code>null</code> for the default registry
+ */
+ public ThreadSafeClientConnManager(HttpParams params,
+ SchemeRegistry schreg) {
+
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.schemeRegistry = schreg;
+ this.connOperator = createConnectionOperator(schreg);
+ this.connectionPool = createConnectionPool(params);
+
+ } // <constructor>
+
+
+ @Override
+ protected void finalize() throws Throwable {
+ shutdown();
+ super.finalize();
+ }
+
+
+ /**
+ * Hook for creating the connection pool.
+ *
+ * @return the connection pool to use
+ */
+ protected AbstractConnPool createConnectionPool(final HttpParams params) {
+
+ AbstractConnPool acp = new ConnPoolByRoute(connOperator, params);
+ boolean conngc = true; //@@@ check parameters to decide
+ if (conngc) {
+ acp.enableConnectionGC();
+ }
+ return acp;
+ }
+
+
+ /**
+ * 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 <code>null</code>
+ *
+ * @return the connection operator to use
+ */
+ protected ClientConnectionOperator
+ createConnectionOperator(SchemeRegistry schreg) {
+
+ return new DefaultClientConnectionOperator(schreg);
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public SchemeRegistry getSchemeRegistry() {
+ return this.schemeRegistry;
+ }
+
+
+ public ClientConnectionRequest requestConnection(
+ final HttpRoute route,
+ final Object state) {
+
+ final PoolEntryRequest poolRequest = connectionPool.requestPoolEntry(
+ route, state);
+
+ return new ClientConnectionRequest() {
+
+ public void abortRequest() {
+ poolRequest.abortRequest();
+ }
+
+ public ManagedClientConnection getConnection(
+ long timeout, TimeUnit tunit) throws InterruptedException,
+ ConnectionPoolTimeoutException {
+ if (route == null) {
+ throw new IllegalArgumentException("Route may not be null.");
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("ThreadSafeClientConnManager.getConnection: "
+ + route + ", timeout = " + timeout);
+ }
+
+ BasicPoolEntry entry = poolRequest.getPoolEntry(timeout, tunit);
+ return new BasicPooledConnAdapter(ThreadSafeClientConnManager.this, entry);
+ }
+
+ };
+
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
+
+ if (!(conn instanceof BasicPooledConnAdapter)) {
+ throw new IllegalArgumentException
+ ("Connection class mismatch, " +
+ "connection not obtained from this manager.");
+ }
+ BasicPooledConnAdapter hca = (BasicPooledConnAdapter) conn;
+ if ((hca.getPoolEntry() != null) && (hca.getManager() != this)) {
+ throw new IllegalArgumentException
+ ("Connection not obtained from this manager.");
+ }
+
+ try {
+ // make sure that the response has been read completely
+ if (hca.isOpen() && !hca.isMarkedReusable()) {
+ if (log.isDebugEnabled()) {
+ log.debug
+ ("Released connection open but not marked reusable.");
+ }
+ // 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 (IOException iox) {
+ //@@@ log as warning? let pass?
+ if (log.isDebugEnabled())
+ log.debug("Exception shutting down released connection.",
+ iox);
+ } finally {
+ BasicPoolEntry entry = (BasicPoolEntry) hca.getPoolEntry();
+ boolean reusable = hca.isMarkedReusable();
+ hca.detach();
+ if (entry != null) {
+ connectionPool.freeEntry(entry, reusable, validDuration, timeUnit);
+ }
+ }
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public void shutdown() {
+ connectionPool.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(HttpRoute route) {
+ return ((ConnPoolByRoute)connectionPool).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() {
+ synchronized (connectionPool) {
+ return connectionPool.numConnections; //@@@
+ }
+ }
+
+
+ // non-javadoc, see interface ClientConnectionManager
+ public void closeIdleConnections(long idleTimeout, TimeUnit tunit) {
+ // combine these two in a single call?
+ connectionPool.closeIdleConnections(idleTimeout, tunit);
+ connectionPool.deleteClosedConnections();
+ }
+
+ public void closeExpiredConnections() {
+ connectionPool.closeExpiredConnections();
+ connectionPool.deleteClosedConnections();
+ }
+
+
+} // class ThreadSafeClientConnManager
+
diff --git a/src/org/apache/http/impl/conn/tsccm/WaitingThread.java b/src/org/apache/http/impl/conn/tsccm/WaitingThread.java
new file mode 100644
index 0000000..a50e11f
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/WaitingThread.java
@@ -0,0 +1,197 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java $
+ * $Revision: 649217 $
+ * $Date: 2008-04-17 11:32:32 -0700 (Thu, 17 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+
+import java.util.Date;
+import java.util.concurrent.locks.Condition;
+
+
+/**
+ * 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.
+ * <br/>
+ * All methods assume external synchronization on the condition
+ * passed to the constructor.
+ * Instances of this class do <i>not</i> synchronize access!
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ */
+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 <code>null</code>
+ */
+ public WaitingThread(Condition cond, RouteSpecificPool pool) {
+
+ if (cond == null) {
+ throw new IllegalArgumentException("Condition must not be null.");
+ }
+
+ this.cond = cond;
+ this.pool = pool;
+ }
+
+
+ /**
+ * Obtains the condition.
+ *
+ * @return the condition on which to wait, never <code>null</code>
+ */
+ 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 <code>null</code>
+ */
+ public final RouteSpecificPool getPool() {
+ // not synchronized
+ return this.pool;
+ }
+
+
+ /**
+ * Obtains the thread, if there is one.
+ *
+ * @return the thread which is waiting, or <code>null</code>
+ */
+ 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.
+ * <br/>
+ * This method assumes external synchronization.
+ *
+ * @param deadline when to time out, or <code>null</code> for no timeout
+ *
+ * @return <code>true</code> if the condition was satisfied,
+ * <code>false</code> 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(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.
+ * <br/>
+ * 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/src/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java b/src/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java
new file mode 100644
index 0000000..1844457
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java $
+ * $Revision: 649220 $
+ * $Date: 2008-04-17 11:40:24 -0700 (Thu, 17 Apr 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn.tsccm;
+
+/** A simple class that can interrupt a {@link WaitingThread}. */
+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(WaitingThread waitingThread) {
+ this.waitingThread = waitingThread;
+ if (aborted)
+ waitingThread.interrupt();
+ }
+
+}
diff --git a/src/org/apache/http/impl/conn/tsccm/doc-files/tsccm-structure.png b/src/org/apache/http/impl/conn/tsccm/doc-files/tsccm-structure.png
new file mode 100644
index 0000000..2e2820d
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/doc-files/tsccm-structure.png
Binary files differ
diff --git a/src/org/apache/http/impl/conn/tsccm/package.html b/src/org/apache/http/impl/conn/tsccm/package.html
new file mode 100644
index 0000000..5aca5d4
--- /dev/null
+++ b/src/org/apache/http/impl/conn/tsccm/package.html
@@ -0,0 +1,205 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/package.html $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+
+The implementation of a thread-safe client connection manager.
+
+<center>
+<img src="doc-files/tsccm-structure.png" alt="Relation Diagram"/>
+</center>
+
+<p>
+The implementation is structured into three areas, as illustrated
+by the diagram above.
+Facing the application is the <i>Manager</i> (green), which internally
+maintains a <i>Pool</i> (yellow) of connections and waiting threads.
+Both Manager and Pool rely on <i>Operations</i> (cyan) to provide the
+actual connections.
+</p>
+<p>
+In order to allow connection garbage collection, it is
+imperative that hard object references between the areas are
+restricted to the relations indicated by arrows in the diagram:
+</p>
+<ul>
+<li>Applications reference only the Manager objects.</li>
+<li>Manager objects reference Pool objects, but not vice versa.</li>
+<li>Operations objects do not reference either Manager or Pool objects.</li>
+</ul>
+
+<p>
+The following table shows a selection of classes and interfaces,
+and their assignment to the three areas.
+</p>
+<center>
+<table border="1">
+<colgroup>
+ <col width="50%"/>
+ <col width="50%"/>
+</colgroup>
+
+<tr>
+<td style="text-align: center; background-color: #00ff00;">
+{@link org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager}
+</td>
+<td style="text-align: center; background-color: #ffff00;">
+{@link org.apache.http.impl.conn.tsccm.AbstractConnPool}
+</td>
+</tr>
+
+<tr>
+<td style="text-align: center; background-color: #00ff00;">
+{@link org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter}
+</td>
+<td style="text-align: center; background-color: #ffff00;">
+{@link org.apache.http.impl.conn.tsccm.ConnPoolByRoute}
+</td>
+</tr>
+
+<!-- appears on both sides! -->
+
+<tr>
+<td style="text-align: right; background-color: #00ff00;">
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}
+</td>
+<td style="text-align: left; background-color: #ffff00;">
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}
+</td>
+</tr>
+
+<!-- ====================== -->
+
+<tr style="border-width: 5px;">
+</tr>
+
+<tr>
+<td colspan="2" style="text-align: center; background-color: #00ffff;">
+{@link org.apache.http.conn.ClientConnectionOperator}
+</td>
+</tr>
+
+<tr>
+<td colspan="2" style="text-align: center; background-color: #00ffff;">
+{@link org.apache.http.conn.OperatedClientConnection}
+</td>
+</tr>
+
+</table>
+</center>
+
+<p>
+The Manager area has implementations for the connection management
+interfaces {@link org.apache.http.conn.ClientConnectionManager}
+and {@link org.apache.http.conn.ManagedClientConnection}.
+The latter is an adapter from managed to operated connections, based on a
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}.
+<br/>
+The Pool area shows an abstract pool class
+{@link org.apache.http.impl.conn.tsccm.AbstractConnPool}
+and a concrete implementation
+{@link org.apache.http.impl.conn.tsccm.ConnPoolByRoute}
+which uses the same basic algorithm as the
+<code>MultiThreadedHttpConnectionManager</code>
+in HttpClient 3.x.
+A pool contains instances of
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}.
+Most other classes in this package also belong to the Pool area.
+<br/>
+In the Operations area, you will find only the interfaces for
+operated connections as defined in the org.apache.http.conn package.
+The connection manager will work with all correct implementations
+of these interfaces. This package therefore does not define anything
+specific to the Operations area.
+</p>
+
+<p>
+As you have surely noticed, the
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntry}
+appears in both the Manager and Pool areas.
+This is where things get tricky for connection garbage collection.
+<br/>
+A connection pool may start a background thread to implement cleanup.
+In that case, the connection pool will not be garbage collected until
+it is shut down, since the background thread keeps a hard reference
+to the pool. The pool itself keeps hard references to the pooled entries,
+which in turn reference idle connections. Neither of these is subject
+to garbage collection.
+Only the shutdown of the pool will stop the background thread,
+thereby enabling garbage collection of the pool objects.
+<br/>
+A pool entry that is passed to an application by means of a connection
+adapter will move from the Pool area to the Manager area. When the
+connection is released by the application, the manager returns the
+entry back to the pool. With that step, the pool entry moves from
+the Manager area back to the Pool area.
+While the entry is in the Manager area, the pool MUST NOT keep a
+hard reference to it.
+</p>
+
+<p>
+The purpose of connection garbage collection is to detect when an
+application fails to return a connection. In order to achieve this,
+the only hard reference to the pool entry in the Manager area is
+in the connection wrapper. The manager will not keep a hard reference
+to the connection wrapper either, since that wrapper is effectively
+moving to the Application area.
+If the application drops it's reference to the connection wrapper,
+that wrapper will be garbage collected, and with it the pool entry.
+<br/>
+In order to detect garbage collection of pool entries handed out
+to the application, the pool keeps a <i>weak reference</i> to the
+entry. Instances of
+{@link org.apache.http.impl.conn.tsccm.BasicPoolEntryRef}
+combine the weak reference with information about the route for
+which the pool entry was allocated. If one of these entry references
+becomes stale, the pool can accommodate for the lost connection.
+This is triggered either by a background thread waiting for the
+references to be queued by the garbage collector, or by the
+application calling a {@link
+ org.apache.http.conn.ClientConnectionManager#closeIdleConnections cleanup}
+method of the connection manager.
+<br/>
+Basically the same trick is used for detecting garbage collection
+of the connection manager itself. The pool keeps a weak reference
+to the connection manager that created it. However, this will work
+only if there is a background thread to detect when that reference
+is queued by the garbage collector. Otherwise, a finalizer of the
+connection manager will shut down the pool and release it's resources.
+</p>
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java b/src/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java
new file mode 100644
index 0000000..1aa4d2c
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieAttributeHandler.java $
+ * $Revision: 503525 $
+ * $Date: 2007-02-04 17:15:08 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+
+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/src/org/apache/http/impl/cookie/AbstractCookieSpec.java b/src/org/apache/http/impl/cookie/AbstractCookieSpec.java
new file mode 100644
index 0000000..3e47a4d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/AbstractCookieSpec.java
@@ -0,0 +1,110 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieSpec.java $
+ * $Revision: 617207 $
+ * $Date: 2008-01-31 12:14:12 -0800 (Thu, 31 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieSpec;
+
+/**
+ * Abstract cookie specification which can delegate the job of parsing,
+ * validation or matching cookie attributes to a number of arbitrary
+ * {@link CookieAttributeHandler}s.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public abstract class AbstractCookieSpec implements CookieSpec {
+
+ /**
+ * Stores attribute name -> attribute handler mappings
+ */
+ private final Map<String, CookieAttributeHandler> attribHandlerMap;
+
+ /**
+ * Default constructor
+ * */
+ public AbstractCookieSpec() {
+ super();
+ this.attribHandlerMap = new HashMap<String, CookieAttributeHandler>(10);
+ }
+
+ public void registerAttribHandler(
+ final String name, final CookieAttributeHandler handler) {
+ if (name == null) {
+ throw new IllegalArgumentException("Attribute name may not be null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("Attribute handler may not be null");
+ }
+ this.attribHandlerMap.put(name, handler);
+ }
+
+ /**
+ * Finds an attribute handler {@link CookieAttributeHandler} for the
+ * given attribute. Returns <tt>null</tt> if no attribute handler is
+ * found for the specified attribute.
+ *
+ * @param name attribute name. e.g. Domain, Path, etc.
+ * @return an attribute handler or <tt>null</tt>
+ */
+ 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) {
+ CookieAttributeHandler handler = findAttribHandler(name);
+ if (handler == null) {
+ throw new IllegalStateException("Handler not registered for " +
+ name + " attribute.");
+ } else {
+ return handler;
+ }
+ }
+
+ protected Collection<CookieAttributeHandler> getAttribHandlers() {
+ return this.attribHandlerMap.values();
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BasicClientCookie.java b/src/org/apache/http/impl/cookie/BasicClientCookie.java
new file mode 100644
index 0000000..6ec6c2b
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicClientCookie.java
@@ -0,0 +1,376 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie.java $
+ * $Revision: 659191 $
+ * $Date: 2008-05-22 11:26:53 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.SetCookie;
+
+/**
+ * HTTP "magic-cookie" represents a piece of state information
+ * that the HTTP agent and the target server can exchange to maintain
+ * a session.
+ *
+ * @author B.C. Holmes
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
+ * @author Rod Waldhoff
+ * @author dIon Gillard
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
+ * @author Marc A. Saegesser
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @version $Revision: 659191 $
+ */
+public class BasicClientCookie implements SetCookie, ClientCookie, Cloneable {
+
+ /**
+ * 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();
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ this.name = name;
+ this.attribs = new HashMap<String, String>();
+ 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
+ * <tt>null</tt> 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(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 <tt>null</tt>
+ * if none exists.
+ * <p><strong>Note:</strong> 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. </p>
+ * @return Expiration {@link Date}, or <tt>null</tt>.
+ *
+ * @see #setExpiryDate(java.util.Date)
+ *
+ */
+ public Date getExpiryDate() {
+ return cookieExpiryDate;
+ }
+
+ /**
+ * Sets expiration date.
+ * <p><strong>Note:</strong> 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.</p>
+ *
+ * @param expiryDate the {@link Date} after which this cookie is no longer valid.
+ *
+ * @see #getExpiryDate
+ *
+ */
+ public void setExpiryDate (Date expiryDate) {
+ cookieExpiryDate = expiryDate;
+ }
+
+
+ /**
+ * Returns <tt>false</tt> if the cookie should be discarded at the end
+ * of the "session"; <tt>true</tt> otherwise.
+ *
+ * @return <tt>false</tt> if the cookie should be discarded at the end
+ * of the "session"; <tt>true</tt> 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(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(String path) {
+ cookiePath = path;
+ }
+
+ /**
+ * @return <code>true</code> 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.
+ * <p>
+ * When <tt>true</tt> 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 (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(int version) {
+ cookieVersion = version;
+ }
+
+ /**
+ * Returns true if this cookie has expired.
+ * @param date Current time
+ *
+ * @return <tt>true</tt> if the cookie has expired.
+ */
+ public boolean isExpired(final Date date) {
+ if (date == null) {
+ throw new IllegalArgumentException("Date may not be null");
+ }
+ 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 {
+ BasicClientCookie clone = (BasicClientCookie) super.clone();
+ clone.attribs = new HashMap<String, String>(this.attribs);
+ return clone;
+ }
+
+ @Override
+ public String toString() {
+ 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<String, String> 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/src/org/apache/http/impl/cookie/BasicClientCookie2.java b/src/org/apache/http/impl/cookie/BasicClientCookie2.java
new file mode 100644
index 0000000..86ec60d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicClientCookie2.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie2.java $
+ * $Revision: 659191 $
+ * $Date: 2008-05-22 11:26:53 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.Date;
+
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * HTTP "magic-cookie" represents a piece of state information
+ * that the HTTP agent and the target server can exchange to maintain
+ * a session as specified by RFC2965.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public class BasicClientCookie2 extends BasicClientCookie implements SetCookie2 {
+
+ 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(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 {
+ BasicClientCookie2 clone = (BasicClientCookie2) super.clone();
+ clone.ports = this.ports.clone();
+ return clone;
+ }
+
+}
+
diff --git a/src/org/apache/http/impl/cookie/BasicCommentHandler.java b/src/org/apache/http/impl/cookie/BasicCommentHandler.java
new file mode 100644
index 0000000..ce8baea
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicCommentHandler.java
@@ -0,0 +1,50 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicCommentHandler.java $
+ * $Revision: 558519 $
+ * $Date: 2007-07-22 11:19:49 -0700 (Sun, 22 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicCommentHandler extends AbstractCookieAttributeHandler {
+
+ public BasicCommentHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ cookie.setComment(value);
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BasicDomainHandler.java b/src/org/apache/http/impl/cookie/BasicDomainHandler.java
new file mode 100644
index 0000000..267faf8
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicDomainHandler.java
@@ -0,0 +1,122 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicDomainHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicDomainHandler implements CookieAttributeHandler {
+
+ public BasicDomainHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ 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 {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ // 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.
+ String host = origin.getHost();
+ String domain = cookie.getDomain();
+ if (domain == null) {
+ throw new MalformedCookieException("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 MalformedCookieException(
+ "Illegal domain attribute \"" + domain
+ + "\". Domain of origin: \"" + host + "\"");
+ }
+ }
+ } else {
+ if (!host.equals(domain)) {
+ throw new MalformedCookieException(
+ "Illegal domain attribute \"" + domain
+ + "\". Domain of origin: \"" + host + "\"");
+ }
+ }
+ }
+
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ 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/src/org/apache/http/impl/cookie/BasicExpiresHandler.java b/src/org/apache/http/impl/cookie/BasicExpiresHandler.java
new file mode 100644
index 0000000..a53519e
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicExpiresHandler.java
@@ -0,0 +1,65 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicExpiresHandler.java $
+ * $Revision: 558519 $
+ * $Date: 2007-07-22 11:19:49 -0700 (Sun, 22 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+
+public class BasicExpiresHandler extends AbstractCookieAttributeHandler {
+
+ /** Valid date patterns */
+ private final String[] datepatterns;
+
+ public BasicExpiresHandler(final String[] datepatterns) {
+ if (datepatterns == null) {
+ throw new IllegalArgumentException("Array of date patterns may not be null");
+ }
+ this.datepatterns = datepatterns;
+ }
+
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (value == null) {
+ throw new MalformedCookieException("Missing value for expires attribute");
+ }
+ try {
+ cookie.setExpiryDate(DateUtils.parseDate(value, this.datepatterns));
+ } catch (DateParseException dpe) {
+ throw new MalformedCookieException("Unable to parse expires attribute: "
+ + value);
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BasicMaxAgeHandler.java b/src/org/apache/http/impl/cookie/BasicMaxAgeHandler.java
new file mode 100644
index 0000000..92a5c7d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicMaxAgeHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java $
+ * $Revision: 581953 $
+ * $Date: 2007-10-04 08:53:50 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import java.util.Date;
+
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicMaxAgeHandler extends AbstractCookieAttributeHandler {
+
+ public BasicMaxAgeHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (value == null) {
+ throw new MalformedCookieException("Missing value for max-age attribute");
+ }
+ int age;
+ try {
+ age = Integer.parseInt(value);
+ } catch (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/src/org/apache/http/impl/cookie/BasicPathHandler.java b/src/org/apache/http/impl/cookie/BasicPathHandler.java
new file mode 100644
index 0000000..43a12c8
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicPathHandler.java
@@ -0,0 +1,91 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicPathHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicPathHandler implements CookieAttributeHandler {
+
+ public BasicPathHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (value == null || value.trim().length() == 0) {
+ value = "/";
+ }
+ cookie.setPath(value);
+ }
+
+ public void validate(final Cookie cookie, final CookieOrigin origin)
+ throws MalformedCookieException {
+ if (!match(cookie, origin)) {
+ throw new MalformedCookieException(
+ "Illegal path attribute \"" + cookie.getPath()
+ + "\". Path of origin: \"" + origin.getPath() + "\"");
+ }
+ }
+
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ 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/src/org/apache/http/impl/cookie/BasicSecureHandler.java b/src/org/apache/http/impl/cookie/BasicSecureHandler.java
new file mode 100644
index 0000000..9100b9c
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BasicSecureHandler.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BasicSecureHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class BasicSecureHandler extends AbstractCookieAttributeHandler {
+
+ public BasicSecureHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ cookie.setSecure(true);
+ }
+
+ @Override
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ return !cookie.isSecure() || origin.isSecure();
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BestMatchSpec.java b/src/org/apache/http/impl/cookie/BestMatchSpec.java
new file mode 100644
index 0000000..e33fec3
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BestMatchSpec.java
@@ -0,0 +1,186 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java $
+ * $Revision: 657334 $
+ * $Date: 2008-05-17 04:44:16 -0700 (Sat, 17 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.MalformedCookieException;
+
+/**
+ * 'Meta' cookie specification that selects a cookie policy depending
+ * on the format of the cookie(s)
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BestMatchSpec implements CookieSpec {
+
+ private final String[] datepatterns;
+ private final boolean oneHeader;
+
+ private RFC2965Spec strict;
+ private BrowserCompatSpec compat;
+ private NetscapeDraftSpec netscape;
+
+ public BestMatchSpec(final String[] datepatterns, boolean oneHeader) {
+ super();
+ this.datepatterns = datepatterns;
+ 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 BrowserCompatSpec getCompat() {
+ if (this.compat == null) {
+ this.compat = new BrowserCompatSpec(this.datepatterns);
+ }
+ return compat;
+ }
+
+ private NetscapeDraftSpec getNetscape() {
+ if (this.netscape == null) {
+ String[] patterns = this.datepatterns;
+ if (patterns == null) {
+ patterns = BrowserCompatSpec.DATE_PATTERNS;
+ }
+ this.netscape = new NetscapeDraftSpec(patterns);
+ }
+ return netscape;
+ }
+
+ public List<Cookie> parse(
+ final Header header,
+ final CookieOrigin origin) throws MalformedCookieException {
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ HeaderElement[] helems = header.getElements();
+ boolean versioned = false;
+ boolean netscape = false;
+ for (HeaderElement helem: helems) {
+ if (helem.getParameterByName("version") != null) {
+ versioned = true;
+ }
+ if (helem.getParameterByName("expires") != null) {
+ netscape = true;
+ }
+ }
+ if (netscape) {
+
+ }
+ // Do we have a cookie with a version attribute?
+ if (versioned) {
+ return getStrict().parse(helems, origin);
+ } else if (netscape) {
+ // Need to parse the header again,
+ // because Netscape draft cannot handle
+ // comma separators
+ return getNetscape().parse(header, origin);
+ } else {
+ return getCompat().parse(helems, origin);
+ }
+ }
+
+ public void validate(
+ final Cookie cookie,
+ final CookieOrigin origin) throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ if (cookie.getVersion() > 0) {
+ getStrict().validate(cookie, origin);
+ } else {
+ getCompat().validate(cookie, origin);
+ }
+ }
+
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ if (cookie.getVersion() > 0) {
+ return getStrict().match(cookie, origin);
+ } else {
+ return getCompat().match(cookie, origin);
+ }
+ }
+
+ public List<Header> formatCookies(final List<Cookie> cookies) {
+ if (cookies == null) {
+ throw new IllegalArgumentException("List of cookie may not be null");
+ }
+ int version = Integer.MAX_VALUE;
+ for (Cookie cookie: cookies) {
+ if (cookie.getVersion() < version) {
+ version = cookie.getVersion();
+ }
+ }
+ if (version > 0) {
+ return getStrict().formatCookies(cookies);
+ } else {
+ return getCompat().formatCookies(cookies);
+ }
+ }
+
+ public int getVersion() {
+ return getStrict().getVersion();
+ }
+
+ public Header getVersionHeader() {
+ return getStrict().getVersionHeader();
+ }
+
+} \ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/BestMatchSpecFactory.java b/src/org/apache/http/impl/cookie/BestMatchSpecFactory.java
new file mode 100644
index 0000000..cb632bb
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BestMatchSpecFactory.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpecFactory.java $
+ * $Revision: 613707 $
+ * $Date: 2008-01-20 16:28:37 -0800 (Sun, 20 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BestMatchSpecFactory implements CookieSpecFactory {
+
+ public CookieSpec newInstance(final HttpParams params) {
+ if (params != null) {
+ return new BestMatchSpec(
+ (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS),
+ params.getBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, false));
+ } else {
+ return new BestMatchSpec();
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BrowserCompatSpec.java b/src/org/apache/http/impl/cookie/BrowserCompatSpec.java
new file mode 100644
index 0000000..d7bc0da
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BrowserCompatSpec.java
@@ -0,0 +1,188 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpec.java $
+ * $Revision: 657334 $
+ * $Date: 2008-05-17 04:44:16 -0700 (Sat, 17 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Cookie specification that strives to closely mimic (mis)behavior of
+ * common web browser applications such as Microsoft Internet Explorer
+ * and Mozilla FireFox.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BrowserCompatSpec extends CookieSpecBase {
+
+ /** Valid date patterns used per default */
+ protected static final String[] 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) {
+ super();
+ if (datepatterns != null) {
+ this.datepatterns = datepatterns.clone();
+ } else {
+ this.datepatterns = DATE_PATTERNS;
+ }
+ registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler());
+ 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));
+ }
+
+ /** Default constructor */
+ public BrowserCompatSpec() {
+ this(null);
+ }
+
+ public List<Cookie> parse(final Header header, final CookieOrigin origin)
+ throws MalformedCookieException {
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ String headervalue = header.getValue();
+ boolean isNetscapeCookie = false;
+ int i1 = headervalue.toLowerCase(Locale.ENGLISH).indexOf("expires=");
+ if (i1 != -1) {
+ i1 += "expires=".length();
+ int i2 = headervalue.indexOf(';', i1);
+ if (i2 == -1) {
+ i2 = headervalue.length();
+ }
+ try {
+ DateUtils.parseDate(headervalue.substring(i1, i2), this.datepatterns);
+ isNetscapeCookie = true;
+ } catch (DateParseException e) {
+ // Does not look like a valid expiry date
+ }
+ }
+ HeaderElement[] elems = null;
+ if (isNetscapeCookie) {
+ NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT;
+ CharArrayBuffer buffer;
+ ParserCursor cursor;
+ if (header instanceof FormattedHeader) {
+ buffer = ((FormattedHeader) header).getBuffer();
+ cursor = new ParserCursor(
+ ((FormattedHeader) header).getValuePos(),
+ buffer.length());
+ } else {
+ 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());
+ }
+ elems = new HeaderElement[] { parser.parseHeader(buffer, cursor) };
+ } else {
+ elems = header.getElements();
+ }
+ return parse(elems, origin);
+ }
+
+ public List<Header> formatCookies(final List<Cookie> cookies) {
+ if (cookies == null) {
+ throw new IllegalArgumentException("List of cookies may not be null");
+ }
+ if (cookies.isEmpty()) {
+ throw new IllegalArgumentException("List of cookies may not be empty");
+ }
+ CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size());
+ buffer.append(SM.COOKIE);
+ buffer.append(": ");
+ for (int i = 0; i < cookies.size(); i++) {
+ Cookie cookie = cookies.get(i);
+ if (i > 0) {
+ buffer.append("; ");
+ }
+ buffer.append(cookie.getName());
+ buffer.append("=");
+ String s = cookie.getValue();
+ if (s != null) {
+ buffer.append(s);
+ }
+ }
+ List<Header> headers = new ArrayList<Header>(1);
+ headers.add(new BufferedHeader(buffer));
+ return headers;
+ }
+
+ public int getVersion() {
+ return 0;
+ }
+
+ public Header getVersionHeader() {
+ return null;
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java b/src/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java
new file mode 100644
index 0000000..71c0c05
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpecFactory.java $
+ * $Revision: 576068 $
+ * $Date: 2007-09-16 03:25:01 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class BrowserCompatSpecFactory implements CookieSpecFactory {
+
+ public CookieSpec newInstance(final HttpParams params) {
+ if (params != null) {
+ return new BrowserCompatSpec(
+ (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS));
+ } else {
+ return new BrowserCompatSpec();
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/CookieSpecBase.java b/src/org/apache/http/impl/cookie/CookieSpecBase.java
new file mode 100644
index 0000000..8e70bb1
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/CookieSpecBase.java
@@ -0,0 +1,131 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/CookieSpecBase.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+
+/**
+ * Cookie management functions shared by all specification.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+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<Cookie> parse(final HeaderElement[] elems, final CookieOrigin origin)
+ throws MalformedCookieException {
+ List<Cookie> cookies = new ArrayList<Cookie>(elems.length);
+ for (HeaderElement headerelement : elems) {
+ String name = headerelement.getName();
+ String value = headerelement.getValue();
+ if (name == null || name.length() == 0) {
+ throw new MalformedCookieException("Cookie name may not be empty");
+ }
+
+ BasicClientCookie cookie = new BasicClientCookie(name, value);
+ cookie.setPath(getDefaultPath(origin));
+ cookie.setDomain(getDefaultDomain(origin));
+
+ // cycle through the parameters
+ NameValuePair[] attribs = headerelement.getParameters();
+ for (int j = attribs.length - 1; j >= 0; j--) {
+ NameValuePair attrib = attribs[j];
+ String s = attrib.getName().toLowerCase(Locale.ENGLISH);
+
+ cookie.setAttribute(s, attrib.getValue());
+
+ 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 {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ for (CookieAttributeHandler handler: getAttribHandlers()) {
+ handler.validate(cookie, origin);
+ }
+ }
+
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ for (CookieAttributeHandler handler: getAttribHandlers()) {
+ if (!handler.match(cookie, origin)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/DateParseException.java b/src/org/apache/http/impl/cookie/DateParseException.java
new file mode 100644
index 0000000..c80b669
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/DateParseException.java
@@ -0,0 +1,60 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/DateParseException.java $
+ * $Revision: 609105 $
+ * $Date: 2008-01-05 00:55:00 -0800 (Sat, 05 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+
+/**
+ * An exception to indicate an error parsing a date string.
+ *
+ * @see DateUtils
+ *
+ * @author Michael Becke
+ */
+public class DateParseException extends Exception {
+
+ private static final long serialVersionUID = 4417696455000643370L;
+
+ /**
+ *
+ */
+ public DateParseException() {
+ super();
+ }
+
+ /**
+ * @param message the exception message
+ */
+ public DateParseException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/DateUtils.java b/src/org/apache/http/impl/cookie/DateUtils.java
new file mode 100644
index 0000000..a0a056c
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/DateUtils.java
@@ -0,0 +1,261 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/DateUtils.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.lang.ref.SoftReference;
+import java.text.ParseException;
+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;
+
+/**
+ * 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.
+ *
+ * @author Christopher Brown
+ * @author Michael Becke
+ */
+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 = "EEEE, dd-MMM-yy HH:mm:ss zzz";
+
+ /**
+ * Date format pattern used to parse HTTP date headers in ANSI C
+ * <code>asctime()</code> format.
+ */
+ public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
+
+ private static final String[] DEFAULT_PATTERNS = new String[] {
+ PATTERN_RFC1036,
+ PATTERN_RFC1123,
+ PATTERN_ASCTIME
+ };
+
+ private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
+
+ public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+
+ static {
+ 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
+ *
+ * @throws DateParseException if the value could not be parsed using any of the
+ * supported date formats
+ */
+ public static Date parseDate(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, 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
+ * <code>startDate</code> to <code>startDate + 100 years</code>. This value may
+ * be <code>null</code>. When <code>null</code> is given as a parameter, year
+ * <code>2000</code> will be used.
+ *
+ * @return the parsed date
+ *
+ * @throws DateParseException if none of the dataFormats could parse the dateValue
+ */
+ public static Date parseDate(
+ String dateValue,
+ String[] dateFormats,
+ Date startDate
+ ) throws DateParseException {
+
+ if (dateValue == null) {
+ throw new IllegalArgumentException("dateValue is null");
+ }
+ if (dateFormats == null) {
+ dateFormats = DEFAULT_PATTERNS;
+ }
+ if (startDate == null) {
+ startDate = DEFAULT_TWO_DIGIT_YEAR_START;
+ }
+ // trim single quotes around date if present
+ // see issue #5279
+ if (dateValue.length() > 1
+ && dateValue.startsWith("'")
+ && dateValue.endsWith("'")
+ ) {
+ dateValue = dateValue.substring (1, dateValue.length() - 1);
+ }
+
+ for (String dateFormat : dateFormats) {
+ SimpleDateFormat dateParser = DateFormatHolder.formatFor(dateFormat);
+ dateParser.set2DigitYearStart(startDate);
+
+ try {
+ return dateParser.parse(dateValue);
+ } catch (ParseException pe) {
+ // ignore this exception, we will try the next format
+ }
+ }
+
+ // we were unable to parse the date
+ throw new DateParseException("Unable to parse the date " + dateValue);
+ }
+
+ /**
+ * 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(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(Date date, String pattern) {
+ if (date == null) throw new IllegalArgumentException("date is null");
+ if (pattern == null) throw new IllegalArgumentException("pattern is null");
+
+ SimpleDateFormat formatter = DateFormatHolder.formatFor(pattern);
+ return formatter.format(date);
+ }
+
+ /** 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}.
+ *
+ * @author Daniel Mueller
+ */
+ final static class DateFormatHolder {
+
+ private static final ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>>
+ THREADLOCAL_FORMATS = new ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>>() {
+
+ @Override
+ protected SoftReference<Map<String, SimpleDateFormat>> initialValue() {
+ return new SoftReference<Map<String, SimpleDateFormat>>(
+ new HashMap<String, SimpleDateFormat>());
+ }
+
+ };
+
+ /**
+ * creates a {@link SimpleDateFormat} for the requested format string.
+ *
+ * @param pattern
+ * a non-<code>null</code> format String according to
+ * {@link SimpleDateFormat}. The format is not checked against
+ * <code>null</code> 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(String pattern) {
+ SoftReference<Map<String, SimpleDateFormat>> ref = THREADLOCAL_FORMATS.get();
+ Map<String, SimpleDateFormat> formats = ref.get();
+ if (formats == null) {
+ formats = new HashMap<String, SimpleDateFormat>();
+ THREADLOCAL_FORMATS.set(
+ new SoftReference<Map<String, SimpleDateFormat>>(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;
+ }
+
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDomainHandler.java b/src/org/apache/http/impl/cookie/NetscapeDomainHandler.java
new file mode 100644
index 0000000..8b785ae
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDomainHandler.java
@@ -0,0 +1,106 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDomainHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import java.util.Locale;
+import java.util.StringTokenizer;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+
+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
+ String host = origin.getHost();
+ String domain = cookie.getDomain();
+ if (host.contains(".")) {
+ int domainParts = new StringTokenizer(domain, ".").countTokens();
+
+ if (isSpecialDomain(domain)) {
+ if (domainParts < 2) {
+ throw new MalformedCookieException("Domain attribute \""
+ + domain
+ + "\" violates the Netscape cookie specification for "
+ + "special domains");
+ }
+ } else {
+ if (domainParts < 3) {
+ throw new MalformedCookieException("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(Cookie cookie, CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ String host = origin.getHost();
+ String domain = cookie.getDomain();
+ if (domain == null) {
+ return false;
+ }
+ return host.endsWith(domain);
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java b/src/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java
new file mode 100644
index 0000000..ca6b7fa
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java
@@ -0,0 +1,78 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDraftHeaderParser.java $
+ * $Revision: 603563 $
+ * $Date: 2007-12-12 03:17:55 -0800 (Wed, 12 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.message.BasicHeaderElement;
+import org.apache.http.message.BasicHeaderValueParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+public class NetscapeDraftHeaderParser {
+
+ public final static NetscapeDraftHeaderParser DEFAULT = new NetscapeDraftHeaderParser();
+
+ private final static char[] DELIMITERS = new char[] { ';' };
+
+ private final BasicHeaderValueParser nvpParser;
+
+ public NetscapeDraftHeaderParser() {
+ super();
+ this.nvpParser = BasicHeaderValueParser.DEFAULT;
+ }
+
+ public HeaderElement parseHeader(
+ final CharArrayBuffer buffer,
+ final ParserCursor cursor) throws ParseException {
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+ NameValuePair nvp = this.nvpParser.parseNameValuePair(buffer, cursor, DELIMITERS);
+ List<NameValuePair> params = new ArrayList<NameValuePair>();
+ while (!cursor.atEnd()) {
+ NameValuePair param = this.nvpParser.parseNameValuePair(buffer, cursor, DELIMITERS);
+ params.add(param);
+ }
+ return new BasicHeaderElement(
+ nvp.getName(),
+ nvp.getValue(), params.toArray(new NameValuePair[params.size()]));
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDraftSpec.java b/src/org/apache/http/impl/cookie/NetscapeDraftSpec.java
new file mode 100644
index 0000000..3bc4f9f
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDraftSpec.java
@@ -0,0 +1,182 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDraftSpec.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Netscape cookie draft compliant cookie policy
+ *
+ * @author B.C. Holmes
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
+ * @author Rod Waldhoff
+ * @author dIon Gillard
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
+ * @author Marc A. Saegesser
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @since 4.0
+ */
+public class NetscapeDraftSpec extends CookieSpecBase {
+
+ protected static final String EXPIRES_PATTERN = "EEE, dd-MMM-yyyy 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 <tt>Cookie</tt>s.
+ *
+ * <p>Syntax of the Set-Cookie HTTP Response Header:</p>
+ *
+ * <p>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.</p>
+ *
+ * <PRE>
+ * Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
+ * </PRE>
+ *
+ * <p>Please note that Netscape draft specification does not fully
+ * conform to the HTTP header format. Netscape draft does not specify
+ * whether multiple cookies may be sent in one header. Hence, comma
+ * character may be present in unquoted cookie value or unquoted
+ * parameter value.</p>
+ *
+ * @see <a href="http://wp.netscape.com/newsref/std/cookie_spec.html">
+ * The Cookie Spec.</a>
+ *
+ * @param header the <tt>Set-Cookie</tt> received from the server
+ * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie value
+ * @throws MalformedCookieException if an exception occurs during parsing
+ */
+ public List<Cookie> parse(final Header header, final CookieOrigin origin)
+ throws MalformedCookieException {
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT;
+ CharArrayBuffer buffer;
+ ParserCursor cursor;
+ if (header instanceof FormattedHeader) {
+ buffer = ((FormattedHeader) header).getBuffer();
+ cursor = new ParserCursor(
+ ((FormattedHeader) header).getValuePos(),
+ buffer.length());
+ } else {
+ 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<Header> formatCookies(final List<Cookie> cookies) {
+ if (cookies == null) {
+ throw new IllegalArgumentException("List of cookies may not be null");
+ }
+ if (cookies.isEmpty()) {
+ throw new IllegalArgumentException("List of cookies may not be empty");
+ }
+ CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size());
+ buffer.append(SM.COOKIE);
+ buffer.append(": ");
+ for (int i = 0; i < cookies.size(); i++) {
+ Cookie cookie = cookies.get(i);
+ if (i > 0) {
+ buffer.append("; ");
+ }
+ buffer.append(cookie.getName());
+ String s = cookie.getValue();
+ if (s != null) {
+ buffer.append("=");
+ buffer.append(s);
+ }
+ }
+ List<Header> headers = new ArrayList<Header>(1);
+ headers.add(new BufferedHeader(buffer));
+ return headers;
+ }
+
+ public int getVersion() {
+ return 0;
+ }
+
+ public Header getVersionHeader() {
+ return null;
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java b/src/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java
new file mode 100644
index 0000000..0dcb187
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/NetscapeDraftSpecFactory.java $
+ * $Revision: 657334 $
+ * $Date: 2008-05-17 04:44:16 -0700 (Sat, 17 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class NetscapeDraftSpecFactory implements CookieSpecFactory {
+
+ public CookieSpec newInstance(final HttpParams params) {
+ if (params != null) {
+ return new NetscapeDraftSpec(
+ (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS));
+ } else {
+ return new NetscapeDraftSpec();
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109DomainHandler.java b/src/org/apache/http/impl/cookie/RFC2109DomainHandler.java
new file mode 100644
index 0000000..9cfd484
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109DomainHandler.java
@@ -0,0 +1,126 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109DomainHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import java.util.Locale;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class RFC2109DomainHandler implements CookieAttributeHandler {
+
+ public RFC2109DomainHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ 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 {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ String host = origin.getHost();
+ String domain = cookie.getDomain();
+ if (domain == null) {
+ throw new MalformedCookieException("Cookie domain may not be null");
+ }
+ if (!domain.equals(host)) {
+ int dotIndex = domain.indexOf('.');
+ if (dotIndex == -1) {
+ throw new MalformedCookieException("Domain attribute \""
+ + domain
+ + "\" does not match the host \""
+ + host + "\"");
+ }
+ // domain must start with dot
+ if (!domain.startsWith(".")) {
+ throw new MalformedCookieException("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 MalformedCookieException("Domain attribute \""
+ + domain
+ + "\" violates RFC 2109: domain must contain an embedded dot");
+ }
+ host = host.toLowerCase(Locale.ENGLISH);
+ if (!host.endsWith(domain)) {
+ throw new MalformedCookieException(
+ "Illegal domain attribute \"" + domain
+ + "\". Domain of origin: \"" + host + "\"");
+ }
+ // host minus domain may not contain any dots
+ String hostWithoutDomain = host.substring(0, host.length() - domain.length());
+ if (hostWithoutDomain.indexOf('.') != -1) {
+ throw new MalformedCookieException("Domain attribute \""
+ + domain
+ + "\" violates RFC 2109: host minus domain may not contain any dots");
+ }
+ }
+ }
+
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ String host = origin.getHost();
+ String domain = cookie.getDomain();
+ if (domain == null) {
+ return false;
+ }
+ return host.equals(domain) || (domain.startsWith(".") && host.endsWith(domain));
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109Spec.java b/src/org/apache/http/impl/cookie/RFC2109Spec.java
new file mode 100644
index 0000000..9e45408
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109Spec.java
@@ -0,0 +1,246 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java $
+ * $Revision: 677240 $
+ * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.CookiePathComparator;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * RFC 2109 compliant cookie policy
+ *
+ * @author B.C. Holmes
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
+ * @author Rod Waldhoff
+ * @author dIon Gillard
+ * @author Sean C. Sullivan
+ * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
+ * @author Marc A. Saegesser
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @since 4.0
+ */
+
+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, 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<Cookie> parse(final Header header, final CookieOrigin origin)
+ throws MalformedCookieException {
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ HeaderElement[] elems = header.getElements();
+ return parse(elems, origin);
+ }
+
+ @Override
+ public void validate(final Cookie cookie, final CookieOrigin origin)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ String name = cookie.getName();
+ if (name.indexOf(' ') != -1) {
+ throw new MalformedCookieException("Cookie name may not contain blanks");
+ }
+ if (name.startsWith("$")) {
+ throw new MalformedCookieException("Cookie name may not start with $");
+ }
+ super.validate(cookie, origin);
+ }
+
+ public List<Header> formatCookies(List<Cookie> cookies) {
+ if (cookies == null) {
+ throw new IllegalArgumentException("List of cookies may not be null");
+ }
+ if (cookies.isEmpty()) {
+ throw new IllegalArgumentException("List of cookies may not be empty");
+ }
+ if (cookies.size() > 1) {
+ // Create a mutable copy and sort the copy.
+ cookies = new ArrayList<Cookie>(cookies);
+ Collections.sort(cookies, PATH_COMPARATOR);
+ }
+ if (this.oneHeader) {
+ return doFormatOneHeader(cookies);
+ } else {
+ return doFormatManyHeaders(cookies);
+ }
+ }
+
+ private List<Header> doFormatOneHeader(final List<Cookie> cookies) {
+ int version = Integer.MAX_VALUE;
+ // Pick the lowest common denominator
+ for (Cookie cookie : cookies) {
+ if (cookie.getVersion() < version) {
+ version = cookie.getVersion();
+ }
+ }
+ CharArrayBuffer buffer = new CharArrayBuffer(40 * cookies.size());
+ buffer.append(SM.COOKIE);
+ buffer.append(": ");
+ buffer.append("$Version=");
+ buffer.append(Integer.toString(version));
+ for (Cookie cooky : cookies) {
+ buffer.append("; ");
+ Cookie cookie = cooky;
+ formatCookieAsVer(buffer, cookie, version);
+ }
+ List<Header> headers = new ArrayList<Header>(1);
+ headers.add(new BufferedHeader(buffer));
+ return headers;
+ }
+
+ private List<Header> doFormatManyHeaders(final List<Cookie> cookies) {
+ List<Header> headers = new ArrayList<Header>(cookies.size());
+ for (Cookie cookie : cookies) {
+ int version = cookie.getVersion();
+ 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 <tt>"Cookie"</tt>
+ * 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, 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 <tt>"Cookie"</tt> 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, 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;
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109SpecFactory.java b/src/org/apache/http/impl/cookie/RFC2109SpecFactory.java
new file mode 100644
index 0000000..35c506e
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109SpecFactory.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109SpecFactory.java $
+ * $Revision: 576068 $
+ * $Date: 2007-09-16 03:25:01 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class RFC2109SpecFactory implements CookieSpecFactory {
+
+ public CookieSpec newInstance(final HttpParams params) {
+ if (params != null) {
+ return new RFC2109Spec(
+ (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS),
+ params.getBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, false));
+ } else {
+ return new RFC2109Spec();
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2109VersionHandler.java b/src/org/apache/http/impl/cookie/RFC2109VersionHandler.java
new file mode 100644
index 0000000..d2c4955
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2109VersionHandler.java
@@ -0,0 +1,74 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109VersionHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+public class RFC2109VersionHandler extends AbstractCookieAttributeHandler {
+
+ public RFC2109VersionHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ 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 (NumberFormatException e) {
+ throw new MalformedCookieException("Invalid version: "
+ + e.getMessage());
+ }
+ }
+
+ @Override
+ public void validate(final Cookie cookie, final CookieOrigin origin)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (cookie.getVersion() < 0) {
+ throw new MalformedCookieException("Cookie version may not be negative");
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java
new file mode 100644
index 0000000..aa3a1c5
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * <tt>"CommantURL"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+ public class RFC2965CommentUrlAttributeHandler implements CookieAttributeHandler {
+
+ public RFC2965CommentUrlAttributeHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String commenturl)
+ throws MalformedCookieException {
+ if (cookie instanceof SetCookie2) {
+ 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;
+ }
+
+ } \ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java
new file mode 100644
index 0000000..aa81145
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java
@@ -0,0 +1,66 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * <tt>"Discard"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+ public class RFC2965DiscardAttributeHandler implements CookieAttributeHandler {
+
+ public RFC2965DiscardAttributeHandler() {
+ super();
+ }
+
+ public void parse(final SetCookie cookie, final String commenturl)
+ throws MalformedCookieException {
+ if (cookie instanceof SetCookie2) {
+ 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;
+ }
+
+ } \ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java
new file mode 100644
index 0000000..b07e5e9
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java
@@ -0,0 +1,195 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.Locale;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+
+/**
+ * <tt>"Domain"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ *
+ * @author jain.samit@gmail.com (Samit Jain)
+ *
+ * @since 3.1
+ */
+public class RFC2965DomainAttributeHandler implements CookieAttributeHandler {
+
+ public RFC2965DomainAttributeHandler() {
+ super();
+ }
+
+ /**
+ * Parse cookie domain attribute.
+ */
+ public void parse(final SetCookie cookie, String domain)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (domain == null) {
+ throw new MalformedCookieException(
+ "Missing value for domain attribute");
+ }
+ if (domain.trim().length() == 0) {
+ throw new MalformedCookieException(
+ "Blank value for domain attribute");
+ }
+ domain = domain.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
+ domain = '.' + domain;
+ }
+ cookie.setDomain(domain);
+ }
+
+ /**
+ * Performs domain-match as defined by the RFC2965.
+ * <p>
+ * Host A's name domain-matches host B's if
+ * <ol>
+ * <ul>their host name strings string-compare equal; or</ul>
+ * <ul>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.)</ul>
+ * </ol>
+ *
+ * @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(String host, String domain) {
+ 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 {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ String host = origin.getHost().toLowerCase(Locale.ENGLISH);
+ if (cookie.getDomain() == null) {
+ throw new MalformedCookieException("Invalid cookie state: " +
+ "domain not specified");
+ }
+ 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 MalformedCookieException("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.
+ int dotIndex = cookieDomain.indexOf('.', 1);
+ if (((dotIndex < 0) || (dotIndex == cookieDomain.length() - 1))
+ && (!cookieDomain.equals(".local"))) {
+ throw new MalformedCookieException(
+ "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 MalformedCookieException(
+ "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
+ String effectiveHostWithoutDomain = host.substring(
+ 0, host.length() - cookieDomain.length());
+ if (effectiveHostWithoutDomain.indexOf('.') != -1) {
+ throw new MalformedCookieException("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 MalformedCookieException("Illegal domain attribute: \""
+ + cookie.getDomain() + "\"."
+ + "Domain of origin: \""
+ + host + "\"");
+ }
+ }
+ }
+
+ /**
+ * Match cookie domain attribute.
+ */
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ String host = origin.getHost().toLowerCase(Locale.ENGLISH);
+ 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
+ String effectiveHostWithoutDomain = host.substring(
+ 0, host.length() - cookieDomain.length());
+ return effectiveHostWithoutDomain.indexOf('.') == -1;
+ }
+
+} \ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java
new file mode 100644
index 0000000..b881cda
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java
@@ -0,0 +1,168 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.StringTokenizer;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * <tt>"Port"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+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 {
+ StringTokenizer st = new StringTokenizer(portValue, ",");
+ 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 (NumberFormatException e) {
+ throw new MalformedCookieException ("Invalid Port "
+ + "attribute: " + e.getMessage());
+ }
+ return ports;
+ }
+
+ /**
+ * Returns <tt>true</tt> 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 <tt>true</tt> if the given port exists in
+ * the given ports list; <tt>false</tt> otherwise.
+ */
+ private static boolean portMatch(int port, int[] ports) {
+ boolean portInList = false;
+ for (int i = 0, len = ports.length; i < len; i++) {
+ if (port == ports[i]) {
+ portInList = true;
+ break;
+ }
+ }
+ return portInList;
+ }
+
+ /**
+ * Parse cookie port attribute.
+ */
+ public void parse(final SetCookie cookie, final String portValue)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (cookie instanceof SetCookie2) {
+ SetCookie2 cookie2 = (SetCookie2) cookie;
+ if (portValue != null && portValue.trim().length() > 0) {
+ 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 {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ int port = origin.getPort();
+ if (cookie instanceof ClientCookie
+ && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
+ if (!portMatch(port, cookie.getPorts())) {
+ throw new MalformedCookieException(
+ "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) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ 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/src/org/apache/http/impl/cookie/RFC2965Spec.java b/src/org/apache/http/impl/cookie/RFC2965Spec.java
new file mode 100644
index 0000000..9422fdf
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965Spec.java
@@ -0,0 +1,259 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965Spec.java $
+ * $Revision: 653041 $
+ * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
+ *
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SM;
+import org.apache.http.message.BufferedHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * <p>RFC 2965 specific cookie management functions.</p>
+ *
+ * @author jain.samit@gmail.com (Samit Jain)
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 3.1
+ */
+public class RFC2965Spec extends RFC2109Spec {
+
+ /**
+ * Default constructor
+ *
+ */
+ public RFC2965Spec() {
+ this(null, false);
+ }
+
+ public RFC2965Spec(final String[] datepatterns, 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());
+ }
+
+ private BasicClientCookie createCookie(
+ final String name, final String value, final CookieOrigin origin) {
+ BasicClientCookie cookie = new BasicClientCookie(name, value);
+ cookie.setPath(getDefaultPath(origin));
+ cookie.setDomain(getDefaultDomain(origin));
+ return cookie;
+ }
+
+ private BasicClientCookie createCookie2(
+ final String name, final String value, final CookieOrigin origin) {
+ BasicClientCookie2 cookie = new BasicClientCookie2(name, value);
+ cookie.setPath(getDefaultPath(origin));
+ cookie.setDomain(getDefaultDomain(origin));
+ cookie.setPorts(new int [] { origin.getPort() });
+ return cookie;
+ }
+
+ @Override
+ public List<Cookie> parse(
+ final Header header,
+ CookieOrigin origin) throws MalformedCookieException {
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+
+ origin = adjustEffectiveHost(origin);
+
+ HeaderElement[] elems = header.getElements();
+
+ List<Cookie> cookies = new ArrayList<Cookie>(elems.length);
+ for (HeaderElement headerelement : elems) {
+ String name = headerelement.getName();
+ String value = headerelement.getValue();
+ if (name == null || name.length() == 0) {
+ throw new MalformedCookieException("Cookie name may not be empty");
+ }
+
+ BasicClientCookie cookie;
+ if (header.getName().equals(SM.SET_COOKIE2)) {
+ cookie = createCookie2(name, value, origin);
+ } else {
+ cookie = createCookie(name, value, origin);
+ }
+
+ // cycle through the parameters
+ NameValuePair[] attribs = headerelement.getParameters();
+
+ // Eliminate duplicate attributes. The first occurrence takes precedence
+ // See RFC2965: 3.2 Origin Server Role
+ Map<String, NameValuePair> attribmap =
+ new HashMap<String, NameValuePair>(attribs.length);
+ for (int j = attribs.length - 1; j >= 0; j--) {
+ NameValuePair param = attribs[j];
+ attribmap.put(param.getName().toLowerCase(Locale.ENGLISH), param);
+ }
+ for (Map.Entry<String, NameValuePair> entry : attribmap.entrySet()) {
+ NameValuePair attrib = entry.getValue();
+ String s = attrib.getName().toLowerCase(Locale.ENGLISH);
+
+ cookie.setAttribute(s, attrib.getValue());
+
+ CookieAttributeHandler handler = findAttribHandler(s);
+ if (handler != null) {
+ handler.parse(cookie, attrib.getValue());
+ }
+ }
+ cookies.add(cookie);
+ }
+ return cookies;
+ }
+
+ @Override
+ public void validate(final Cookie cookie, CookieOrigin origin)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ origin = adjustEffectiveHost(origin);
+ super.validate(cookie, origin);
+ }
+
+ @Override
+ public boolean match(final Cookie cookie, CookieOrigin origin) {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (origin == null) {
+ throw new IllegalArgumentException("Cookie origin may not be null");
+ }
+ origin = adjustEffectiveHost(origin);
+ return super.match(cookie, origin);
+ }
+
+ /**
+ * Adds valid Port attribute value, e.g. "8000,8001,8002"
+ */
+ @Override
+ protected void formatCookieAsVer(final CharArrayBuffer buffer,
+ final Cookie cookie, 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
+ String s = ((ClientCookie) cookie).getAttribute(ClientCookie.PORT_ATTR);
+ if (s != null) {
+ buffer.append("; $Port");
+ buffer.append("=\"");
+ if (s.trim().length() > 0) {
+ int[] ports = cookie.getPorts();
+ if (ports != null) {
+ for (int i = 0, len = ports.length; 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.
+ * <p>
+ * 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.
+ * @return
+ */
+ 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++) {
+ 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() {
+ CharArrayBuffer buffer = new CharArrayBuffer(40);
+ buffer.append(SM.COOKIE2);
+ buffer.append(": ");
+ buffer.append("$Version=");
+ buffer.append(Integer.toString(getVersion()));
+ return new BufferedHeader(buffer);
+ }
+
+}
+
diff --git a/src/org/apache/http/impl/cookie/RFC2965SpecFactory.java b/src/org/apache/http/impl/cookie/RFC2965SpecFactory.java
new file mode 100644
index 0000000..4b3cc4d
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965SpecFactory.java
@@ -0,0 +1,57 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965SpecFactory.java $
+ * $Revision: 581953 $
+ * $Date: 2007-10-04 08:53:50 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.CookieSpec;
+import org.apache.http.cookie.CookieSpecFactory;
+import org.apache.http.cookie.params.CookieSpecPNames;
+import org.apache.http.params.HttpParams;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class RFC2965SpecFactory implements CookieSpecFactory {
+
+ public CookieSpec newInstance(final HttpParams params) {
+ if (params != null) {
+ return new RFC2965Spec(
+ (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS),
+ params.getBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, false));
+ } else {
+ return new RFC2965Spec();
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java b/src/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java
new file mode 100644
index 0000000..8ea8481
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java
@@ -0,0 +1,96 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java $
+ * $Revision: 590695 $
+ * $Date: 2007-10-31 07:55:41 -0700 (Wed, 31 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.cookie;
+
+import org.apache.http.cookie.ClientCookie;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.CookieAttributeHandler;
+import org.apache.http.cookie.CookieOrigin;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.cookie.SetCookie;
+import org.apache.http.cookie.SetCookie2;
+
+/**
+ * <tt>"Version"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+public class RFC2965VersionAttributeHandler implements CookieAttributeHandler {
+
+ public RFC2965VersionAttributeHandler() {
+ super();
+ }
+
+ /**
+ * Parse cookie version attribute.
+ */
+ public void parse(final SetCookie cookie, final String value)
+ throws MalformedCookieException {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (value == null) {
+ throw new MalformedCookieException(
+ "Missing value for version attribute");
+ }
+ int version = -1;
+ try {
+ version = Integer.parseInt(value);
+ } catch (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 {
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (cookie instanceof SetCookie2) {
+ if (cookie instanceof ClientCookie
+ && !((ClientCookie) cookie).containsAttribute(ClientCookie.VERSION_ATTR)) {
+ throw new MalformedCookieException(
+ "Violates RFC 2965. Version attribute is required.");
+ }
+ }
+ }
+
+ public boolean match(final Cookie cookie, final CookieOrigin origin) {
+ return true;
+ }
+
+} \ No newline at end of file
diff --git a/src/org/apache/http/impl/cookie/package.html b/src/org/apache/http/impl/cookie/package.html
new file mode 100644
index 0000000..e301283
--- /dev/null
+++ b/src/org/apache/http/impl/cookie/package.html
@@ -0,0 +1,4 @@
+<body>
+
+</body>
+
diff --git a/src/org/apache/http/impl/entity/EntityDeserializer.java b/src/org/apache/http/impl/entity/EntityDeserializer.java
new file mode 100644
index 0000000..12c4756
--- /dev/null
+++ b/src/org/apache/http/impl/entity/EntityDeserializer.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/EntityDeserializer.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import java.io.IOException;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.io.ChunkedInputStream;
+import org.apache.http.impl.io.ContentLengthInputStream;
+import org.apache.http.impl.io.IdentityInputStream;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Default implementation of an entity deserializer.
+ * <p>
+ * This entity deserializer currently supports only "chunked" and "identitiy" transfer-coding</a>
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ *
+ * @since 4.0
+ */
+public class EntityDeserializer {
+
+ private final ContentLengthStrategy lenStrategy;
+
+ public EntityDeserializer(final ContentLengthStrategy lenStrategy) {
+ super();
+ if (lenStrategy == null) {
+ throw new IllegalArgumentException("Content length strategy may not be null");
+ }
+ this.lenStrategy = lenStrategy;
+ }
+
+ protected BasicHttpEntity doDeserialize(
+ final SessionInputBuffer inbuffer,
+ final HttpMessage message) throws HttpException, IOException {
+ BasicHttpEntity entity = new BasicHttpEntity();
+
+ 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));
+ }
+
+ Header contentTypeHeader = message.getFirstHeader(HTTP.CONTENT_TYPE);
+ if (contentTypeHeader != null) {
+ entity.setContentType(contentTypeHeader);
+ }
+ Header contentEncodingHeader = message.getFirstHeader(HTTP.CONTENT_ENCODING);
+ if (contentEncodingHeader != null) {
+ entity.setContentEncoding(contentEncodingHeader);
+ }
+ return entity;
+ }
+
+ public HttpEntity deserialize(
+ final SessionInputBuffer inbuffer,
+ final HttpMessage message) throws HttpException, IOException {
+ if (inbuffer == null) {
+ throw new IllegalArgumentException("Session input buffer may not be null");
+ }
+ if (message == null) {
+ throw new IllegalArgumentException("HTTP message may not be null");
+ }
+ return doDeserialize(inbuffer, message);
+ }
+
+}
diff --git a/src/org/apache/http/impl/entity/EntitySerializer.java b/src/org/apache/http/impl/entity/EntitySerializer.java
new file mode 100644
index 0000000..3221980
--- /dev/null
+++ b/src/org/apache/http/impl/entity/EntitySerializer.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/EntitySerializer.java $
+ * $Revision: 560343 $
+ * $Date: 2007-07-27 11:18:19 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.io.ChunkedOutputStream;
+import org.apache.http.impl.io.ContentLengthOutputStream;
+import org.apache.http.impl.io.IdentityOutputStream;
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * Default implementation of an entity serializer.
+ * <p>
+ * This entity serializer currently supports only "chunked" and "identitiy" transfer-coding</a>
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560343 $
+ *
+ * @since 4.0
+ */
+public class EntitySerializer {
+
+ private final ContentLengthStrategy lenStrategy;
+
+ public EntitySerializer(final ContentLengthStrategy lenStrategy) {
+ super();
+ if (lenStrategy == null) {
+ throw new IllegalArgumentException("Content length strategy may not be null");
+ }
+ this.lenStrategy = lenStrategy;
+ }
+
+ protected OutputStream doSerialize(
+ final SessionOutputBuffer outbuffer,
+ final HttpMessage message) throws HttpException, IOException {
+ 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);
+ }
+ }
+
+ public void serialize(
+ final SessionOutputBuffer outbuffer,
+ final HttpMessage message,
+ final HttpEntity entity) throws HttpException, IOException {
+ if (outbuffer == null) {
+ throw new IllegalArgumentException("Session output buffer may not be null");
+ }
+ if (message == null) {
+ throw new IllegalArgumentException("HTTP message may not be null");
+ }
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+ OutputStream outstream = doSerialize(outbuffer, message);
+ entity.writeTo(outstream);
+ outstream.close();
+ }
+
+}
diff --git a/src/org/apache/http/impl/entity/LaxContentLengthStrategy.java b/src/org/apache/http/impl/entity/LaxContentLengthStrategy.java
new file mode 100644
index 0000000..9a0d238
--- /dev/null
+++ b/src/org/apache/http/impl/entity/LaxContentLengthStrategy.java
@@ -0,0 +1,260 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/LaxContentLengthStrategy.java $
+ * $Revision: 576073 $
+ * $Date: 2007-09-16 03:53:13 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * The lax implementation of the content length strategy.
+ * <p>
+ * This strategy conforms to the entity transfer rules outlined in
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec4.4">Section 4.4</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41">Section 14.41</a>
+ * and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec14.13">Section 14.13</a>
+ * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>, but is lenient
+ * about unsupported transfer codecs and malformed content-length headers.
+ * </p>
+ * <h>4.4 Message Length</h>
+ * <p>
+ * The transfer-length of a message is the length of the message-body as it appears in the
+ * message; that is, after any transfer-codings have been applied. When a message-body is
+ * included with a message, the transfer-length of that body is determined by one of the
+ * following (in order of precedence):
+ * </p>
+ * <p>
+ * 1.Any response message which "MUST NOT" include a message-body (such as the 1xx, 204,
+ * and 304 responses and any response to a HEAD request) is always terminated by the first
+ * empty line after the header fields, regardless of the entity-header fields present in the
+ * message.
+ * </p>
+ * <p>
+ * 2.If a Transfer-Encoding header field (section 14.41) is present and has any value other
+ * than "identity", then the transfer-length is defined by use of the "chunked" transfer-
+ * coding (section 3.6), unless the message is terminated by closing the connection.
+ * </p>
+ * <p>
+ * 3.If a Content-Length header field (section 14.13) is present, its decimal value in
+ * OCTETs represents both the entity-length and the transfer-length. The Content-Length
+ * header field MUST NOT be sent if these two lengths are different (i.e., if a
+ * Transfer-Encoding
+ * </p>
+ * <pre>
+ * header field is present). If a message is received with both a
+ * Transfer-Encoding header field and a Content-Length header field,
+ * the latter MUST be ignored.
+ * </pre>
+ * <p>
+ * 4.If the message uses the media type "multipart/byteranges", and the ransfer-length is not
+ * otherwise specified, then this self- elimiting media type defines the transfer-length.
+ * This media type UST NOT be used unless the sender knows that the recipient can arse it; the
+ * presence in a request of a Range header with ultiple byte- range specifiers from a 1.1
+ * client implies that the lient can parse multipart/byteranges responses.
+ * </p>
+ * <pre>
+ * A range header might be forwarded by a 1.0 proxy that does not
+ * understand multipart/byteranges; in this case the server MUST
+ * delimit the message using methods defined in items 1,3 or 5 of
+ * this section.
+ * </pre>
+ * <p>
+ * 5.By the server closing the connection. (Closing the connection cannot be used to indicate
+ * the end of a request body, since that would leave no possibility for the server to send back
+ * a response.)
+ * </p>
+ * <p>
+ * For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing a message-body
+ * MUST include a valid Content-Length header field unless the server is known to be HTTP/1.1
+ * compliant. If a request contains a message-body and a Content-Length is not given, the
+ * server SHOULD respond with 400 (bad request) if it cannot determine the length of the
+ * message, or with 411 (length required) if it wishes to insist on receiving a valid
+ * Content-Length.
+ * </p>
+ * <p>All HTTP/1.1 applications that receive entities MUST accept the "chunked" transfer-coding
+ * (section 3.6), thus allowing this mechanism to be used for messages when the message
+ * length cannot be determined in advance.
+ * </p>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ * Transfer-coding values are used to indicate an encoding transformation that
+ * has been, can be, or may need to be applied to an entity-body in order to ensure
+ * "safe transport" through the network. This differs from a content coding in that
+ * the transfer-coding is a property of the message, not of the original entity.
+ * </p>
+ * <pre>
+ * transfer-coding = "chunked" | transfer-extension
+ * transfer-extension = token *( ";" parameter )
+ * </pre>
+ * <p>
+ * Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter = attribute "=" value
+ * attribute = token
+ * value = token | quoted-string
+ * </pre>
+ * <p>
+ * All transfer-coding values are case-insensitive. HTTP/1.1 uses transfer-coding values in
+ * the TE header field (section 14.39) and in the Transfer-Encoding header field (section 14.41).
+ * </p>
+ * <p>
+ * Whenever a transfer-coding is applied to a message-body, the set of transfer-codings MUST
+ * include "chunked", unless the message is terminated by closing the connection. When the
+ * "chunked" transfer-coding is used, it MUST be the last transfer-coding applied to the
+ * message-body. The "chunked" transfer-coding MUST NOT be applied more than once to a
+ * message-body. These rules allow the recipient to determine the transfer-length of the
+ * message (section 4.4).
+ * </p>
+ * <h>14.41 Transfer-Encoding</h>
+ * <p>
+ * The Transfer-Encoding general-header field indicates what (if any) type of transformation has
+ * been applied to the message body in order to safely transfer it between the sender and the
+ * recipient. This differs from the content-coding in that the transfer-coding is a property of
+ * the message, not of the entity.
+ * </p>
+ * <pre>
+ * Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
+ * </pre>
+ * <p>
+ * If multiple encodings have been applied to an entity, the transfer- codings MUST be listed in
+ * the order in which they were applied. Additional information about the encoding parameters
+ * MAY be provided by other entity-header fields not defined by this specification.
+ * </p>
+ * <h>14.13 Content-Length</h>
+ * <p>
+ * The Content-Length entity-header field indicates the size of the entity-body, in decimal
+ * number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of
+ * the entity-body that would have been sent had the request been a GET.
+ * </p>
+ * <pre>
+ * Content-Length = "Content-Length" ":" 1*DIGIT
+ * </pre>
+ * <p>
+ * Applications SHOULD use this field to indicate the transfer-length of the message-body,
+ * unless this is prohibited by the rules in section 4.4.
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576073 $
+ *
+ * @since 4.0
+ */
+public class LaxContentLengthStrategy implements ContentLengthStrategy {
+
+ public LaxContentLengthStrategy() {
+ super();
+ }
+
+ public long determineLength(final HttpMessage message) throws HttpException {
+ if (message == null) {
+ throw new IllegalArgumentException("HTTP message may not be null");
+ }
+
+ HttpParams params = message.getParams();
+ boolean strict = params.isParameterTrue(CoreProtocolPNames.STRICT_TRANSFER_ENCODING);
+
+ Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING);
+ Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN);
+ // We use Transfer-Encoding if present and ignore Content-Length.
+ // RFC2616, 4.4 item number 3
+ if (transferEncodingHeader != null) {
+ HeaderElement[] encodings = null;
+ try {
+ encodings = transferEncodingHeader.getElements();
+ } catch (ParseException px) {
+ throw new ProtocolException
+ ("Invalid Transfer-Encoding header value: " +
+ transferEncodingHeader, px);
+ }
+ if (strict) {
+ // Currently only chunk and identity are supported
+ for (int i = 0; i < encodings.length; i++) {
+ String encoding = encodings[i].getName();
+ if (encoding != null && encoding.length() > 0
+ && !encoding.equalsIgnoreCase(HTTP.CHUNK_CODING)
+ && !encoding.equalsIgnoreCase(HTTP.IDENTITY_CODING)) {
+ throw new ProtocolException("Unsupported transfer encoding: " + encoding);
+ }
+ }
+ }
+ // The chunked encoding must be the last one applied RFC2616, 14.41
+ 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 {
+ if (strict) {
+ throw new ProtocolException("Chunk-encoding must be the last one applied");
+ }
+ return IDENTITY;
+ }
+ } else if (contentLengthHeader != null) {
+ long contentlen = -1;
+ Header[] headers = message.getHeaders(HTTP.CONTENT_LEN);
+ if (strict && headers.length > 1) {
+ throw new ProtocolException("Multiple content length headers");
+ }
+ for (int i = headers.length - 1; i >= 0; i--) {
+ Header header = headers[i];
+ try {
+ contentlen = Long.parseLong(header.getValue());
+ break;
+ } catch (NumberFormatException e) {
+ if (strict) {
+ throw new ProtocolException("Invalid content length: " + header.getValue());
+ }
+ }
+ // See if we can have better luck with another header, if present
+ }
+ if (contentlen >= 0) {
+ return contentlen;
+ } else {
+ return IDENTITY;
+ }
+ } else {
+ return IDENTITY;
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/entity/StrictContentLengthStrategy.java b/src/org/apache/http/impl/entity/StrictContentLengthStrategy.java
new file mode 100644
index 0000000..30be8e2
--- /dev/null
+++ b/src/org/apache/http/impl/entity/StrictContentLengthStrategy.java
@@ -0,0 +1,220 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/StrictContentLengthStrategy.java $
+ * $Revision: 573949 $
+ * $Date: 2007-09-08 22:46:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.entity;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolException;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * The strict implementation of the content length strategy.
+ * <p>
+ * This entity generator comforms to the entity transfer rules outlined in the
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec4.4">Section 4.4</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41">Section 14.41</a>
+ * and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec14.13">Section 14.13</a>
+ * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
+ * </p>
+ * <h>4.4 Message Length</h>
+ * <p>
+ * The transfer-length of a message is the length of the message-body as it appears in the
+ * message; that is, after any transfer-codings have been applied. When a message-body is
+ * included with a message, the transfer-length of that body is determined by one of the
+ * following (in order of precedence):
+ * </p>
+ * <p>
+ * 1.Any response message which "MUST NOT" include a message-body (such as the 1xx, 204,
+ * and 304 responses and any response to a HEAD request) is always terminated by the first
+ * empty line after the header fields, regardless of the entity-header fields present in the
+ * message.
+ * </p>
+ * <p>
+ * 2.If a Transfer-Encoding header field (section 14.41) is present and has any value other
+ * than "identity", then the transfer-length is defined by use of the "chunked" transfer-
+ * coding (section 3.6), unless the message is terminated by closing the connection.
+ * </p>
+ * <p>
+ * 3.If a Content-Length header field (section 14.13) is present, its decimal value in
+ * OCTETs represents both the entity-length and the transfer-length. The Content-Length
+ * header field MUST NOT be sent if these two lengths are different (i.e., if a
+ * Transfer-Encoding
+ * </p>
+ * <pre>
+ * header field is present). If a message is received with both a
+ * Transfer-Encoding header field and a Content-Length header field,
+ * the latter MUST be ignored.
+ * </pre>
+ * <p>
+ * 4.If the message uses the media type "multipart/byteranges", and the ransfer-length is not
+ * otherwise specified, then this self- elimiting media type defines the transfer-length.
+ * This media type UST NOT be used unless the sender knows that the recipient can arse it; the
+ * presence in a request of a Range header with ultiple byte- range specifiers from a 1.1
+ * client implies that the lient can parse multipart/byteranges responses.
+ * </p>
+ * <pre>
+ * A range header might be forwarded by a 1.0 proxy that does not
+ * understand multipart/byteranges; in this case the server MUST
+ * delimit the message using methods defined in items 1,3 or 5 of
+ * this section.
+ * </pre>
+ * <p>
+ * 5.By the server closing the connection. (Closing the connection cannot be used to indicate
+ * the end of a request body, since that would leave no possibility for the server to send back
+ * a response.)
+ * </p>
+ * <p>
+ * For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing a message-body
+ * MUST include a valid Content-Length header field unless the server is known to be HTTP/1.1
+ * compliant. If a request contains a message-body and a Content-Length is not given, the
+ * server SHOULD respond with 400 (bad request) if it cannot determine the length of the
+ * message, or with 411 (length required) if it wishes to insist on receiving a valid
+ * Content-Length.
+ * </p>
+ * <p>All HTTP/1.1 applications that receive entities MUST accept the "chunked" transfer-coding
+ * (section 3.6), thus allowing this mechanism to be used for messages when the message
+ * length cannot be determined in advance.
+ * </p>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ * Transfer-coding values are used to indicate an encoding transformation that
+ * has been, can be, or may need to be applied to an entity-body in order to ensure
+ * "safe transport" through the network. This differs from a content coding in that
+ * the transfer-coding is a property of the message, not of the original entity.
+ * </p>
+ * <pre>
+ * transfer-coding = "chunked" | transfer-extension
+ * transfer-extension = token *( ";" parameter )
+ * </pre>
+ * <p>
+ * Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter = attribute "=" value
+ * attribute = token
+ * value = token | quoted-string
+ * </pre>
+ * <p>
+ * All transfer-coding values are case-insensitive. HTTP/1.1 uses transfer-coding values in
+ * the TE header field (section 14.39) and in the Transfer-Encoding header field (section 14.41).
+ * </p>
+ * <p>
+ * Whenever a transfer-coding is applied to a message-body, the set of transfer-codings MUST
+ * include "chunked", unless the message is terminated by closing the connection. When the
+ * "chunked" transfer-coding is used, it MUST be the last transfer-coding applied to the
+ * message-body. The "chunked" transfer-coding MUST NOT be applied more than once to a
+ * message-body. These rules allow the recipient to determine the transfer-length of the
+ * message (section 4.4).
+ * </p>
+ * <h>14.41 Transfer-Encoding</h>
+ * <p>
+ * The Transfer-Encoding general-header field indicates what (if any) type of transformation has
+ * been applied to the message body in order to safely transfer it between the sender and the
+ * recipient. This differs from the content-coding in that the transfer-coding is a property of
+ * the message, not of the entity.
+ * </p>
+ * <pre>
+ * Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
+ * </pre>
+ * <p>
+ * If multiple encodings have been applied to an entity, the transfer- codings MUST be listed in
+ * the order in which they were applied. Additional information about the encoding parameters
+ * MAY be provided by other entity-header fields not defined by this specification.
+ * </p>
+ * <h>14.13 Content-Length</h>
+ * <p>
+ * The Content-Length entity-header field indicates the size of the entity-body, in decimal
+ * number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of
+ * the entity-body that would have been sent had the request been a GET.
+ * </p>
+ * <pre>
+ * Content-Length = "Content-Length" ":" 1*DIGIT
+ * </pre>
+ * <p>
+ * Applications SHOULD use this field to indicate the transfer-length of the message-body,
+ * unless this is prohibited by the rules in section 4.4.
+ * </p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573949 $
+ *
+ * @since 4.0
+ */
+public class StrictContentLengthStrategy implements ContentLengthStrategy {
+
+ public StrictContentLengthStrategy() {
+ super();
+ }
+
+ public long determineLength(final HttpMessage message) throws HttpException {
+ if (message == null) {
+ throw new IllegalArgumentException("HTTP message may not be null");
+ }
+ // 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.
+ Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING);
+ Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN);
+ if (transferEncodingHeader != null) {
+ 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);
+ }
+ } else if (contentLengthHeader != null) {
+ String s = contentLengthHeader.getValue();
+ try {
+ long len = Long.parseLong(s);
+ return len;
+ } catch (NumberFormatException e) {
+ throw new ProtocolException("Invalid content length: " + s);
+ }
+ } else {
+ return IDENTITY;
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/entity/package.html b/src/org/apache/http/impl/entity/package.html
new file mode 100644
index 0000000..9ac4481
--- /dev/null
+++ b/src/org/apache/http/impl/entity/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/entity/package.html $
+ * $Revision: 496072 $
+ * $Date: 2007-01-14 04:22:55 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Default implementations for interfaces in
+{@link org.apache.http.entity org.apache.http.entity}.
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/io/AbstractMessageParser.java b/src/org/apache/http/impl/io/AbstractMessageParser.java
new file mode 100644
index 0000000..679bcd1
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractMessageParser.java
@@ -0,0 +1,187 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractMessageParser.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Message parser base class.
+ *
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ */
+public abstract class AbstractMessageParser implements HttpMessageParser {
+
+ private final SessionInputBuffer sessionBuffer;
+ private final int maxHeaderCount;
+ private final int maxLineLen;
+ protected final LineParser lineParser;
+
+
+ public AbstractMessageParser(
+ final SessionInputBuffer buffer,
+ final LineParser parser,
+ final HttpParams params) {
+ super();
+ if (buffer == null) {
+ throw new IllegalArgumentException("Session input buffer may not be null");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.sessionBuffer = buffer;
+ this.maxHeaderCount = params.getIntParameter(
+ CoreConnectionPNames.MAX_HEADER_COUNT, -1);
+ this.maxLineLen = params.getIntParameter(
+ CoreConnectionPNames.MAX_LINE_LENGTH, -1);
+ this.lineParser = (parser != null) ? parser : BasicLineParser.DEFAULT;
+ }
+
+ /**
+ * 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
+ * @return array of HTTP headers
+ *
+ * @throws HttpException
+ * @throws IOException
+ */
+ public static Header[] parseHeaders(
+ final SessionInputBuffer inbuffer,
+ int maxHeaderCount,
+ int maxLineLen,
+ LineParser parser)
+ throws HttpException, IOException {
+
+ if (inbuffer == null) {
+ throw new IllegalArgumentException("Session input buffer may not be null");
+ }
+ if (parser == null)
+ parser = BasicLineParser.DEFAULT;
+
+ ArrayList headerLines = new ArrayList();
+
+ CharArrayBuffer current = null;
+ CharArrayBuffer previous = null;
+ for (;;) {
+ if (current == null) {
+ current = new CharArrayBuffer(64);
+ } else {
+ current.clear();
+ }
+ 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()) {
+ char ch = current.charAt(i);
+ if (ch != ' ' && ch != '\t') {
+ break;
+ }
+ i++;
+ }
+ if (maxLineLen > 0
+ && previous.length() + 1 + current.length() - i > maxLineLen) {
+ throw new IOException("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 IOException("Maximum header count exceeded");
+ }
+ }
+ Header[] headers = new Header[headerLines.size()];
+ for (int i = 0; i < headerLines.size(); i++) {
+ CharArrayBuffer buffer = (CharArrayBuffer) headerLines.get(i);
+ try {
+ headers[i] = parser.parseHeader(buffer);
+ } catch (ParseException ex) {
+ throw new ProtocolException(ex.getMessage());
+ }
+ }
+ return headers;
+ }
+
+ protected abstract HttpMessage parseHead(SessionInputBuffer sessionBuffer)
+ throws IOException, HttpException, ParseException;
+
+ public HttpMessage parse() throws IOException, HttpException {
+ HttpMessage message = null;
+ try {
+ message = parseHead(this.sessionBuffer);
+ } catch (ParseException px) {
+ throw new ProtocolException(px.getMessage(), px);
+ }
+ Header[] headers = AbstractMessageParser.parseHeaders(
+ this.sessionBuffer,
+ this.maxHeaderCount,
+ this.maxLineLen,
+ this.lineParser);
+ message.setHeaders(headers);
+ return message;
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/AbstractMessageWriter.java b/src/org/apache/http/impl/io/AbstractMessageWriter.java
new file mode 100644
index 0000000..f9644ce
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractMessageWriter.java
@@ -0,0 +1,85 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractMessageWriter.java $
+ * $Revision: 569673 $
+ * $Date: 2007-08-25 06:58:51 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.io.HttpMessageWriter;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.message.LineFormatter;
+import org.apache.http.message.BasicLineFormatter;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public abstract class AbstractMessageWriter implements HttpMessageWriter {
+
+ protected final SessionOutputBuffer sessionBuffer;
+ protected final CharArrayBuffer lineBuf;
+ protected final LineFormatter lineFormatter;
+
+ public AbstractMessageWriter(final SessionOutputBuffer buffer,
+ final LineFormatter formatter,
+ final HttpParams params) {
+ super();
+ if (buffer == null) {
+ throw new IllegalArgumentException("Session input buffer may not be null");
+ }
+ this.sessionBuffer = buffer;
+ this.lineBuf = new CharArrayBuffer(128);
+ this.lineFormatter = (formatter != null) ?
+ formatter : BasicLineFormatter.DEFAULT;
+ }
+
+ protected abstract void writeHeadLine(HttpMessage message)
+ throws IOException
+ ;
+
+ public void write(
+ final HttpMessage message) throws IOException, HttpException {
+ if (message == null) {
+ throw new IllegalArgumentException("HTTP message may not be null");
+ }
+ writeHeadLine(message);
+ for (Iterator it = message.headerIterator(); it.hasNext(); ) {
+ Header header = (Header) it.next();
+ this.sessionBuffer.writeLine
+ (lineFormatter.formatHeader(this.lineBuf, header));
+ }
+ this.lineBuf.clear();
+ this.sessionBuffer.writeLine(this.lineBuf);
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/AbstractSessionInputBuffer.java b/src/org/apache/http/impl/io/AbstractSessionInputBuffer.java
new file mode 100644
index 0000000..eb007a9
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractSessionInputBuffer.java
@@ -0,0 +1,271 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionInputBuffer.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.ByteArrayBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Abstract base class for session input buffers that stream data
+ * from a {@link InputStream}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ */
+public abstract class AbstractSessionInputBuffer implements SessionInputBuffer {
+
+ private InputStream instream;
+ private byte[] buffer;
+ private int bufferpos;
+ private int bufferlen;
+
+ private ByteArrayBuffer linebuffer = null;
+
+ private String charset = HTTP.US_ASCII;
+ private boolean ascii = true;
+ private int maxLineLen = -1;
+
+ private HttpTransportMetricsImpl metrics;
+
+ protected void init(final InputStream instream, int buffersize, final HttpParams params) {
+ if (instream == null) {
+ throw new IllegalArgumentException("Input stream may not be null");
+ }
+ if (buffersize <= 0) {
+ throw new IllegalArgumentException("Buffer size may not be negative or zero");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.instream = instream;
+ this.buffer = new byte[buffersize];
+ this.bufferpos = 0;
+ this.bufferlen = 0;
+ this.linebuffer = new ByteArrayBuffer(buffersize);
+ this.charset = HttpProtocolParams.getHttpElementCharset(params);
+ this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII)
+ || this.charset.equalsIgnoreCase(HTTP.ASCII);
+ this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1);
+ this.metrics = new HttpTransportMetricsImpl();
+ }
+
+ protected int fillBuffer() throws IOException {
+ // compact the buffer if necessary
+ if (this.bufferpos > 0) {
+ 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;
+ }
+ int l;
+ int off = this.bufferlen;
+ 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 = 0;
+ while (!hasBufferedData()) {
+ noRead = fillBuffer();
+ if (noRead == -1) {
+ return -1;
+ }
+ }
+ return this.buffer[this.bufferpos++] & 0xff;
+ }
+
+ public int read(final byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ return 0;
+ }
+ int noRead = 0;
+ while (!hasBufferedData()) {
+ noRead = fillBuffer();
+ if (noRead == -1) {
+ return -1;
+ }
+ }
+ int chunk = this.bufferlen - this.bufferpos;
+ if (chunk > len) {
+ chunk = len;
+ }
+ 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;
+ }
+
+ public int readLine(final CharArrayBuffer charbuffer) throws IOException {
+ if (charbuffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ this.linebuffer.clear();
+ int noRead = 0;
+ boolean retry = true;
+ while (retry) {
+ // attempt to find end of line (LF)
+ 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;
+ 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()) {
+ 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);
+ }
+
+ private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
+ throws IOException {
+ // discard LF if found
+ int l = this.linebuffer.length();
+ if (l > 0) {
+ if (this.linebuffer.byteAt(l - 1) == HTTP.LF) {
+ l--;
+ this.linebuffer.setLength(l);
+ }
+ // discard CR if found
+ if (l > 0) {
+ if (this.linebuffer.byteAt(l - 1) == HTTP.CR) {
+ l--;
+ this.linebuffer.setLength(l);
+ }
+ }
+ }
+ l = this.linebuffer.length();
+ if (this.ascii) {
+ charbuffer.append(this.linebuffer, 0, l);
+ } else {
+ // This is VERY memory inefficient, BUT since non-ASCII charsets are
+ // NOT meant to be used anyway, there's no point optimizing it
+ String s = new String(this.linebuffer.buffer(), 0, l, this.charset);
+ charbuffer.append(s);
+ }
+ return l;
+ }
+
+ private int lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos)
+ throws IOException {
+ int off = this.bufferpos;
+ int len;
+ this.bufferpos = pos + 1;
+ if (pos > 0 && this.buffer[pos - 1] == HTTP.CR) {
+ // skip CR if found
+ pos--;
+ }
+ len = pos - off;
+ if (this.ascii) {
+ charbuffer.append(this.buffer, off, len);
+ } else {
+ // This is VERY memory inefficient, BUT since non-ASCII charsets are
+ // NOT meant to be used anyway, there's no point optimizing it
+ String s = new String(this.buffer, off, len, this.charset);
+ charbuffer.append(s);
+ }
+ return len;
+ }
+
+ public String readLine() throws IOException {
+ CharArrayBuffer charbuffer = new CharArrayBuffer(64);
+ int l = readLine(charbuffer);
+ if (l != -1) {
+ return charbuffer.toString();
+ } else {
+ return null;
+ }
+ }
+
+ public HttpTransportMetrics getMetrics() {
+ return this.metrics;
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/AbstractSessionOutputBuffer.java b/src/org/apache/http/impl/io/AbstractSessionOutputBuffer.java
new file mode 100644
index 0000000..bf4e56e
--- /dev/null
+++ b/src/org/apache/http/impl/io/AbstractSessionOutputBuffer.java
@@ -0,0 +1,179 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionOutputBuffer.java $
+ * $Revision: 652091 $
+ * $Date: 2008-04-29 13:41:07 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.ByteArrayBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Abstract base class for session output buffers that stream data
+ * to an {@link OutputStream}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ */
+public abstract class AbstractSessionOutputBuffer implements SessionOutputBuffer {
+
+ private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
+
+ private static final int MAX_CHUNK = 256;
+
+ private OutputStream outstream;
+ private ByteArrayBuffer buffer;
+
+ private String charset = HTTP.US_ASCII;
+ private boolean ascii = true;
+
+ private HttpTransportMetricsImpl metrics;
+
+ protected void init(final OutputStream outstream, int buffersize, final HttpParams params) {
+ if (outstream == null) {
+ throw new IllegalArgumentException("Input stream may not be null");
+ }
+ if (buffersize <= 0) {
+ throw new IllegalArgumentException("Buffer size may not be negative or zero");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.outstream = outstream;
+ this.buffer = new ByteArrayBuffer(buffersize);
+ this.charset = HttpProtocolParams.getHttpElementCharset(params);
+ this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII)
+ || this.charset.equalsIgnoreCase(HTTP.ASCII);
+ this.metrics = new HttpTransportMetricsImpl();
+ }
+
+ protected void flushBuffer() throws IOException {
+ 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, int off, int len) throws IOException {
+ if (b == null) {
+ return;
+ }
+ // Do not want to buffer largish chunks
+ // if the byte array is larger then MAX_CHUNK
+ // write it directly to the output stream
+ if (len > MAX_CHUNK || 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
+ 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(int b) throws IOException {
+ if (this.buffer.isFull()) {
+ flushBuffer();
+ }
+ this.buffer.append(b);
+ }
+
+ public void writeLine(final String s) throws IOException {
+ if (s == null) {
+ return;
+ }
+ if (s.length() > 0) {
+ write(s.getBytes(this.charset));
+ }
+ write(CRLF);
+ }
+
+ public void writeLine(final CharArrayBuffer s) throws IOException {
+ if (s == null) {
+ return;
+ }
+ if (this.ascii) {
+ int off = 0;
+ int remaining = s.length();
+ while (remaining > 0) {
+ int chunk = this.buffer.capacity() - this.buffer.length();
+ chunk = Math.min(chunk, remaining);
+ if (chunk > 0) {
+ this.buffer.append(s, off, chunk);
+ }
+ if (this.buffer.isFull()) {
+ flushBuffer();
+ }
+ off += chunk;
+ remaining -= chunk;
+ }
+ } else {
+ // This is VERY memory inefficient, BUT since non-ASCII charsets are
+ // NOT meant to be used anyway, there's no point optimizing it
+ byte[] tmp = s.toString().getBytes(this.charset);
+ write(tmp);
+ }
+ write(CRLF);
+ }
+
+ public HttpTransportMetrics getMetrics() {
+ return this.metrics;
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/ChunkedInputStream.java b/src/org/apache/http/impl/io/ChunkedInputStream.java
new file mode 100644
index 0000000..60cae90
--- /dev/null
+++ b/src/org/apache/http/impl/io/ChunkedInputStream.java
@@ -0,0 +1,294 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java $
+ * $Revision: 569843 $
+ * $Date: 2007-08-26 10:05:40 -0700 (Sun, 26 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.MalformedChunkCodingException;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.ExceptionUtils;
+
+/**
+ * Implements chunked transfer coding.
+ * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">section 3.6.1</a>.
+ * It transparently coalesces chunks of a HTTP stream that uses chunked
+ * transfer coding. After the stream is read to the end, it provides access
+ * to the trailers, if any.
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @author Ortwin Glueck
+ * @author Sean C. Sullivan
+ * @author Martin Elwin
+ * @author Eric Johnson
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ *
+ */
+public class ChunkedInputStream extends InputStream {
+
+ /** The session input buffer */
+ private SessionInputBuffer in;
+
+ private final CharArrayBuffer buffer;
+
+ /** The chunk size */
+ private int chunkSize;
+
+ /** The current position within the current chunk */
+ private int pos;
+
+ /** True if we'are at the beginning of stream */
+ private boolean bof = true;
+
+ /** 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[] {};
+
+ public ChunkedInputStream(final SessionInputBuffer in) {
+ super();
+ if (in == null) {
+ throw new IllegalArgumentException("Session input buffer may not be null");
+ }
+ this.in = in;
+ this.pos = 0;
+ this.buffer = new CharArrayBuffer(16);
+ }
+
+ /**
+ * <p> 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.</p>
+ *
+ * <p> Trailer headers are read automcatically at the end of the stream and
+ * can be obtained with the getResponseFooters() method.</p>
+ *
+ * @return -1 of the end of the stream has been reached or the next data
+ * byte
+ * @throws IOException If an IO problem occurs
+ */
+ public int read() throws IOException {
+ if (this.closed) {
+ throw new IOException("Attempted read from closed stream.");
+ }
+ if (this.eof) {
+ return -1;
+ }
+ if (this.pos >= this.chunkSize) {
+ nextChunk();
+ if (this.eof) {
+ return -1;
+ }
+ }
+ pos++;
+ return in.read();
+ }
+
+ /**
+ * 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.
+ * @see java.io.InputStream#read(byte[], int, int)
+ * @throws IOException if an IO problem occurs.
+ */
+ public int read (byte[] b, int off, int len) throws IOException {
+
+ if (closed) {
+ throw new IOException("Attempted read from closed stream.");
+ }
+
+ if (eof) {
+ return -1;
+ }
+ if (pos >= chunkSize) {
+ nextChunk();
+ if (eof) {
+ return -1;
+ }
+ }
+ len = Math.min(len, chunkSize - pos);
+ int count = in.read(b, off, len);
+ pos += count;
+ return count;
+ }
+
+ /**
+ * 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.
+ * @see java.io.InputStream#read(byte[])
+ * @throws IOException if an IO problem occurs.
+ */
+ public int read (byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Read the next chunk.
+ * @throws IOException If an IO error occurs.
+ */
+ private void nextChunk() throws IOException {
+ chunkSize = getChunkSize();
+ if (chunkSize < 0) {
+ throw new MalformedChunkCodingException("Negative chunk size");
+ }
+ bof = false;
+ 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.
+ *
+ * @param in The new input stream.
+ * @param required <tt>true<tt/> if a valid chunk must be present,
+ * <tt>false<tt/> otherwise.
+ *
+ * @return the chunk size as integer
+ *
+ * @throws IOException when the chunk size could not be parsed
+ */
+ private int getChunkSize() throws IOException {
+ // skip CRLF
+ if (!bof) {
+ int cr = in.read();
+ int lf = in.read();
+ if ((cr != HTTP.CR) || (lf != HTTP.LF)) {
+ throw new MalformedChunkCodingException(
+ "CRLF expected at end of chunk");
+ }
+ }
+ //parse data
+ this.buffer.clear();
+ int i = this.in.readLine(this.buffer);
+ if (i == -1) {
+ throw new MalformedChunkCodingException(
+ "Chunked stream ended unexpectedly");
+ }
+ int separator = this.buffer.indexOf(';');
+ if (separator < 0) {
+ separator = this.buffer.length();
+ }
+ try {
+ return Integer.parseInt(this.buffer.substringTrimmed(0, separator), 16);
+ } catch (NumberFormatException e) {
+ throw new MalformedChunkCodingException("Bad chunk header");
+ }
+ }
+
+ /**
+ * Reads and stores the Trailer headers.
+ * @throws IOException If an IO problem occurs
+ */
+ private void parseTrailerHeaders() throws IOException {
+ try {
+ this.footers = AbstractMessageParser.parseHeaders
+ (in, -1, -1, null);
+ } catch (HttpException e) {
+ IOException ioe = new MalformedChunkCodingException("Invalid footer: "
+ + e.getMessage());
+ ExceptionUtils.initCause(ioe, e);
+ 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 If an IO problem occurs.
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ try {
+ if (!eof) {
+ exhaustInputStream(this);
+ }
+ } finally {
+ eof = true;
+ closed = true;
+ }
+ }
+ }
+
+ public Header[] getFooters() {
+ return (Header[])this.footers.clone();
+ }
+
+ /**
+ * Exhaust an input stream, reading until EOF has been encountered.
+ *
+ * <p>Note that this function is intended as a non-public utility.
+ * This is a little weird, but it seemed silly to make a utility
+ * class for this one function, so instead it is just static and
+ * shared that way.</p>
+ *
+ * @param inStream The {@link InputStream} to exhaust.
+ * @throws IOException If an IO problem occurs
+ */
+ static void exhaustInputStream(final InputStream inStream) throws IOException {
+ // read and discard the remainder of the message
+ byte buffer[] = new byte[1024];
+ while (inStream.read(buffer) >= 0) {
+ ;
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/ChunkedOutputStream.java b/src/org/apache/http/impl/io/ChunkedOutputStream.java
new file mode 100644
index 0000000..5ee7dd6
--- /dev/null
+++ b/src/org/apache/http/impl/io/ChunkedOutputStream.java
@@ -0,0 +1,191 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ChunkedOutputStream.java $
+ * $Revision: 645081 $
+ * $Date: 2008-04-05 04:36:42 -0700 (Sat, 05 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * Implements chunked transfer coding.
+ * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>,
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">section 3.6.1</a>.
+ * Writes are buffered to an internal buffer (2048 default size).
+ *
+ * @author Mohammad Rezaei (Goldman, Sachs &amp; Co.)
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 4.0
+ */
+public class ChunkedOutputStream extends OutputStream {
+
+ // ----------------------------------------------------- Instance Variables
+ private final SessionOutputBuffer out;
+
+ private byte[] cache;
+
+ private int cachePosition = 0;
+
+ private boolean wroteLastChunk = false;
+
+ /** True if the stream is closed. */
+ private boolean closed = false;
+
+ // ----------------------------------------------------------- Constructors
+ /**
+ * Wraps a session output buffer and chunks the output.
+ * @param out the session output buffer to wrap
+ * @param bufferSize minimum chunk size (excluding last chunk)
+ * @throws IOException
+ */
+ public ChunkedOutputStream(final SessionOutputBuffer out, int bufferSize)
+ throws IOException {
+ super();
+ this.cache = new byte[bufferSize];
+ this.out = 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
+ */
+ public ChunkedOutputStream(final SessionOutputBuffer out)
+ throws IOException {
+ this(out, 2048);
+ }
+
+ // ----------------------------------------------------------- Internal methods
+ /**
+ * Writes the cache out onto the underlying stream
+ * @throws IOException
+ */
+ 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
+ * @param bufferToAppend
+ * @param off
+ * @param len
+ * @throws IOException
+ */
+ protected void flushCacheWithAppend(byte bufferToAppend[], int off, 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
+ */
+ public void finish() throws IOException {
+ if (!this.wroteLastChunk) {
+ flushCache();
+ writeClosingChunk();
+ this.wroteLastChunk = true;
+ }
+ }
+
+ // -------------------------------------------- OutputStream Methods
+ public void write(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.
+ * @param b
+ * @throws IOException
+ */
+ public void write(byte b[]) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ public void write(byte src[], int off, 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.
+ * @throws IOException
+ */
+ public void flush() throws IOException {
+ flushCache();
+ this.out.flush();
+ }
+
+ /**
+ * Finishes writing to the underlying stream, but does NOT close the underlying stream.
+ * @throws IOException
+ */
+ public void close() throws IOException {
+ if (!this.closed) {
+ this.closed = true;
+ finish();
+ this.out.flush();
+ }
+ }
+}
diff --git a/src/org/apache/http/impl/io/ContentLengthInputStream.java b/src/org/apache/http/impl/io/ContentLengthInputStream.java
new file mode 100644
index 0000000..3b19c5b
--- /dev/null
+++ b/src/org/apache/http/impl/io/ContentLengthInputStream.java
@@ -0,0 +1,220 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ContentLengthInputStream.java $
+ * $Revision: 652091 $
+ * $Date: 2008-04-29 13:41:07 -0700 (Tue, 29 Apr 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.io.SessionInputBuffer;
+
+/**
+ * Stream that cuts off after a specified number of bytes.
+ * 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.
+ *
+ * <p>Implementation note: Choices abound. One approach would pass
+ * through the {@link InputStream#mark} and {@link InputStream#reset} calls to
+ * the underlying stream. That's tricky, though, because you then have to
+ * start duplicating the work of keeping track of how much a reset rewinds.
+ * Further, you have to watch out for the "readLimit", and since the semantics
+ * for the readLimit leave room for differing implementations, you might get
+ * into a lot of trouble.</p>
+ *
+ * <p>Alternatively, you could make this class extend
+ * {@link java.io.BufferedInputStream}
+ * and then use the protected members of that class to avoid duplicated effort.
+ * That solution has the side effect of adding yet another possible layer of
+ * buffering.</p>
+ *
+ * <p>Then, there is the simple choice, which this takes - simply don't
+ * support {@link InputStream#mark} and {@link InputStream#reset}. That choice
+ * has the added benefit of keeping this class very simple.</p>
+ *
+ * @author Ortwin Glueck
+ * @author Eric Johnson
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @since 4.0
+ */
+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 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;
+
+ /**
+ * Creates a new length limited stream
+ *
+ * @param in The session input buffer to wrap
+ * @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, long contentLength) {
+ super();
+ if (in == null) {
+ throw new IllegalArgumentException("Input stream may not be null");
+ }
+ if (contentLength < 0) {
+ throw new IllegalArgumentException("Content length may not be negative");
+ }
+ this.in = in;
+ this.contentLength = contentLength;
+ }
+
+ /**
+ * <p>Reads until the end of the known length of content.</p>
+ *
+ * <p>Does not close the underlying socket input, but instead leaves it
+ * primed to parse the next response.</p>
+ * @throws IOException If an IO problem occurs.
+ */
+ public void close() throws IOException {
+ if (!closed) {
+ try {
+ 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;
+ }
+ }
+ }
+
+
+ /**
+ * 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()
+ */
+ public int read() throws IOException {
+ if (closed) {
+ throw new IOException("Attempted read from closed stream.");
+ }
+
+ if (pos >= contentLength) {
+ return -1;
+ }
+ pos++;
+ return this.in.read();
+ }
+
+ /**
+ * 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.
+ */
+ public int read (byte[] b, int off, int len) throws java.io.IOException {
+ if (closed) {
+ throw new IOException("Attempted read from closed stream.");
+ }
+
+ if (pos >= contentLength) {
+ return -1;
+ }
+
+ if (pos + len > contentLength) {
+ len = (int) (contentLength - pos);
+ }
+ int count = this.in.read(b, off, len);
+ 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[])
+ */
+ public int read(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)
+ */
+ public long skip(long n) throws IOException {
+ if (n <= 0) {
+ return 0;
+ }
+ 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) {
+ int l = read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining));
+ if (l == -1) {
+ break;
+ }
+ count += l;
+ remaining -= l;
+ }
+ this.pos += count;
+ return count;
+ }
+}
diff --git a/src/org/apache/http/impl/io/ContentLengthOutputStream.java b/src/org/apache/http/impl/io/ContentLengthOutputStream.java
new file mode 100644
index 0000000..afcb883
--- /dev/null
+++ b/src/org/apache/http/impl/io/ContentLengthOutputStream.java
@@ -0,0 +1,132 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/ContentLengthOutputStream.java $
+ * $Revision: 560343 $
+ * $Date: 2007-07-27 11:18:19 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * A stream wrapper that closes itself after a defined number of bytes.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560343 $
+ *
+ * @since 4.0
+ */
+public class ContentLengthOutputStream extends OutputStream {
+
+ /**
+ * Wrapped session outbut 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;
+
+ /**
+ * Creates a new length limited stream
+ *
+ * @param out The data transmitter to wrap
+ * @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, long contentLength) {
+ super();
+ if (out == null) {
+ throw new IllegalArgumentException("Session output buffer may not be null");
+ }
+ if (contentLength < 0) {
+ throw new IllegalArgumentException("Content length may not be negative");
+ }
+ this.out = out;
+ this.contentLength = contentLength;
+ }
+
+ /**
+ * <p>Does not close the underlying socket output.</p>
+ *
+ * @throws IOException If an I/O problem occurs.
+ */
+ public void close() throws IOException {
+ if (!this.closed) {
+ this.closed = true;
+ this.out.flush();
+ }
+ }
+
+ public void flush() throws IOException {
+ this.out.flush();
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (this.closed) {
+ throw new IOException("Attempted write to closed stream.");
+ }
+ if (this.total < this.contentLength) {
+ long max = this.contentLength - this.total;
+ if (len > max) {
+ len = (int) max;
+ }
+ this.out.write(b, off, len);
+ this.total += len;
+ }
+ }
+
+ public void write(byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ public void write(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/src/org/apache/http/impl/io/HttpRequestParser.java b/src/org/apache/http/impl/io/HttpRequestParser.java
new file mode 100644
index 0000000..a7bae6d
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpRequestParser.java
@@ -0,0 +1,80 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpRequestParser.java $
+ * $Revision: 589374 $
+ * $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpRequestFactory;
+import org.apache.http.RequestLine;
+import org.apache.http.ParseException;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpRequestParser extends AbstractMessageParser {
+
+ private final HttpRequestFactory requestFactory;
+ private final CharArrayBuffer lineBuf;
+
+ public HttpRequestParser(
+ final SessionInputBuffer buffer,
+ final LineParser parser,
+ final HttpRequestFactory requestFactory,
+ final HttpParams params) {
+ super(buffer, parser, params);
+ if (requestFactory == null) {
+ throw new IllegalArgumentException("Request factory may not be null");
+ }
+ this.requestFactory = requestFactory;
+ this.lineBuf = new CharArrayBuffer(128);
+ }
+
+ protected HttpMessage parseHead(
+ final SessionInputBuffer sessionBuffer)
+ throws IOException, HttpException, ParseException {
+
+ this.lineBuf.clear();
+ int i = sessionBuffer.readLine(this.lineBuf);
+ if (i == -1) {
+ throw new ConnectionClosedException("Client closed connection");
+ }
+ ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
+ RequestLine requestline = this.lineParser.parseRequestLine(this.lineBuf, cursor);
+ return this.requestFactory.newHttpRequest(requestline);
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/HttpRequestWriter.java b/src/org/apache/http/impl/io/HttpRequestWriter.java
new file mode 100644
index 0000000..b784e2d
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpRequestWriter.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpRequestWriter.java $
+ * $Revision: 569673 $
+ * $Date: 2007-08-25 06:58:51 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpRequest;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.message.LineFormatter;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpRequestWriter extends AbstractMessageWriter {
+
+ public HttpRequestWriter(final SessionOutputBuffer buffer,
+ final LineFormatter formatter,
+ final HttpParams params) {
+ super(buffer, formatter, params);
+ }
+
+ protected void writeHeadLine(final HttpMessage message)
+ throws IOException {
+
+ final CharArrayBuffer buffer = lineFormatter.formatRequestLine
+ (this.lineBuf, ((HttpRequest) message).getRequestLine());
+ this.sessionBuffer.writeLine(buffer);
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/HttpResponseParser.java b/src/org/apache/http/impl/io/HttpResponseParser.java
new file mode 100644
index 0000000..575aa18
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpResponseParser.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpResponseParser.java $
+ * $Revision: 589374 $
+ * $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.StatusLine;
+import org.apache.http.ParseException;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpResponseParser extends AbstractMessageParser {
+
+ private final HttpResponseFactory responseFactory;
+ private final CharArrayBuffer lineBuf;
+
+ public HttpResponseParser(
+ final SessionInputBuffer buffer,
+ final LineParser parser,
+ final HttpResponseFactory responseFactory,
+ final HttpParams params) {
+ super(buffer, parser, params);
+ if (responseFactory == null) {
+ throw new IllegalArgumentException("Response factory may not be null");
+ }
+ this.responseFactory = responseFactory;
+ this.lineBuf = new CharArrayBuffer(128);
+ }
+
+ protected HttpMessage parseHead(
+ final SessionInputBuffer sessionBuffer)
+ throws IOException, HttpException, ParseException {
+
+ this.lineBuf.clear();
+ 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
+ ParserCursor cursor = new ParserCursor(0, this.lineBuf.length());
+ StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
+ return this.responseFactory.newHttpResponse(statusline, null);
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/HttpResponseWriter.java b/src/org/apache/http/impl/io/HttpResponseWriter.java
new file mode 100644
index 0000000..f88791e
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpResponseWriter.java
@@ -0,0 +1,59 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpResponseWriter.java $
+ * $Revision: 569673 $
+ * $Date: 2007-08-25 06:58:51 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpResponse;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.message.LineFormatter;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+public class HttpResponseWriter extends AbstractMessageWriter {
+
+ public HttpResponseWriter(final SessionOutputBuffer buffer,
+ final LineFormatter formatter,
+ final HttpParams params) {
+ super(buffer, formatter, params);
+ }
+
+ protected void writeHeadLine(final HttpMessage message)
+ throws IOException {
+
+ final CharArrayBuffer buffer = lineFormatter.formatStatusLine
+ (this.lineBuf, ((HttpResponse) message).getStatusLine());
+ this.sessionBuffer.writeLine(buffer);
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/HttpTransportMetricsImpl.java b/src/org/apache/http/impl/io/HttpTransportMetricsImpl.java
new file mode 100644
index 0000000..53e6772
--- /dev/null
+++ b/src/org/apache/http/impl/io/HttpTransportMetricsImpl.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/HttpTransportMetricsImpl.java $
+ * $Revision: 539755 $
+ * $Date: 2007-05-19 07:05:02 -0700 (Sat, 19 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import org.apache.http.io.HttpTransportMetrics;
+
+/**
+ * Default implementation of {@link HttpTransportMetrics}.
+ */
+public class HttpTransportMetricsImpl implements HttpTransportMetrics {
+
+ private long bytesTransferred = 0;
+
+ public HttpTransportMetricsImpl() {
+ super();
+ }
+
+ public long getBytesTransferred() {
+ return this.bytesTransferred;
+ }
+
+ public void setBytesTransferred(long count) {
+ this.bytesTransferred = count;
+ }
+
+ public void incrementBytesTransferred(long count) {
+ this.bytesTransferred += count;
+ }
+
+ public void reset() {
+ this.bytesTransferred = 0;
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/IdentityInputStream.java b/src/org/apache/http/impl/io/IdentityInputStream.java
new file mode 100644
index 0000000..390d5b7
--- /dev/null
+++ b/src/org/apache/http/impl/io/IdentityInputStream.java
@@ -0,0 +1,90 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/IdentityInputStream.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.io.SessionInputBuffer;
+
+/**
+ * A stream for reading from a {@link SessionInputBuffer session input buffer}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ *
+ * @since 4.0
+ */
+public class IdentityInputStream extends InputStream {
+
+ private final SessionInputBuffer in;
+
+ private boolean closed = false;
+
+ public IdentityInputStream(final SessionInputBuffer in) {
+ super();
+ if (in == null) {
+ throw new IllegalArgumentException("Session input buffer may not be null");
+ }
+ this.in = in;
+ }
+
+ public int available() throws IOException {
+ if (!this.closed && this.in.isDataAvailable(10)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public void close() throws IOException {
+ this.closed = true;
+ }
+
+ public int read() throws IOException {
+ if (this.closed) {
+ return -1;
+ } else {
+ return this.in.read();
+ }
+ }
+
+ public int read(final byte[] b, int off, int len) throws IOException {
+ if (this.closed) {
+ return -1;
+ } else {
+ return this.in.read(b, off, len);
+ }
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/IdentityOutputStream.java b/src/org/apache/http/impl/io/IdentityOutputStream.java
new file mode 100644
index 0000000..10b64f7
--- /dev/null
+++ b/src/org/apache/http/impl/io/IdentityOutputStream.java
@@ -0,0 +1,100 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/IdentityOutputStream.java $
+ * $Revision: 560343 $
+ * $Date: 2007-07-27 11:18:19 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.io.SessionOutputBuffer;
+
+/**
+ * A stream for writing with an "identity" transport encoding.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560343 $
+ *
+ * @since 4.0
+ */
+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();
+ if (out == null) {
+ throw new IllegalArgumentException("Session output buffer may not be null");
+ }
+ this.out = out;
+ }
+
+ /**
+ * <p>Does not close the underlying socket output.</p>
+ *
+ * @throws IOException If an I/O problem occurs.
+ */
+ public void close() throws IOException {
+ if (!this.closed) {
+ this.closed = true;
+ this.out.flush();
+ }
+ }
+
+ public void flush() throws IOException {
+ this.out.flush();
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (this.closed) {
+ throw new IOException("Attempted write to closed stream.");
+ }
+ this.out.write(b, off, len);
+ }
+
+ public void write(byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ public void write(int b) throws IOException {
+ if (this.closed) {
+ throw new IOException("Attempted write to closed stream.");
+ }
+ this.out.write(b);
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/SocketInputBuffer.java b/src/org/apache/http/impl/io/SocketInputBuffer.java
new file mode 100644
index 0000000..925e80a
--- /dev/null
+++ b/src/org/apache/http/impl/io/SocketInputBuffer.java
@@ -0,0 +1,115 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/SocketInputBuffer.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * {@link Socket} bound session input buffer.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ *
+ * @since 4.0
+ */
+public class SocketInputBuffer extends AbstractSessionInputBuffer {
+
+ static private final Class SOCKET_TIMEOUT_CLASS = SocketTimeoutExceptionClass();
+
+ /**
+ * Returns <code>SocketTimeoutExceptionClass<code> or <code>null</code> if the class
+ * does not exist.
+ *
+ * @return <code>SocketTimeoutExceptionClass<code>, or <code>null</code> if unavailable.
+ */
+ static private Class SocketTimeoutExceptionClass() {
+ try {
+ return Class.forName("java.net.SocketTimeoutException");
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ private static boolean isSocketTimeoutException(final InterruptedIOException e) {
+ if (SOCKET_TIMEOUT_CLASS != null) {
+ return SOCKET_TIMEOUT_CLASS.isInstance(e);
+ } else {
+ return true;
+ }
+ }
+
+ private final Socket socket;
+
+ public SocketInputBuffer(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ super();
+ if (socket == null) {
+ throw new IllegalArgumentException("Socket may not be null");
+ }
+ this.socket = socket;
+ if (buffersize < 0) {
+ buffersize = socket.getReceiveBufferSize();
+ }
+ if (buffersize < 1024) {
+ buffersize = 1024;
+ }
+ init(socket.getInputStream(), buffersize, params);
+ }
+
+ public boolean isDataAvailable(int timeout) throws IOException {
+ boolean result = hasBufferedData();
+ if (!result) {
+ int oldtimeout = this.socket.getSoTimeout();
+ try {
+ this.socket.setSoTimeout(timeout);
+ fillBuffer();
+ result = hasBufferedData();
+ } catch (InterruptedIOException e) {
+ if (!isSocketTimeoutException(e)) {
+ throw e;
+ }
+ } finally {
+ socket.setSoTimeout(oldtimeout);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/SocketOutputBuffer.java b/src/org/apache/http/impl/io/SocketOutputBuffer.java
new file mode 100644
index 0000000..efb91e9
--- /dev/null
+++ b/src/org/apache/http/impl/io/SocketOutputBuffer.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/SocketOutputBuffer.java $
+ * $Revision: 560358 $
+ * $Date: 2007-07-27 12:30:42 -0700 (Fri, 27 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.io;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * {@link Socket} bound session output buffer.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560358 $
+ *
+ * @since 4.0
+ */
+public class SocketOutputBuffer extends AbstractSessionOutputBuffer {
+
+ public SocketOutputBuffer(
+ final Socket socket,
+ int buffersize,
+ final HttpParams params) throws IOException {
+ super();
+ if (socket == null) {
+ throw new IllegalArgumentException("Socket may not be null");
+ }
+ if (buffersize < 0) {
+ buffersize = socket.getReceiveBufferSize();
+// BEGIN android-changed
+ // Workaround for http://b/issue?id=1083103.
+ if (buffersize > 8096) {
+ buffersize = 8096;
+ }
+// END android-changed
+ }
+ if (buffersize < 1024) {
+ buffersize = 1024;
+ }
+
+// BEGIN android-changed
+ socket.setSendBufferSize(buffersize * 3);
+// END andrdoid-changed
+
+ init(socket.getOutputStream(), buffersize, params);
+ }
+
+}
diff --git a/src/org/apache/http/impl/io/package.html b/src/org/apache/http/impl/io/package.html
new file mode 100644
index 0000000..48eb2c1
--- /dev/null
+++ b/src/org/apache/http/impl/io/package.html
@@ -0,0 +1,48 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/package.html $
+ * $Revision: 567360 $
+ * $Date: 2007-08-18 23:49:21 -0700 (Sat, 18 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Default implementations for interfaces in
+{@link org.apache.http.io org.apache.http.io}.
+
+<br/>
+
+There are implementations of the transport encodings used by HTTP,
+in particular the chunked encoding for
+{@link org.apache.http.impl.io.ChunkedOutputStream sending} and
+{@link org.apache.http.impl.io.ChunkedInputStream receiving} entities.
+
+</body>
+</html>
diff --git a/src/org/apache/http/impl/package.html b/src/org/apache/http/impl/package.html
new file mode 100644
index 0000000..6cec586
--- /dev/null
+++ b/src/org/apache/http/impl/package.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/package.html $
+ * $Revision: 496072 $
+ * $Date: 2007-01-14 04:22:55 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Default implementations for interfaces in
+{@link org.apache.http org.apache.http}.
+
+</body>
+</html>
diff --git a/src/org/apache/http/io/HttpMessageParser.java b/src/org/apache/http/io/HttpMessageParser.java
new file mode 100644
index 0000000..5c24736
--- /dev/null
+++ b/src/org/apache/http/io/HttpMessageParser.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/HttpMessageParser.java $
+ * $Revision: 567370 $
+ * $Date: 2007-08-19 01:13:21 -0700 (Sun, 19 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+
+/**
+ * Generic message parser interface.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 567370 $
+ *
+ * @since 4.0
+ */
+public interface HttpMessageParser {
+
+ HttpMessage parse()
+ throws IOException, HttpException;
+
+}
diff --git a/src/org/apache/http/io/HttpMessageWriter.java b/src/org/apache/http/io/HttpMessageWriter.java
new file mode 100644
index 0000000..b6ac7c1
--- /dev/null
+++ b/src/org/apache/http/io/HttpMessageWriter.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/HttpMessageWriter.java $
+ * $Revision: 567370 $
+ * $Date: 2007-08-19 01:13:21 -0700 (Sun, 19 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+
+/**
+ * Generic message writer interface.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 567370 $
+ *
+ * @since 4.0
+ */
+public interface HttpMessageWriter {
+
+ void write(HttpMessage message)
+ throws IOException, HttpException;
+
+}
diff --git a/src/org/apache/http/io/HttpTransportMetrics.java b/src/org/apache/http/io/HttpTransportMetrics.java
new file mode 100644
index 0000000..f88e036
--- /dev/null
+++ b/src/org/apache/http/io/HttpTransportMetrics.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/HttpTransportMetrics.java $
+ * $Revision: 536667 $
+ * $Date: 2007-05-09 14:48:41 -0700 (Wed, 09 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+public interface HttpTransportMetrics {
+
+ /**
+ * Returns the number of bytes trasferred.
+ */
+ long getBytesTransferred();
+
+ /**
+ * Resets the counts
+ */
+ void reset();
+
+}
diff --git a/src/org/apache/http/io/SessionInputBuffer.java b/src/org/apache/http/io/SessionInputBuffer.java
new file mode 100644
index 0000000..d7824d9
--- /dev/null
+++ b/src/org/apache/http/io/SessionInputBuffer.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/SessionInputBuffer.java $
+ * $Revision: 560528 $
+ * $Date: 2007-07-28 04:34:17 -0700 (Sat, 28 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Session input buffer for blocking connections.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560528 $
+ *
+ * @since 4.0
+ */
+public interface SessionInputBuffer {
+
+ int read(byte[] b, int off, int len) throws IOException;
+
+ int read(byte[] b) throws IOException;
+
+ int read() throws IOException;
+
+ int readLine(CharArrayBuffer buffer) throws IOException;
+
+ String readLine() throws IOException;
+
+ boolean isDataAvailable(int timeout) throws IOException;
+
+ HttpTransportMetrics getMetrics();
+
+}
diff --git a/src/org/apache/http/io/SessionOutputBuffer.java b/src/org/apache/http/io/SessionOutputBuffer.java
new file mode 100644
index 0000000..6587a26
--- /dev/null
+++ b/src/org/apache/http/io/SessionOutputBuffer.java
@@ -0,0 +1,63 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/SessionOutputBuffer.java $
+ * $Revision: 560528 $
+ * $Date: 2007-07-28 04:34:17 -0700 (Sat, 28 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.io;
+
+import java.io.IOException;
+
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Session output buffer for blocking connections.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 560528 $
+ *
+ * @since 4.0
+ */
+public interface SessionOutputBuffer {
+
+ void write(byte[] b, int off, int len) throws IOException;
+
+ void write(byte[] b) throws IOException;
+
+ void write(int b) throws IOException;
+
+ void writeLine(String s) throws IOException;
+
+ void writeLine(CharArrayBuffer buffer) throws IOException;
+
+ void flush() throws IOException;
+
+ HttpTransportMetrics getMetrics();
+
+}
diff --git a/src/org/apache/http/io/package.html b/src/org/apache/http/io/package.html
new file mode 100644
index 0000000..da9fabf
--- /dev/null
+++ b/src/org/apache/http/io/package.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/io/package.html $
+ * $Revision: 503192 $
+ * $Date: 2007-02-03 03:55:49 -0800 (Sat, 03 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The transport layer abstraction of the HTTP components.
+
+This layer is used to transfer messages over connections.
+Connections are typically based on sockets: plain or secure,
+direct or via SOCKS hosts, with bandwidth throttling, or
+whatever else you might think of.
+However, opening connections is not within the responsibility
+of HttpCore.
+
+</body>
+</html>
diff --git a/src/org/apache/http/message/AbstractHttpMessage.java b/src/org/apache/http/message/AbstractHttpMessage.java
new file mode 100644
index 0000000..d8a6962
--- /dev/null
+++ b/src/org/apache/http/message/AbstractHttpMessage.java
@@ -0,0 +1,166 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/AbstractHttpMessage.java $
+ * $Revision: 620287 $
+ * $Date: 2008-02-10 07:15:53 -0800 (Sun, 10 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.Iterator;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpMessage;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.BasicHttpParams;
+
+/**
+ * Basic implementation of an HTTP message that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 620287 $
+ *
+ * @since 4.0
+ */
+public abstract class AbstractHttpMessage implements HttpMessage {
+
+ protected HeaderGroup headergroup;
+
+ protected HttpParams params;
+
+ 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(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) {
+ if (name == null) {
+ throw new IllegalArgumentException("Header name may not be null");
+ }
+ 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) {
+ if (name == null) {
+ throw new IllegalArgumentException("Header name may not be null");
+ }
+ 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 (Iterator i = this.headergroup.iterator(); i.hasNext(); ) {
+ Header header = (Header) i.next();
+ 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(String name) {
+ return this.headergroup.iterator(name);
+ }
+
+ // non-javadoc, see interface HttpMessage
+ public HttpParams getParams() {
+ if (this.params == null) {
+ this.params = new BasicHttpParams();
+ }
+ return this.params;
+ }
+
+ // non-javadoc, see interface HttpMessage
+ public void setParams(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.params = params;
+ }
+}
diff --git a/src/org/apache/http/message/BasicHeader.java b/src/org/apache/http/message/BasicHeader.java
new file mode 100644
index 0000000..f134d8d
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeader.java
@@ -0,0 +1,143 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeader.java $
+ * $Revision: 652956 $
+ * $Date: 2008-05-02 17:13:05 -0700 (Fri, 02 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.ParseException;
+
+/**
+ * Represents an HTTP header field.
+ *
+ * <p>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.
+ *
+ *<pre>
+ * message-header = field-name ":" [ field-value ]
+ * field-name = token
+ * field-value = *( field-content | LWS )
+ * field-content = &lt;the OCTETs making up the field-value
+ * and consisting of either *TEXT or combinations
+ * of token, separators, and quoted-string&gt;
+ *</pre>
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 652956 $ $Date: 2008-05-02 17:13:05 -0700 (Fri, 02 May 2008) $
+ *
+ * @since 4.0
+ */
+public class BasicHeader implements Header, Cloneable {
+
+ /**
+ * Header name.
+ */
+ private final String name;
+
+ /**
+ * Header value.
+ */
+ 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();
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ this.name = name;
+ this.value = value;
+ }
+
+ /**
+ * Returns the header name.
+ *
+ * @return String name The name
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Returns the header value.
+ *
+ * @return String value The current value.
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * Returns a {@link String} representation of the header.
+ *
+ * @return a string
+ */
+ public String toString() {
+ // no need for non-default formatting in toString()
+ return BasicLineFormatter.DEFAULT.formatHeader(null, this).toString();
+ }
+
+ /**
+ * Returns an array of {@link HeaderElement}s constructed from my value.
+ *
+ * @see BasicHeaderValueParser#parseElements(String, HeaderValueParser)
+ *
+ * @return an array of header elements
+ *
+ * @throws ParseException in case of a parse error
+ */
+ 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[] {};
+ }
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+}
diff --git a/src/org/apache/http/message/BasicHeaderElement.java b/src/org/apache/http/message/BasicHeaderElement.java
new file mode 100644
index 0000000..19a40c6
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderElement.java
@@ -0,0 +1,243 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderElement.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.LangUtils;
+
+/**
+ * One element of an HTTP header's value.
+ * <p>
+ * 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:
+ * </p>
+ * <pre>
+ * header = [ element ] *( "," [ element ] )
+ * element = name [ "=" [ value ] ] *( ";" [ param ] )
+ * param = name [ "=" [ value ] ]
+ *
+ * name = token
+ * value = ( token | quoted-string )
+ *
+ * token = 1*&lt;any char except "=", ",", ";", &lt;"&gt; and
+ * white space&gt;
+ * quoted-string = &lt;"&gt; *( text | quoted-char ) &lt;"&gt;
+ * text = any char except &lt;"&gt;
+ * quoted-char = "\" char
+ * </pre>
+ * <p>
+ * 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 <var>null</var> will be stored instead.
+ * </p>
+ * <p>
+ * This class represents an individual header element, containing
+ * both a name/value pair (value may be <tt>null</tt>) and optionally
+ * a set of additional parameters.
+ * </p>
+ *
+ * @author <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a>
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 604625 $ $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * @since 4.0
+ */
+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 <tt>null</tt>
+ * @param parameters header element parameters. May be <tt>null</tt>.
+ * Parameters are copied by reference, not by value
+ */
+ public BasicHeaderElement(
+ final String name,
+ final String value,
+ final NameValuePair[] parameters) {
+ super();
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ this.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 <tt>null</tt>
+ */
+ public BasicHeaderElement(final String name, final String value) {
+ this(name, value, null);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Get parameters, if any.
+ * The returned array is created for each invocation and can
+ * be modified by the caller without affecting this header element.
+ *
+ * @return parameters as an array of {@link NameValuePair}s
+ */
+ public NameValuePair[] getParameters() {
+ return (NameValuePair[])this.parameters.clone();
+ }
+
+
+ /**
+ * Obtains the number of parameters.
+ *
+ * @return the number of parameters
+ */
+ public int getParameterCount() {
+ return this.parameters.length;
+ }
+
+
+ /**
+ * Obtains the parameter with the given index.
+ *
+ * @param index the index of the parameter, 0-based
+ *
+ * @return the parameter with the given index
+ */
+ public NameValuePair getParameter(int index) {
+ // ArrayIndexOutOfBoundsException is appropriate
+ return this.parameters[index];
+ }
+
+
+ /**
+ * Returns parameter with the given name, if found. Otherwise null
+ * is returned
+ *
+ * @param name The name to search by.
+ * @return NameValuePair parameter with the given name
+ */
+ public NameValuePair getParameterByName(final String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ NameValuePair found = null;
+ for (int i = 0; i < this.parameters.length; i++) {
+ NameValuePair current = this.parameters[ i ];
+ if (current.getName().equalsIgnoreCase(name)) {
+ found = current;
+ break;
+ }
+ }
+ return found;
+ }
+
+ public boolean equals(final Object object) {
+ if (object == null) return false;
+ if (this == object) return true;
+ if (object instanceof HeaderElement) {
+ 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;
+ }
+ }
+
+ public int hashCode() {
+ int hash = LangUtils.HASH_SEED;
+ hash = LangUtils.hashCode(hash, this.name);
+ hash = LangUtils.hashCode(hash, this.value);
+ for (int i = 0; i < this.parameters.length; i++) {
+ hash = LangUtils.hashCode(hash, this.parameters[i]);
+ }
+ return hash;
+ }
+
+ public String toString() {
+ CharArrayBuffer buffer = new CharArrayBuffer(64);
+ buffer.append(this.name);
+ if (this.value != null) {
+ buffer.append("=");
+ buffer.append(this.value);
+ }
+ for (int i = 0; i < this.parameters.length; i++) {
+ buffer.append("; ");
+ buffer.append(this.parameters[i]);
+ }
+ return buffer.toString();
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ // parameters array is considered immutable
+ // no need to make a copy of it
+ return super.clone();
+ }
+
+}
+
diff --git a/src/org/apache/http/message/BasicHeaderElementIterator.java b/src/org/apache/http/message/BasicHeaderElementIterator.java
new file mode 100644
index 0000000..46f53a8
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderElementIterator.java
@@ -0,0 +1,161 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderElementIterator.java $
+ * $Revision: 592088 $
+ * $Date: 2007-11-05 09:03:39 -0800 (Mon, 05 Nov 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.NoSuchElementException;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderElementIterator;
+import org.apache.http.HeaderIterator;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Basic implementation of a {@link HeaderElementIterator}.
+ *
+ * @version $Revision: 592088 $
+ *
+ * @author Andrea Selva <selva.andre at gmail.com>
+ * @author Oleg Kalnichevski <oleg at ural.ru>
+ */
+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) {
+ if (headerIterator == null) {
+ throw new IllegalArgumentException("Header iterator may not be null");
+ }
+ if (parser == null) {
+ throw new IllegalArgumentException("Parser may not be null");
+ }
+ this.headerIt = headerIterator;
+ this.parser = parser;
+ }
+
+
+ public BasicHeaderElementIterator(final HeaderIterator headerIterator) {
+ this(headerIterator, BasicHeaderValueParser.DEFAULT);
+ }
+
+
+ private void bufferHeaderValue() {
+ this.cursor = null;
+ this.buffer = null;
+ while (this.headerIt.hasNext()) {
+ 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 {
+ 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()) {
+ 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");
+ }
+
+ 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");
+ }
+
+} \ No newline at end of file
diff --git a/src/org/apache/http/message/BasicHeaderIterator.java b/src/org/apache/http/message/BasicHeaderIterator.java
new file mode 100644
index 0000000..32cd1c8
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderIterator.java
@@ -0,0 +1,180 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderIterator.java $
+ * $Revision: 581981 $
+ * $Date: 2007-10-04 11:26:26 -0700 (Thu, 04 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import java.util.NoSuchElementException;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+
+
+/**
+ * Basic implementation of a {@link HeaderIterator}.
+ *
+ * @version $Revision: 581981 $
+ */
+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.
+ * <code>null</code> 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
+ * <code>null</code> for any
+ */
+ public BasicHeaderIterator(Header[] headers, String name) {
+ if (headers == null) {
+ throw new IllegalArgumentException
+ ("Header array must not be null.");
+ }
+
+ this.allHeaders = headers;
+ this.headerName = name;
+ this.currentIndex = findNext(-1);
+ }
+
+
+ /**
+ * Determines the index of the next header.
+ *
+ * @param from 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(int from) {
+ 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 <code>true</code> if the header should be part of the
+ * iteration, <code>false</code> to skip
+ */
+ protected boolean filterHeader(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/src/org/apache/http/message/BasicHeaderValueFormatter.java b/src/org/apache/http/message/BasicHeaderValueFormatter.java
new file mode 100644
index 0000000..b63bdf7
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderValueFormatter.java
@@ -0,0 +1,441 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderValueFormatter.java $
+ * $Revision: 574185 $
+ * $Date: 2007-09-10 02:19:47 -0700 (Mon, 10 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.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.
+ *
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ * @author and others
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 574185 $
+ *
+ * @since 4.0
+ */
+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.
+ */
+ public final static
+ BasicHeaderValueFormatter DEFAULT = 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 default constructor
+
+
+
+ /**
+ * Formats an array of header elements.
+ *
+ * @param elems the header elements to format
+ * @param quote <code>true</code> to always format with quoted values,
+ * <code>false</code> to use quotes only when necessary
+ * @param formatter the formatter to use, or <code>null</code>
+ * for the {@link #DEFAULT default}
+ *
+ * @return the formatted header elements
+ */
+ public final static
+ String formatElements(final HeaderElement[] elems,
+ final boolean quote,
+ HeaderValueFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicHeaderValueFormatter.DEFAULT;
+ return formatter.formatElements(null, elems, quote).toString();
+ }
+
+
+ // non-javadoc, see interface HeaderValueFormatter
+ public CharArrayBuffer formatElements(CharArrayBuffer buffer,
+ final HeaderElement[] elems,
+ final boolean quote) {
+ if (elems == null) {
+ throw new IllegalArgumentException
+ ("Header element array must not be null.");
+ }
+
+ int len = estimateElementsLen(elems);
+ if (buffer == null) {
+ buffer = new CharArrayBuffer(len);
+ } else {
+ buffer.ensureCapacity(len);
+ }
+
+ for (int i=0; i<elems.length; i++) {
+ if (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 <code>null</code>
+ *
+ * @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 (int i=0; i<elems.length; i++) {
+ result += estimateHeaderElementLen(elems[i]);
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Formats a header element.
+ *
+ * @param elem the header element to format
+ * @param quote <code>true</code> to always format with quoted values,
+ * <code>false</code> to use quotes only when necessary
+ * @param formatter the formatter to use, or <code>null</code>
+ * for the {@link #DEFAULT default}
+ *
+ * @return the formatted header element
+ */
+ public final static
+ String formatHeaderElement(final HeaderElement elem,
+ boolean quote,
+ HeaderValueFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicHeaderValueFormatter.DEFAULT;
+ return formatter.formatHeaderElement(null, elem, quote).toString();
+ }
+
+
+ // non-javadoc, see interface HeaderValueFormatter
+ public CharArrayBuffer formatHeaderElement(CharArrayBuffer buffer,
+ final HeaderElement elem,
+ final boolean quote) {
+ if (elem == null) {
+ throw new IllegalArgumentException
+ ("Header element must not be null.");
+ }
+
+ int len = estimateHeaderElementLen(elem);
+ 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; i<parcnt; i++) {
+ buffer.append("; ");
+ formatNameValuePair(buffer, elem.getParameter(i), quote);
+ }
+ }
+
+ return buffer;
+ }
+
+
+ /**
+ * Estimates the length of a formatted header element.
+ *
+ * @param elem the header element to format, or <code>null</code>
+ *
+ * @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<parcnt; i++) {
+ result += 2 + // ; <param>
+ estimateNameValuePairLen(elem.getParameter(i));
+ }
+ }
+
+ return result;
+ }
+
+
+
+
+ /**
+ * Formats a set of parameters.
+ *
+ * @param nvps the parameters to format
+ * @param quote <code>true</code> to always format with quoted values,
+ * <code>false</code> to use quotes only when necessary
+ * @param formatter the formatter to use, or <code>null</code>
+ * for the {@link #DEFAULT default}
+ *
+ * @return the formatted parameters
+ */
+ public final static
+ String formatParameters(final NameValuePair[] nvps,
+ final boolean quote,
+ HeaderValueFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicHeaderValueFormatter.DEFAULT;
+ return formatter.formatParameters(null, nvps, quote).toString();
+ }
+
+
+ // non-javadoc, see interface HeaderValueFormatter
+ public CharArrayBuffer formatParameters(CharArrayBuffer buffer,
+ NameValuePair[] nvps,
+ boolean quote) {
+ if (nvps == null) {
+ throw new IllegalArgumentException
+ ("Parameters must not be null.");
+ }
+
+ int len = estimateParametersLen(nvps);
+ 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 <code>null</code>
+ *
+ * @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 (int i=0; i<nvps.length; i++) {
+ result += estimateNameValuePairLen(nvps[i]);
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Formats a name-value pair.
+ *
+ * @param nvp the name-value pair to format
+ * @param quote <code>true</code> to always format with a quoted value,
+ * <code>false</code> to use quotes only when necessary
+ * @param formatter the formatter to use, or <code>null</code>
+ * for the {@link #DEFAULT default}
+ *
+ * @return the formatted name-value pair
+ */
+ public final static
+ String formatNameValuePair(final NameValuePair nvp,
+ final boolean quote,
+ HeaderValueFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicHeaderValueFormatter.DEFAULT;
+ return formatter.formatNameValuePair(null, nvp, quote).toString();
+ }
+
+
+ // non-javadoc, see interface HeaderValueFormatter
+ public CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
+ final NameValuePair nvp,
+ final boolean quote) {
+ if (nvp == null) {
+ throw new IllegalArgumentException
+ ("NameValuePair must not be null.");
+ }
+
+ int len = estimateNameValuePairLen(nvp);
+ 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 <code>null</code>
+ *
+ * @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 <code>null</code>
+ * @param value the value to append, never <code>null</code>
+ * @param quote <code>true</code> to always format with quotes,
+ * <code>false</code> to use quotes only when necessary
+ */
+ protected void doFormatValue(final CharArrayBuffer buffer,
+ final String value,
+ boolean quote) {
+
+ if (!quote) {
+ for (int i = 0; (i < value.length()) && !quote; i++) {
+ quote = isSeparator(value.charAt(i));
+ }
+ }
+
+ if (quote) {
+ buffer.append('"');
+ }
+ for (int i = 0; i < value.length(); i++) {
+ char ch = value.charAt(i);
+ if (isUnsafe(ch)) {
+ buffer.append('\\');
+ }
+ buffer.append(ch);
+ }
+ if (quote) {
+ buffer.append('"');
+ }
+ }
+
+
+ /**
+ * Checks whether a character is a {@link #SEPARATORS separator}.
+ *
+ * @param ch the character to check
+ *
+ * @return <code>true</code> if the character is a separator,
+ * <code>false</code> otherwise
+ */
+ protected boolean isSeparator(char ch) {
+ return SEPARATORS.indexOf(ch) >= 0;
+ }
+
+
+ /**
+ * Checks whether a character is {@link #UNSAFE_CHARS unsafe}.
+ *
+ * @param ch the character to check
+ *
+ * @return <code>true</code> if the character is unsafe,
+ * <code>false</code> otherwise
+ */
+ protected boolean isUnsafe(char ch) {
+ return UNSAFE_CHARS.indexOf(ch) >= 0;
+ }
+
+
+} // class BasicHeaderValueFormatter
diff --git a/src/org/apache/http/message/BasicHeaderValueParser.java b/src/org/apache/http/message/BasicHeaderValueParser.java
new file mode 100644
index 0000000..5216196
--- /dev/null
+++ b/src/org/apache/http/message/BasicHeaderValueParser.java
@@ -0,0 +1,420 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHeaderValueParser.java $
+ * $Revision: 595670 $
+ * $Date: 2007-11-16 06:15:01 -0800 (Fri, 16 Nov 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.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.
+ *
+ * @author <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a>
+ * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ * @author and others
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 595670 $
+ *
+ * @since 4.0
+ */
+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.
+ */
+ public final static
+ BasicHeaderValueParser DEFAULT = 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 default constructor
+
+
+ /**
+ * Parses elements with the given parser.
+ *
+ * @param value the header value to parse
+ * @param parser the parser to use, or <code>null</code> for default
+ *
+ * @return array holding the header elements, never <code>null</code>
+ */
+ public final static
+ HeaderElement[] parseElements(final String value,
+ HeaderValueParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null");
+ }
+
+ if (parser == null)
+ parser = BasicHeaderValueParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ ParserCursor cursor = new ParserCursor(0, value.length());
+ return parser.parseElements(buffer, cursor);
+ }
+
+
+ // non-javadoc, see interface HeaderValueParser
+ public HeaderElement[] parseElements(final CharArrayBuffer buffer,
+ final ParserCursor cursor) {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+
+ List elements = new ArrayList();
+ while (!cursor.atEnd()) {
+ HeaderElement element = parseHeaderElement(buffer, cursor);
+ if (!(element.getName().length() == 0 && element.getValue() == null)) {
+ elements.add(element);
+ }
+ }
+ return (HeaderElement[])
+ 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 <code>null</code> for default
+ *
+ * @return the parsed header element
+ */
+ public final static
+ HeaderElement parseHeaderElement(final String value,
+ HeaderValueParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null");
+ }
+
+ if (parser == null)
+ parser = BasicHeaderValueParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ ParserCursor cursor = new ParserCursor(0, value.length());
+ return parser.parseHeaderElement(buffer, cursor);
+ }
+
+
+ // non-javadoc, see interface HeaderValueParser
+ public HeaderElement parseHeaderElement(final CharArrayBuffer buffer,
+ final ParserCursor cursor) {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+
+ NameValuePair nvp = parseNameValuePair(buffer, cursor);
+ NameValuePair[] params = null;
+ if (!cursor.atEnd()) {
+ 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 <code>null</code> for default
+ *
+ * @return array holding the parameters, never <code>null</code>
+ */
+ public final static
+ NameValuePair[] parseParameters(final String value,
+ HeaderValueParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null");
+ }
+
+ if (parser == null)
+ parser = BasicHeaderValueParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ ParserCursor cursor = new ParserCursor(0, value.length());
+ return parser.parseParameters(buffer, cursor);
+ }
+
+
+
+ // non-javadoc, see interface HeaderValueParser
+ public NameValuePair[] parseParameters(final CharArrayBuffer buffer,
+ final ParserCursor cursor) {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+
+ int pos = cursor.getPos();
+ int indexTo = cursor.getUpperBound();
+
+ while (pos < indexTo) {
+ char ch = buffer.charAt(pos);
+ if (HTTP.isWhitespace(ch)) {
+ pos++;
+ } else {
+ break;
+ }
+ }
+ cursor.updatePos(pos);
+ if (cursor.atEnd()) {
+ return new NameValuePair[] {};
+ }
+
+ List params = new ArrayList();
+ while (!cursor.atEnd()) {
+ NameValuePair param = parseNameValuePair(buffer, cursor);
+ params.add(param);
+ char ch = buffer.charAt(cursor.getPos() - 1);
+ if (ch == ELEM_DELIMITER) {
+ break;
+ }
+ }
+
+ return (NameValuePair[])
+ 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 <code>null</code> for default
+ *
+ * @return the parsed name-value pair
+ */
+ public final static
+ NameValuePair parseNameValuePair(final String value,
+ HeaderValueParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null");
+ }
+
+ if (parser == null)
+ parser = BasicHeaderValueParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ ParserCursor cursor = new ParserCursor(0, value.length());
+ return parser.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 (int i = 0; i < chs.length; i++) {
+ if (ch == chs[i]) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public NameValuePair parseNameValuePair(final CharArrayBuffer buffer,
+ final ParserCursor cursor,
+ final char[] delimiters) {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+
+ boolean terminated = false;
+
+ int pos = cursor.getPos();
+ int indexFrom = cursor.getPos();
+ int indexTo = cursor.getUpperBound();
+
+ // Find name
+ String name = null;
+ while (pos < indexTo) {
+ 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
+ String value = null;
+ int i1 = pos;
+
+ boolean qouted = false;
+ boolean escaped = false;
+ while (pos < indexTo) {
+ 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 <code>null</code>
+ *
+ * @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/src/org/apache/http/message/BasicHttpEntityEnclosingRequest.java b/src/org/apache/http/message/BasicHttpEntityEnclosingRequest.java
new file mode 100644
index 0000000..dbb70c8
--- /dev/null
+++ b/src/org/apache/http/message/BasicHttpEntityEnclosingRequest.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHttpEntityEnclosingRequest.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Basic implementation of a request with an entity that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ *
+ * @since 4.0
+ */
+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) {
+ this(new BasicRequestLine(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() {
+ Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
+ return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
+ }
+
+}
diff --git a/src/org/apache/http/message/BasicHttpRequest.java b/src/org/apache/http/message/BasicHttpRequest.java
new file mode 100644
index 0000000..eedf8bc
--- /dev/null
+++ b/src/org/apache/http/message/BasicHttpRequest.java
@@ -0,0 +1,98 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHttpRequest.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * Basic implementation of an HTTP request that can be modified.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @since 4.0
+ */
+public class BasicHttpRequest extends AbstractHttpMessage implements HttpRequest {
+
+ private final RequestLine requestline;
+ private final String method;
+ private final String uri;
+
+ public BasicHttpRequest(final String method, final String uri) {
+ super();
+ if (method == null) {
+ throw new IllegalArgumentException("Method name may not be null");
+ }
+ if (uri == null) {
+ throw new IllegalArgumentException("Request URI may not be null");
+ }
+ this.method = method;
+ this.uri = uri;
+ this.requestline = null;
+ }
+
+ public BasicHttpRequest(final String method, final String uri, final ProtocolVersion ver) {
+ this(new BasicRequestLine(method, uri, ver));
+ }
+
+ public BasicHttpRequest(final RequestLine requestline) {
+ super();
+ if (requestline == null) {
+ throw new IllegalArgumentException("Request line may not be null");
+ }
+ this.requestline = requestline;
+ this.method = requestline.getMethod();
+ this.uri = requestline.getUri();
+ }
+
+ public ProtocolVersion getProtocolVersion() {
+ if (this.requestline != null) {
+ return this.requestline.getProtocolVersion();
+ } else {
+ return HttpProtocolParams.getVersion(getParams());
+ }
+ }
+
+ public RequestLine getRequestLine() {
+ if (this.requestline != null) {
+ return this.requestline;
+ } else {
+ ProtocolVersion ver = HttpProtocolParams.getVersion(getParams());
+ return new BasicRequestLine(this.method, this.uri, ver);
+ }
+ }
+
+}
diff --git a/src/org/apache/http/message/BasicHttpResponse.java b/src/org/apache/http/message/BasicHttpResponse.java
new file mode 100644
index 0000000..7da4bea
--- /dev/null
+++ b/src/org/apache/http/message/BasicHttpResponse.java
@@ -0,0 +1,204 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicHttpResponse.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.Locale;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.ReasonPhraseCatalog;
+
+
+/**
+ * Basic implementation of an HTTP response that can be modified.
+ * This implementation makes sure that there always is a status line.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @since 4.0
+ */
+public class BasicHttpResponse extends AbstractHttpMessage
+ implements HttpResponse {
+
+ private StatusLine statusline;
+ private HttpEntity entity;
+ private 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
+ * <code>null</code> to disable automatic
+ * reason phrase lookup
+ * @param locale the locale for looking up reason phrases, or
+ * <code>null</code> for the system locale
+ */
+ public BasicHttpResponse(final StatusLine statusline,
+ final ReasonPhraseCatalog catalog,
+ final Locale locale) {
+ super();
+ if (statusline == null) {
+ throw new IllegalArgumentException("Status line may not be null.");
+ }
+ this.statusline = statusline;
+ this.reasonCatalog = catalog;
+ this.locale = (locale != null) ? locale : Locale.getDefault();
+ }
+
+ /**
+ * 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) {
+ this(statusline, null, 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
+ * <code>null</code>
+ */
+ public BasicHttpResponse(final ProtocolVersion ver,
+ final int code,
+ final String reason) {
+ this(new BasicStatusLine(ver, code, reason), null, null);
+ }
+
+
+ // non-javadoc, see interface HttpMessage
+ public ProtocolVersion getProtocolVersion() {
+ return this.statusline.getProtocolVersion();
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public StatusLine getStatusLine() {
+ return this.statusline;
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public HttpEntity getEntity() {
+ return this.entity;
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public void setStatusLine(final StatusLine statusline) {
+ if (statusline == null) {
+ throw new IllegalArgumentException("Status line may not be null");
+ }
+ this.statusline = statusline;
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public void setStatusLine(final ProtocolVersion ver, final int code) {
+ // arguments checked in BasicStatusLine constructor
+ this.statusline = new BasicStatusLine(ver, code, getReason(code));
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public void setStatusLine(final ProtocolVersion ver, final int code,
+ final String reason) {
+ // arguments checked in BasicStatusLine constructor
+ this.statusline = new BasicStatusLine(ver, code, reason);
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public void setStatusCode(int code) {
+ // argument checked in BasicStatusLine constructor
+ ProtocolVersion ver = this.statusline.getProtocolVersion();
+ this.statusline = new BasicStatusLine(ver, code, getReason(code));
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public void setReasonPhrase(String reason) {
+
+ if ((reason != null) && ((reason.indexOf('\n') >= 0) ||
+ (reason.indexOf('\r') >= 0))
+ ) {
+ throw new IllegalArgumentException("Line break in reason phrase.");
+ }
+ this.statusline = new BasicStatusLine(this.statusline.getProtocolVersion(),
+ this.statusline.getStatusCode(),
+ reason);
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public void setEntity(final HttpEntity entity) {
+ this.entity = entity;
+ }
+
+ // non-javadoc, see interface HttpResponse
+ public void setLocale(Locale loc) {
+ if (loc == null) {
+ throw new IllegalArgumentException("Locale may not be null.");
+ }
+ this.locale = loc;
+ final int code = this.statusline.getStatusCode();
+ this.statusline = new BasicStatusLine
+ (this.statusline.getProtocolVersion(), code, getReason(code));
+ }
+
+ /**
+ * 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 <code>null</code> if there is none
+ */
+ protected String getReason(int code) {
+ return (this.reasonCatalog == null) ?
+ null : this.reasonCatalog.getReason(code, this.locale);
+ }
+
+}
diff --git a/src/org/apache/http/message/BasicLineFormatter.java b/src/org/apache/http/message/BasicLineFormatter.java
new file mode 100644
index 0000000..7c3bbc4
--- /dev/null
+++ b/src/org/apache/http/message/BasicLineFormatter.java
@@ -0,0 +1,346 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicLineFormatter.java $
+ * $Revision: 574185 $
+ * $Date: 2007-09-10 02:19:47 -0700 (Mon, 10 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.FormattedHeader;
+import org.apache.http.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 <i>not</i> 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.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author and others
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 574185 $
+ *
+ * @since 4.0
+ */
+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.
+ */
+ public final static BasicLineFormatter DEFAULT = new BasicLineFormatter();
+
+
+
+ // public default constructor
+
+
+ /**
+ * Obtains a buffer for formatting.
+ *
+ * @param buffer a buffer already available, or <code>null</code>
+ *
+ * @return the cleared argument buffer if there is one, or
+ * a new empty buffer that can be used for formatting
+ */
+ protected CharArrayBuffer initBuffer(CharArrayBuffer buffer) {
+ 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
+ * <code>null</code> for the
+ * {@link #DEFAULT default}
+ *
+ * @return the formatted protocol version
+ */
+ public final static
+ String formatProtocolVersion(final ProtocolVersion version,
+ LineFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicLineFormatter.DEFAULT;
+ return formatter.appendProtocolVersion(null, version).toString();
+ }
+
+
+ // non-javadoc, see interface LineFormatter
+ public CharArrayBuffer appendProtocolVersion(final CharArrayBuffer buffer,
+ final ProtocolVersion version) {
+ if (version == null) {
+ throw new IllegalArgumentException
+ ("Protocol version may not be null");
+ }
+
+ // 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 <code>null</code>
+ *
+ * @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
+ * <code>null</code> for the
+ * {@link #DEFAULT default}
+ *
+ * @return the formatted request line
+ */
+ public final static String formatRequestLine(final RequestLine reqline,
+ LineFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicLineFormatter.DEFAULT;
+ return formatter.formatRequestLine(null, reqline).toString();
+ }
+
+
+ // non-javadoc, see interface LineFormatter
+ public CharArrayBuffer formatRequestLine(CharArrayBuffer buffer,
+ RequestLine reqline) {
+ if (reqline == null) {
+ throw new IllegalArgumentException
+ ("Request line may not be null");
+ }
+
+ 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 <code>null</code>
+ * @param reqline the request line to format, never <code>null</code>
+ */
+ 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"
+ 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
+ * <code>null</code> for the
+ * {@link #DEFAULT default}
+ *
+ * @return the formatted status line
+ */
+ public final static String formatStatusLine(final StatusLine statline,
+ LineFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicLineFormatter.DEFAULT;
+ return formatter.formatStatusLine(null, statline).toString();
+ }
+
+
+ // non-javadoc, see interface LineFormatter
+ public CharArrayBuffer formatStatusLine(final CharArrayBuffer buffer,
+ final StatusLine statline) {
+ if (statline == null) {
+ throw new IllegalArgumentException
+ ("Status line may not be null");
+ }
+
+ 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 <code>null</code>
+ * @param statline the status line to format, never <code>null</code>
+ */
+ 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
+ * <code>null</code> for the
+ * {@link #DEFAULT default}
+ *
+ * @return the formatted header
+ */
+ public final static String formatHeader(final Header header,
+ LineFormatter formatter) {
+ if (formatter == null)
+ formatter = BasicLineFormatter.DEFAULT;
+ return formatter.formatHeader(null, header).toString();
+ }
+
+
+ // non-javadoc, see interface LineFormatter
+ public CharArrayBuffer formatHeader(CharArrayBuffer buffer,
+ Header header) {
+ if (header == null) {
+ throw new IllegalArgumentException
+ ("Header may not be null");
+ }
+ CharArrayBuffer result = null;
+
+ 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 <code>null</code>
+ * @param header the header to format, never <code>null</code>
+ */
+ 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/src/org/apache/http/message/BasicLineParser.java b/src/org/apache/http/message/BasicLineParser.java
new file mode 100644
index 0000000..c5e9ddb
--- /dev/null
+++ b/src/org/apache/http/message/BasicLineParser.java
@@ -0,0 +1,504 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicLineParser.java $
+ * $Revision: 591798 $
+ * $Date: 2007-11-04 08:19:29 -0800 (Sun, 04 Nov 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ParseException;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author and others
+ */
+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.
+ */
+ public final static BasicLineParser DEFAULT = 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
+ * <code>null</code> for HTTP. The actual version
+ * is not relevant, only the protocol name.
+ */
+ public BasicLineParser(ProtocolVersion proto) {
+ if (proto == null) {
+ proto = HttpVersion.HTTP_1_1;
+ }
+ this.protocol = proto;
+ }
+
+
+ /**
+ * Creates a new line parser for HTTP.
+ */
+ public BasicLineParser() {
+ this(null);
+ }
+
+
+
+ public final static
+ ProtocolVersion parseProtocolVersion(String value,
+ LineParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null.");
+ }
+
+ if (parser == null)
+ parser = BasicLineParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ ParserCursor cursor = new ParserCursor(0, value.length());
+ return parser.parseProtocolVersion(buffer, cursor);
+ }
+
+
+ // non-javadoc, see interface LineParser
+ public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer,
+ final ParserCursor cursor)
+ throws ParseException {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+
+ final String protoname = this.protocol.getProtocol();
+ final int protolength = protoname.length();
+
+ int indexFrom = cursor.getPos();
+ 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<protolength); j++) {
+ ok = (buffer.charAt(i+j) == protoname.charAt(j));
+ }
+ if (ok) {
+ ok = (buffer.charAt(i+protolength) == '/');
+ }
+ if (!ok) {
+ throw new ParseException
+ ("Not a valid protocol version: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+
+ i += protolength+1;
+
+ int period = buffer.indexOf('.', i, indexTo);
+ if (period == -1) {
+ throw new ParseException
+ ("Invalid protocol version number: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+ int major;
+ try {
+ major = Integer.parseInt(buffer.substringTrimmed(i, period));
+ } catch (NumberFormatException e) {
+ throw new ParseException
+ ("Invalid protocol major version number: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+ i = period + 1;
+
+ int blank = buffer.indexOf(' ', i, indexTo);
+ if (blank == -1) {
+ blank = indexTo;
+ }
+ int minor;
+ try {
+ minor = Integer.parseInt(buffer.substringTrimmed(i, blank));
+ } catch (NumberFormatException e) {
+ throw new ParseException(
+ "Invalid protocol minor version number: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+
+ cursor.updatePos(blank);
+
+ return createProtocolVersion(major, minor);
+
+ } // parseProtocolVersion
+
+
+ /**
+ * Creates a protocol version.
+ * Called from {@link #parseProtocolVersion}.
+ *
+ * @param major the major version number, for example 1 in HTTP/1.0
+ * @param minor the minor version number, for example 0 in HTTP/1.0
+ *
+ * @return the protocol version
+ */
+ protected ProtocolVersion createProtocolVersion(int major, int minor) {
+ return protocol.forVersion(major, minor);
+ }
+
+
+
+ // non-javadoc, see interface LineParser
+ public boolean hasProtocolVersion(final CharArrayBuffer buffer,
+ final ParserCursor cursor) {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+ int index = cursor.getPos();
+
+ final String protoname = this.protocol.getProtocol();
+ final int protolength = protoname.length();
+
+ if (buffer.length() < protolength+4)
+ return false; // not long enough for "HTTP/1.1"
+
+ if (index < 0) {
+ // end of line, no tolerance for trailing whitespace
+ // this works only for single-digit major and minor version
+ index = buffer.length() -4 -protolength;
+ } else if (index == 0) {
+ // beginning of line, tolerate leading whitespace
+ while ((index < buffer.length()) &&
+ HTTP.isWhitespace(buffer.charAt(index))) {
+ index++;
+ }
+ } // else within line, don't tolerate whitespace
+
+
+ if (index + protolength + 4 > 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<protolength); j++) {
+ ok = (buffer.charAt(index+j) == protoname.charAt(j));
+ }
+ if (ok) {
+ ok = (buffer.charAt(index+protolength) == '/');
+ }
+
+ return ok;
+ }
+
+
+
+ public final static
+ RequestLine parseRequestLine(final String value,
+ LineParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null.");
+ }
+
+ if (parser == null)
+ parser = BasicLineParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ ParserCursor cursor = new ParserCursor(0, value.length());
+ return parser.parseRequestLine(buffer, cursor);
+ }
+
+
+ /**
+ * Parses a request line.
+ *
+ * @param buffer a buffer holding the line to parse
+ *
+ * @return the parsed request line
+ *
+ * @throws ParseException in case of a parse error
+ */
+ public RequestLine parseRequestLine(final CharArrayBuffer buffer,
+ final ParserCursor cursor)
+ throws ParseException {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+
+ int indexFrom = cursor.getPos();
+ int indexTo = cursor.getUpperBound();
+
+ try {
+ skipWhitespace(buffer, cursor);
+ int i = cursor.getPos();
+
+ int blank = buffer.indexOf(' ', i, indexTo);
+ if (blank < 0) {
+ throw new ParseException("Invalid request line: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+ String method = buffer.substringTrimmed(i, blank);
+ cursor.updatePos(blank);
+
+ skipWhitespace(buffer, cursor);
+ i = cursor.getPos();
+
+ blank = buffer.indexOf(' ', i, indexTo);
+ if (blank < 0) {
+ throw new ParseException("Invalid request line: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+ String uri = buffer.substringTrimmed(i, blank);
+ cursor.updatePos(blank);
+
+ ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
+
+ skipWhitespace(buffer, cursor);
+ if (!cursor.atEnd()) {
+ throw new ParseException("Invalid request line: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+
+ return createRequestLine(method, uri, ver);
+ } catch (IndexOutOfBoundsException e) {
+ throw new ParseException("Invalid request line: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+ } // parseRequestLine
+
+
+ /**
+ * Instantiates a new request line.
+ * Called from {@link #parseRequestLine}.
+ *
+ * @param method the request method
+ * @param uri the requested URI
+ * @param ver the protocol version
+ *
+ * @return a new status line with the given data
+ */
+ protected RequestLine createRequestLine(final String method,
+ final String uri,
+ final ProtocolVersion ver) {
+ return new BasicRequestLine(method, uri, ver);
+ }
+
+
+
+ public final static
+ StatusLine parseStatusLine(final String value,
+ LineParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null.");
+ }
+
+ if (parser == null)
+ parser = BasicLineParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ ParserCursor cursor = new ParserCursor(0, value.length());
+ return parser.parseStatusLine(buffer, cursor);
+ }
+
+
+ // non-javadoc, see interface LineParser
+ public StatusLine parseStatusLine(final CharArrayBuffer buffer,
+ final ParserCursor cursor)
+ throws ParseException {
+
+ if (buffer == null) {
+ throw new IllegalArgumentException("Char array buffer may not be null");
+ }
+ if (cursor == null) {
+ throw new IllegalArgumentException("Parser cursor may not be null");
+ }
+
+ int indexFrom = cursor.getPos();
+ int indexTo = cursor.getUpperBound();
+
+ try {
+ // handle the HTTP-Version
+ ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
+
+ // handle the Status-Code
+ skipWhitespace(buffer, cursor);
+ int i = cursor.getPos();
+
+ int blank = buffer.indexOf(' ', i, indexTo);
+ if (blank < 0) {
+ blank = indexTo;
+ }
+ int statusCode = 0;
+ try {
+ statusCode =
+ Integer.parseInt(buffer.substringTrimmed(i, blank));
+ } catch (NumberFormatException e) {
+ throw new ParseException(
+ "Unable to parse status code from status line: "
+ + buffer.substring(indexFrom, indexTo));
+ }
+ //handle the Reason-Phrase
+ i = blank;
+ String reasonPhrase = null;
+ if (i < indexTo) {
+ reasonPhrase = buffer.substringTrimmed(i, indexTo);
+ } else {
+ reasonPhrase = "";
+ }
+ return createStatusLine(ver, statusCode, reasonPhrase);
+
+ } catch (IndexOutOfBoundsException e) {
+ throw new ParseException("Invalid status line: " +
+ buffer.substring(indexFrom, indexTo));
+ }
+ } // parseStatusLine
+
+
+ /**
+ * Instantiates a new status line.
+ * Called from {@link #parseStatusLine}.
+ *
+ * @param ver the protocol version
+ * @param status the status code
+ * @param reason the reason phrase
+ *
+ * @return a new status line with the given data
+ */
+ protected StatusLine createStatusLine(final ProtocolVersion ver,
+ final int status,
+ final String reason) {
+ return new BasicStatusLine(ver, status, reason);
+ }
+
+
+
+ public final static
+ Header parseHeader(final String value,
+ LineParser parser)
+ throws ParseException {
+
+ if (value == null) {
+ throw new IllegalArgumentException
+ ("Value to parse may not be null");
+ }
+
+ if (parser == null)
+ parser = BasicLineParser.DEFAULT;
+
+ CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+ buffer.append(value);
+ return parser.parseHeader(buffer);
+ }
+
+
+ // non-javadoc, see interface LineParser
+ public Header parseHeader(CharArrayBuffer buffer)
+ throws ParseException {
+
+ // the actual parser code is in the constructor of BufferedHeader
+ return new BufferedHeader(buffer);
+ }
+
+
+ /**
+ * Helper to skip whitespace.
+ */
+ protected void skipWhitespace(final CharArrayBuffer buffer, final ParserCursor cursor) {
+ int pos = cursor.getPos();
+ int indexTo = cursor.getUpperBound();
+ while ((pos < indexTo) &&
+ HTTP.isWhitespace(buffer.charAt(pos))) {
+ pos++;
+ }
+ cursor.updatePos(pos);
+ }
+
+} // class BasicLineParser
diff --git a/src/org/apache/http/message/BasicListHeaderIterator.java b/src/org/apache/http/message/BasicListHeaderIterator.java
new file mode 100644
index 0000000..69b8c06
--- /dev/null
+++ b/src/org/apache/http/message/BasicListHeaderIterator.java
@@ -0,0 +1,196 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicListHeaderIterator.java $
+ * $Revision: 584542 $
+ * $Date: 2007-10-14 06:29:34 -0700 (Sun, 14 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+
+
+/**
+ * Implementation of a {@link HeaderIterator} based on a {@link List}.
+ * For use by {@link HeaderGroup}.
+ *
+ * @version $Revision: 584542 $
+ */
+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.
+ * <code>null</code> 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
+ * <code>null</code> for any
+ */
+ public BasicListHeaderIterator(List headers, String name) {
+ if (headers == null) {
+ throw new IllegalArgumentException
+ ("Header list must not be null.");
+ }
+
+ this.allHeaders = headers;
+ this.headerName = name;
+ this.currentIndex = findNext(-1);
+ this.lastIndex = -1;
+ }
+
+
+ /**
+ * Determines the index of the next header.
+ *
+ * @param from 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(int from) {
+ 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 <code>true</code> if the header should be part of the
+ * iteration, <code>false</code> to skip
+ */
+ protected boolean filterHeader(int index) {
+ if (this.headerName == null)
+ return true;
+
+ // non-header elements, including null, will trigger exceptions
+ final String name = ((Header)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 (Header) 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 {
+
+ if (this.lastIndex < 0) {
+ throw new IllegalStateException("No header to remove.");
+ }
+ this.allHeaders.remove(this.lastIndex);
+ this.lastIndex = -1;
+ this.currentIndex--; // adjust for the removed element
+ }
+}
diff --git a/src/org/apache/http/message/BasicNameValuePair.java b/src/org/apache/http/message/BasicNameValuePair.java
new file mode 100644
index 0000000..59fcb42
--- /dev/null
+++ b/src/org/apache/http/message/BasicNameValuePair.java
@@ -0,0 +1,189 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicNameValuePair.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.util.CharArrayBuffer;
+import org.apache.http.util.LangUtils;
+
+/**
+ * A simple class encapsulating an attribute/value pair.
+ * <p>
+ * This class comforms to the generic grammar and formatting rules outlined in the
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2">Section 2.2</a>
+ * and
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6">Section 3.6</a>
+ * of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>
+ * </p>
+ * <h>2.2 Basic Rules</h>
+ * <p>
+ * The following rules are used throughout this specification to describe basic parsing constructs.
+ * The US-ASCII coded character set is defined by ANSI X3.4-1986.
+ * </p>
+ * <pre>
+ * OCTET = <any 8-bit sequence of data>
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ * UPALPHA = <any US-ASCII uppercase letter "A".."Z">
+ * LOALPHA = <any US-ASCII lowercase letter "a".."z">
+ * ALPHA = UPALPHA | LOALPHA
+ * DIGIT = <any US-ASCII digit "0".."9">
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * CR = <US-ASCII CR, carriage return (13)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ * SP = <US-ASCII SP, space (32)>
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ * <"> = <US-ASCII double-quote mark (34)>
+ * </pre>
+ * <p>
+ * Many HTTP/1.1 header field values consist of words separated by LWS or special
+ * characters. These special characters MUST be in a quoted string to be used within
+ * a parameter value (as defined in section 3.6).
+ * <p>
+ * <pre>
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ * </pre>
+ * <p>
+ * A string of text is parsed as a single word if it is quoted using double-quote marks.
+ * </p>
+ * <pre>
+ * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext = <any TEXT except <">>
+ * </pre>
+ * <p>
+ * The backslash character ("\") MAY be used as a single-character quoting mechanism only
+ * within quoted-string and comment constructs.
+ * </p>
+ * <pre>
+ * quoted-pair = "\" CHAR
+ * </pre>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ * Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter = attribute "=" value
+ * attribute = token
+ * value = token | quoted-string
+ * </pre>
+ *
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ *
+ */
+public class BasicNameValuePair implements NameValuePair, Cloneable {
+
+ 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();
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ this.name = name;
+ 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;
+ }
+
+
+ /**
+ * Get a string representation of this pair.
+ *
+ * @return A string representation.
+ */
+ public String toString() {
+ // don't call complex default formatting for a simple toString
+
+ int len = this.name.length();
+ if (this.value != null)
+ len += 1 + this.value.length();
+ CharArrayBuffer buffer = new CharArrayBuffer(len);
+
+ buffer.append(this.name);
+ if (this.value != null) {
+ buffer.append("=");
+ buffer.append(this.value);
+ }
+ return buffer.toString();
+ }
+
+ public boolean equals(final Object object) {
+ if (object == null) return false;
+ if (this == object) return true;
+ if (object instanceof NameValuePair) {
+ BasicNameValuePair that = (BasicNameValuePair) object;
+ return this.name.equals(that.name)
+ && LangUtils.equals(this.value, that.value);
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ int hash = LangUtils.HASH_SEED;
+ hash = LangUtils.hashCode(hash, this.name);
+ hash = LangUtils.hashCode(hash, this.value);
+ return hash;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+}
diff --git a/src/org/apache/http/message/BasicRequestLine.java b/src/org/apache/http/message/BasicRequestLine.java
new file mode 100644
index 0000000..b826064
--- /dev/null
+++ b/src/org/apache/http/message/BasicRequestLine.java
@@ -0,0 +1,99 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicRequestLine.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+
+/**
+ * The first line of an {@link org.apache.http.HttpRequest HttpRequest}.
+ * It contains the method, URI, and HTTP version of the request.
+ * For details, see RFC 2616.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 604625 $
+ *
+ * @since 4.0
+ */
+public class BasicRequestLine implements RequestLine, Cloneable {
+
+ private final ProtocolVersion protoversion;
+ private final String method;
+ private final String uri;
+
+ public BasicRequestLine(final String method,
+ final String uri,
+ final ProtocolVersion version) {
+ super();
+ if (method == null) {
+ throw new IllegalArgumentException
+ ("Method must not be null.");
+ }
+ if (uri == null) {
+ throw new IllegalArgumentException
+ ("URI must not be null.");
+ }
+ if (version == null) {
+ throw new IllegalArgumentException
+ ("Protocol version must not be null.");
+ }
+ this.method = method;
+ this.uri = uri;
+ this.protoversion = version;
+ }
+
+ public String getMethod() {
+ return this.method;
+ }
+
+ public ProtocolVersion getProtocolVersion() {
+ return this.protoversion;
+ }
+
+ public String getUri() {
+ return this.uri;
+ }
+
+ public String toString() {
+ // no need for non-default formatting in toString()
+ return BasicLineFormatter.DEFAULT
+ .formatRequestLine(null, this).toString();
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+}
diff --git a/src/org/apache/http/message/BasicStatusLine.java b/src/org/apache/http/message/BasicStatusLine.java
new file mode 100644
index 0000000..c34cefe
--- /dev/null
+++ b/src/org/apache/http/message/BasicStatusLine.java
@@ -0,0 +1,124 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicStatusLine.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+
+
+
+/**
+ * Represents a status line as returned from a HTTP server.
+ * See <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a> section 6.1.
+ * This class is immutable and therefore inherently thread safe.
+ *
+ * @see HttpStatus
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *
+ * @version $Id: BasicStatusLine.java 604625 2007-12-16 14:11:11Z olegk $
+ *
+ * @since 4.0
+ */
+public class BasicStatusLine implements StatusLine, Cloneable {
+
+ // ----------------------------------------------------- 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
+ * <code>null</code>
+ */
+ public BasicStatusLine(final ProtocolVersion version, int statusCode,
+ final String reasonPhrase) {
+ super();
+ if (version == null) {
+ throw new IllegalArgumentException
+ ("Protocol version may not be null.");
+ }
+ if (statusCode < 0) {
+ throw new IllegalArgumentException
+ ("Status code may not be negative.");
+ }
+ this.protoVersion = version;
+ this.statusCode = statusCode;
+ this.reasonPhrase = reasonPhrase;
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * @return the Status-Code
+ */
+ public int getStatusCode() {
+ return this.statusCode;
+ }
+
+ /**
+ * @return the HTTP-Version
+ */
+ public ProtocolVersion getProtocolVersion() {
+ return this.protoVersion;
+ }
+
+ /**
+ * @return the Reason-Phrase
+ */
+ public String getReasonPhrase() {
+ return this.reasonPhrase;
+ }
+
+ public String toString() {
+ // no need for non-default formatting in toString()
+ return BasicLineFormatter.DEFAULT
+ .formatStatusLine(null, this).toString();
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+}
diff --git a/src/org/apache/http/message/BasicTokenIterator.java b/src/org/apache/http/message/BasicTokenIterator.java
new file mode 100644
index 0000000..5fbf5ba
--- /dev/null
+++ b/src/org/apache/http/message/BasicTokenIterator.java
@@ -0,0 +1,429 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicTokenIterator.java $
+ * $Revision: 602520 $
+ * $Date: 2007-12-08 09:42:26 -0800 (Sat, 08 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.NoSuchElementException;
+
+import org.apache.http.HeaderIterator;
+import org.apache.http.ParseException;
+import org.apache.http.TokenIterator;
+
+/**
+ * Basic implementation of a {@link TokenIterator}.
+ * This implementation parses <tt>#token<tt> sequences as
+ * defined by RFC 2616, section 2.
+ * It extends that definition somewhat beyond US-ASCII.
+ *
+ * @version $Revision: 602520 $
+ */
+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 #currentToken}.
+ * <code>null</code> 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) {
+ if (headerIterator == null) {
+ throw new IllegalArgumentException
+ ("Header iterator must not be null.");
+ }
+
+ this.headerIt = headerIterator;
+ 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 <code>null</code>.
+ *
+ * @param from 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(int from)
+ throws ParseException {
+
+ 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);
+ }
+
+ int start = findTokenStart(from);
+ if (start < 0) {
+ this.currentToken = null;
+ return -1; // nothing found
+ }
+
+ 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}.
+ * <br/>
+ * 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(String value, int start, int end) {
+ return value.substring(start, end);
+ }
+
+
+ /**
+ * Determines the starting position of the next token.
+ * This method will iterate over headers if necessary.
+ *
+ * @param from 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(int from) {
+ if (from < 0) {
+ throw new IllegalArgumentException
+ ("Search position must not be negative: " + from);
+ }
+
+ 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 from 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 <tt>#</tt>.
+ */
+ protected int findTokenSeparator(int from) {
+ if (from < 0) {
+ throw new IllegalArgumentException
+ ("Search position must not be negative: " + from);
+ }
+
+ 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 <code>from</code> does not
+ * point to a token character in the current header value.
+ */
+ protected int findTokenEnd(int from) {
+ if (from < 0) {
+ throw new IllegalArgumentException
+ ("Token start position must not be negative: " + from);
+ }
+
+ 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
+ * <tt>#token</tt> 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 <code>true</code> if the character is a token separator,
+ * <code>false</code> otherwise
+ */
+ protected boolean isTokenSeparator(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 <code>true</code> if the character is whitespace,
+ * <code>false</code> otherwise
+ */
+ protected boolean isWhitespace(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 <code>true</code> if the character is a valid token start,
+ * <code>false</code> otherwise
+ */
+ protected boolean isTokenChar(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 <code>true</code> if the character is an HTTP separator
+ */
+ protected boolean isHttpSeparator(char ch) {
+ return (HTTP_SEPARATORS.indexOf(ch) >= 0);
+ }
+
+
+} // class BasicTokenIterator
+
diff --git a/src/org/apache/http/message/BufferedHeader.java b/src/org/apache/http/message/BufferedHeader.java
new file mode 100644
index 0000000..35c5cfc
--- /dev/null
+++ b/src/org/apache/http/message/BufferedHeader.java
@@ -0,0 +1,133 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BufferedHeader.java $
+ * $Revision: 604625 $
+ * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.FormattedHeader;
+import org.apache.http.HeaderElement;
+import org.apache.http.ParseException;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * This class represents a raw HTTP header whose content is parsed 'on demand'
+ * only when the header value needs to be consumed.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 604625 $ $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $
+ */
+public class BufferedHeader implements FormattedHeader, Cloneable {
+
+ /**
+ * 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();
+ if (buffer == null) {
+ throw new IllegalArgumentException
+ ("Char array buffer may not be null");
+ }
+ int colon = buffer.indexOf(':');
+ if (colon == -1) {
+ throw new ParseException
+ ("Invalid header: " + buffer.toString());
+ }
+ 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 {
+ ParserCursor cursor = new ParserCursor(0, this.buffer.length());
+ cursor.updatePos(this.valuePos);
+ return BasicHeaderValueParser.DEFAULT
+ .parseElements(this.buffer, cursor);
+ }
+
+ public int getValuePos() {
+ return this.valuePos;
+ }
+
+ public CharArrayBuffer getBuffer() {
+ return this.buffer;
+ }
+
+ public String toString() {
+ return this.buffer.toString();
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ // buffer is considered immutable
+ // no need to make a copy of it
+ return super.clone();
+ }
+
+}
diff --git a/src/org/apache/http/message/HeaderGroup.java b/src/org/apache/http/message/HeaderGroup.java
new file mode 100644
index 0000000..4e40db1
--- /dev/null
+++ b/src/org/apache/http/message/HeaderGroup.java
@@ -0,0 +1,295 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/HeaderGroup.java $
+ * $Revision: 659185 $
+ * $Date: 2008-05-22 11:07:36 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.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.
+ *
+ * @author Michael Becke
+ *
+ * @since 4.0
+ */
+public class HeaderGroup implements Cloneable {
+
+ /** The list of headers for this group, in the order in which they were added */
+ private 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(Header header) {
+ if (header == null) {
+ return;
+ }
+ headers.add(header);
+ }
+
+ /**
+ * Removes the given header.
+ *
+ * @param header the header to remove
+ */
+ public void removeHeader(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(Header header) {
+ if (header == null) {
+ return;
+ }
+ for (int i = 0; i < this.headers.size(); i++) {
+ Header current = (Header) 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(Header[] headers) {
+ clear();
+ if (headers == null) {
+ return;
+ }
+ for (int i = 0; i < headers.length; i++) {
+ this.headers.add(headers[i]);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * <p>Header name comparison is case insensitive.
+ *
+ * @param name the name of the header(s) to get
+ * @return a header with a condensed value or <code>null</code> if no
+ * headers by the given name are present
+ */
+ public Header getCondensedHeader(String name) {
+ Header[] headers = getHeaders(name);
+
+ if (headers.length == 0) {
+ return null;
+ } else if (headers.length == 1) {
+ return headers[0];
+ } else {
+ CharArrayBuffer valueBuffer = new CharArrayBuffer(128);
+ valueBuffer.append(headers[0].getValue());
+ for (int i = 1; i < headers.length; i++) {
+ valueBuffer.append(", ");
+ valueBuffer.append(headers[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.
+ *
+ * <p>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(String name) {
+ ArrayList headersFound = new ArrayList();
+
+ for (int i = 0; i < headers.size(); i++) {
+ Header header = (Header) headers.get(i);
+ if (header.getName().equalsIgnoreCase(name)) {
+ headersFound.add(header);
+ }
+ }
+
+ return (Header[]) headersFound.toArray(new Header[headersFound.size()]);
+ }
+
+ /**
+ * Gets the first header with the given name.
+ *
+ * <p>Header name comparison is case insensitive.
+ *
+ * @param name the name of the header to get
+ * @return the first header or <code>null</code>
+ */
+ public Header getFirstHeader(String name) {
+ for (int i = 0; i < headers.size(); i++) {
+ Header header = (Header) headers.get(i);
+ if (header.getName().equalsIgnoreCase(name)) {
+ return header;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the last header with the given name.
+ *
+ * <p>Header name comparison is case insensitive.
+ *
+ * @param name the name of the header to get
+ * @return the last header or <code>null</code>
+ */
+ public Header getLastHeader(String name) {
+ // start at the end of the list and work backwards
+ for (int i = headers.size() - 1; i >= 0; i--) {
+ Header 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 (Header[]) headers.toArray(new Header[headers.size()]);
+ }
+
+ /**
+ * Tests if headers with the given name are contained within this group.
+ *
+ * <p>Header name comparison is case insensitive.
+ *
+ * @param name the header name to test for
+ * @return <code>true</code> if at least one header with the name is
+ * contained, <code>false</code> otherwise
+ */
+ public boolean containsHeader(String name) {
+ for (int i = 0; i < headers.size(); i++) {
+ Header header = (Header) 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
+ * <code>null</code> 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
+ */
+ public HeaderGroup copy() {
+ HeaderGroup clone = new HeaderGroup();
+ clone.headers.addAll(this.headers);
+ return clone;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ HeaderGroup clone = (HeaderGroup) super.clone();
+ clone.headers = new ArrayList(this.headers);
+ return clone;
+ }
+
+}
diff --git a/src/org/apache/http/message/HeaderValueFormatter.java b/src/org/apache/http/message/HeaderValueFormatter.java
new file mode 100644
index 0000000..4f6351f
--- /dev/null
+++ b/src/org/apache/http/message/HeaderValueFormatter.java
@@ -0,0 +1,141 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/HeaderValueFormatter.java $
+ * $Revision: 571954 $
+ * $Date: 2007-09-02 04:05:21 -0700 (Sun, 02 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 571954 $
+ *
+ * @since 4.0
+ */
+public interface HeaderValueFormatter {
+
+ /**
+ * Formats an array of header elements.
+ *
+ * @param buffer the buffer to append to, or
+ * <code>null</code> to create a new buffer
+ * @param elems the header elements to format
+ * @param quote <code>true</code> to always format with quoted values,
+ * <code>false</code> to use quotes only when necessary
+ *
+ * @return a buffer with the formatted header elements.
+ * If the <code>buffer</code> argument was not <code>null</code>,
+ * 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
+ * <code>null</code> to create a new buffer
+ * @param elem the header element to format
+ * @param quote <code>true</code> to always format with quoted values,
+ * <code>false</code> to use quotes only when necessary
+ *
+ * @return a buffer with the formatted header element.
+ * If the <code>buffer</code> argument was not <code>null</code>,
+ * 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 <i>not</i> generate a leading semicolon.
+ *
+ * @param buffer the buffer to append to, or
+ * <code>null</code> to create a new buffer
+ * @param nvps the parameters (name-value pairs) to format
+ * @param quote <code>true</code> to always format with quoted values,
+ * <code>false</code> to use quotes only when necessary
+ *
+ * @return a buffer with the formatted parameters.
+ * If the <code>buffer</code> argument was not <code>null</code>,
+ * 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
+ * <code>null</code> to create a new buffer
+ * @param nvp the name-value pair to format
+ * @param quote <code>true</code> to always format with a quoted value,
+ * <code>false</code> to use quotes only when necessary
+ *
+ * @return a buffer with the formatted name-value pair.
+ * If the <code>buffer</code> argument was not <code>null</code>,
+ * that buffer will be used and returned.
+ */
+ CharArrayBuffer formatNameValuePair(CharArrayBuffer buffer,
+ NameValuePair nvp,
+ boolean quote)
+ ;
+
+}
+
diff --git a/src/org/apache/http/message/HeaderValueParser.java b/src/org/apache/http/message/HeaderValueParser.java
new file mode 100644
index 0000000..74bb93c
--- /dev/null
+++ b/src/org/apache/http/message/HeaderValueParser.java
@@ -0,0 +1,214 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/HeaderValueParser.java $
+ * $Revision: 589325 $
+ * $Date: 2007-10-28 03:37:56 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.util.CharArrayBuffer;
+
+
+
+/**
+ * Interface for parsing header values into elements.
+ * Instances of this interface are expected to be stateless and thread-safe.
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 589325 $ $Date: 2007-10-28 03:37:56 -0700 (Sun, 28 Oct 2007) $
+ *
+ * @since 4.0
+ */
+public interface HeaderValueParser {
+
+ /**
+ * Parses a header value into elements.
+ * Parse errors are indicated as <code>RuntimeException</code>.
+ * <p>
+ * 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:
+ * </p>
+ * <pre>
+ * header = [ element ] *( "," [ element ] )
+ * element = name [ "=" [ value ] ] *( ";" [ param ] )
+ * param = name [ "=" [ value ] ]
+ *
+ * name = token
+ * value = ( token | quoted-string )
+ *
+ * token = 1*&lt;any char except "=", ",", ";", &lt;"&gt; and
+ * white space&gt;
+ * quoted-string = &lt;"&gt; *( text | quoted-char ) &lt;"&gt;
+ * text = any char except &lt;"&gt;
+ * quoted-char = "\" char
+ * </pre>
+ * <p>
+ * 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 <var>null</var> will be stored instead.
+ * </p>
+ *
+ * @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 <code>RuntimeException</code>.
+ * <p>
+ * This method comforms to the generic grammar and formatting rules
+ * outlined in the
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2"
+ * >Section 2.2</a>
+ * and
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6"
+ * >Section 3.6</a>
+ * of
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt">RFC 2616</a>.
+ * </p>
+ * <h>2.2 Basic Rules</h>
+ * <p>
+ * The following rules are used throughout this specification to
+ * describe basic parsing constructs.
+ * The US-ASCII coded character set is defined by ANSI X3.4-1986.
+ * </p>
+ * <pre>
+ * OCTET = <any 8-bit sequence of data>
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ * UPALPHA = <any US-ASCII uppercase letter "A".."Z">
+ * LOALPHA = <any US-ASCII lowercase letter "a".."z">
+ * ALPHA = UPALPHA | LOALPHA
+ * DIGIT = <any US-ASCII digit "0".."9">
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * CR = <US-ASCII CR, carriage return (13)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ * SP = <US-ASCII SP, space (32)>
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ * <"> = <US-ASCII double-quote mark (34)>
+ * </pre>
+ * <p>
+ * Many HTTP/1.1 header field values consist of words separated
+ * by LWS or special characters. These special characters MUST be
+ * in a quoted string to be used within
+ * a parameter value (as defined in section 3.6).
+ * <p>
+ * <pre>
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ * </pre>
+ * <p>
+ * A string of text is parsed as a single word if it is quoted using
+ * double-quote marks.
+ * </p>
+ * <pre>
+ * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext = <any TEXT except <">>
+ * </pre>
+ * <p>
+ * The backslash character ("\") MAY be used as a single-character
+ * quoting mechanism only within quoted-string and comment constructs.
+ * </p>
+ * <pre>
+ * quoted-pair = "\" CHAR
+ * </pre>
+ * <h>3.6 Transfer Codings</h>
+ * <p>
+ * Parameters are in the form of attribute/value pairs.
+ * </p>
+ * <pre>
+ * parameter = attribute "=" value
+ * attribute = token
+ * value = token | quoted-string
+ * </pre>
+ *
+ * @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 <code>null</code>
+ * if no value is specified
+ */
+ NameValuePair parseNameValuePair(
+ CharArrayBuffer buffer,
+ ParserCursor cursor) throws ParseException;
+
+}
+
diff --git a/src/org/apache/http/message/LineFormatter.java b/src/org/apache/http/message/LineFormatter.java
new file mode 100644
index 0000000..ccc603c
--- /dev/null
+++ b/src/org/apache/http/message/LineFormatter.java
@@ -0,0 +1,153 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/LineFormatter.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.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 <i>not</i> include the
+ * trailing line break sequence CR-LF.
+ * Instances of this interface are expected to be stateless and thread-safe.
+ *
+ * <p>
+ * 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
+ * <code>formatXXX</code> 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.
+ * </p>
+ *
+ *
+ * @author <a href="mailto:rolandw AT apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 573864 $ $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * @since 4.0
+ */
+public interface LineFormatter {
+
+
+
+ /**
+ * Formats a protocol version.
+ * This method does <i>not</i> follow the general contract for
+ * <code>buffer</code> arguments.
+ * It does <i>not</i> 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
+ * <code>formatProtocolVersion</code>.
+ *
+ * @param buffer a buffer to which to append, or <code>null</code>
+ * @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 <code>buffer</code> argument is not <code>null</code>,
+ * 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
+ * <code>null</code>.
+ * 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
+ * <code>null</code>.
+ * The buffer will be cleared before use.
+ * @param statline the status line to format
+ *
+ * @return the formatted status line
+ *
+ * @throws 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 <i>no</i> trailing CR-LF in the result.
+ * <br/>
+ * See the class comment for details about the buffer argument.
+ *
+ * @param buffer a buffer available for formatting, or
+ * <code>null</code>.
+ * The buffer will be cleared before use.
+ * @param header the header to format
+ *
+ * @return a buffer holding the formatted header, never <code>null</code>.
+ * The returned buffer may be different from the argument buffer.
+ *
+ * @throws ParseException in case of a parse error
+ */
+ CharArrayBuffer formatHeader(CharArrayBuffer buffer,
+ Header header)
+ ;
+
+}
diff --git a/src/org/apache/http/message/LineParser.java b/src/org/apache/http/message/LineParser.java
new file mode 100644
index 0000000..d1bcd15
--- /dev/null
+++ b/src/org/apache/http/message/LineParser.java
@@ -0,0 +1,156 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/LineParser.java $
+ * $Revision: 589374 $
+ * $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ParseException;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.Header;
+import org.apache.http.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.
+ *
+ * @author <a href="mailto:rolandw AT apache.org">Roland Weber</a>
+ *
+ *
+ * <!-- empty lines above to avoid 'svn diff' context problems -->
+ * @version $Revision: 589374 $ $Date: 2007-10-28 09:25:07 -0700 (Sun, 28 Oct 2007) $
+ *
+ * @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 <i>heuristic</i> to check for a
+ * likely protocol version specification. It does <i>not</i>
+ * 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 <code>true</code> if there is a protocol version at the
+ * argument index (possibly ignoring whitespace),
+ * <code>false</code> 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/src/org/apache/http/message/ParserCursor.java b/src/org/apache/http/message/ParserCursor.java
new file mode 100644
index 0000000..d030675
--- /dev/null
+++ b/src/org/apache/http/message/ParserCursor.java
@@ -0,0 +1,102 @@
+/*
+ * $HeadURL:https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/ParserCursor.java $
+ * $Revision:589325 $
+ * $Date:2007-10-28 11:37:56 +0100 (Sun, 28 Oct 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.message;
+
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * This class represents a context of a parsing operation:
+ * <ul>
+ * <li>the current position the parsing operation is expected to start at</li>
+ * <li>the bounds limiting the scope of the parsing operation</li>
+ * </ul>
+ *
+ * @author <a href="mailto:oleg at ural.com">Oleg Kalnichevski</a>
+ */
+public class ParserCursor {
+
+ private final int lowerBound;
+ private final int upperBound;
+ private int pos;
+
+ public ParserCursor(int lowerBound, 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(int pos) {
+ if (pos < this.lowerBound) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (pos > this.upperBound) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.pos = pos;
+ }
+
+ public boolean atEnd() {
+ return this.pos >= this.upperBound;
+ }
+
+ public String toString() {
+ CharArrayBuffer buffer = new CharArrayBuffer(16);
+ 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/src/org/apache/http/message/package.html b/src/org/apache/http/message/package.html
new file mode 100644
index 0000000..88ee617
--- /dev/null
+++ b/src/org/apache/http/message/package.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/package.html $
+ * $Revision: 539755 $
+ * $Date: 2007-05-19 07:05:02 -0700 (Sat, 19 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+A selection of HTTP
+{@link org.apache.http.message.AbstractHttpMessage message}
+implementations.
+
+There are basic implementations for HTTP requests
+{@link org.apache.http.message.BasicHttpEntityEnclosingRequest with}
+and {@link org.apache.http.message.BasicHttpRequest without}
+an entity, and for
+{@link org.apache.http.message.BasicHttpResponse responses}.
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/package.html b/src/org/apache/http/package.html
new file mode 100644
index 0000000..e9647b4
--- /dev/null
+++ b/src/org/apache/http/package.html
@@ -0,0 +1,51 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The core interfaces and classes of the HTTP components.
+
+These deal with the fundamental things required for using the
+HTTP protocol, such as representing a
+{@link org.apache.http.HttpMessage message} including it's
+{@link org.apache.http.Header headers} and optional
+{@link org.apache.http.HttpEntity entity}, and
+{@link org.apache.http.HttpConnection connections}
+over which messages are sent. In order to prepare messages
+before sending or after receiving, there are interceptors for
+{@link org.apache.http.HttpRequestInterceptor requests} and
+{@link org.apache.http.HttpResponseInterceptor responses}.
+
+</body>
+</html>
diff --git a/src/org/apache/http/params/AbstractHttpParams.java b/src/org/apache/http/params/AbstractHttpParams.java
new file mode 100644
index 0000000..91631fc
--- /dev/null
+++ b/src/org/apache/http/params/AbstractHttpParams.java
@@ -0,0 +1,116 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/AbstractHttpParams.java $
+ * $Revision: 542224 $
+ * $Date: 2007-05-28 06:34:04 -0700 (Mon, 28 May 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.params.HttpParams;
+
+
+/**
+ * Abstract base class for parameter collections.
+ * Type specific setters and getters are mapped to the abstract,
+ * generic getters and setters.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 542224 $
+ */
+public abstract class AbstractHttpParams implements HttpParams {
+
+ /**
+ * Instantiates parameters.
+ */
+ protected AbstractHttpParams() {
+ super();
+ }
+
+ public long getLongParameter(final String name, long defaultValue) {
+ Object param = getParameter(name);
+ if (param == null) {
+ return defaultValue;
+ }
+ return ((Long)param).longValue();
+ }
+
+ public HttpParams setLongParameter(final String name, long value) {
+ setParameter(name, new Long(value));
+ return this;
+ }
+
+ public int getIntParameter(final String name, int defaultValue) {
+ Object param = getParameter(name);
+ if (param == null) {
+ return defaultValue;
+ }
+ return ((Integer)param).intValue();
+ }
+
+ public HttpParams setIntParameter(final String name, int value) {
+ setParameter(name, new Integer(value));
+ return this;
+ }
+
+ public double getDoubleParameter(final String name, double defaultValue) {
+ Object param = getParameter(name);
+ if (param == null) {
+ return defaultValue;
+ }
+ return ((Double)param).doubleValue();
+ }
+
+ public HttpParams setDoubleParameter(final String name, double value) {
+ setParameter(name, new Double(value));
+ return this;
+ }
+
+ public boolean getBooleanParameter(final String name, boolean defaultValue) {
+ Object param = getParameter(name);
+ if (param == null) {
+ return defaultValue;
+ }
+ return ((Boolean)param).booleanValue();
+ }
+
+ public HttpParams setBooleanParameter(final String name, 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);
+ }
+
+} // class AbstractHttpParams
diff --git a/src/org/apache/http/params/BasicHttpParams.java b/src/org/apache/http/params/BasicHttpParams.java
new file mode 100644
index 0000000..70e6605
--- /dev/null
+++ b/src/org/apache/http/params/BasicHttpParams.java
@@ -0,0 +1,160 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/BasicHttpParams.java $
+ * $Revision: 610464 $
+ * $Date: 2008-01-09 09:10:55 -0800 (Wed, 09 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * This class represents a collection of HTTP protocol parameters.
+ * Protocol parameters may be linked together to form a hierarchy.
+ * If a particular parameter value has not been explicitly defined
+ * in the collection itself, its value will be drawn from the parent
+ * collection of parameters.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 610464 $
+ */
+public final class BasicHttpParams extends AbstractHttpParams
+ implements Serializable, Cloneable {
+
+ private static final long serialVersionUID = -7086398485908701455L;
+
+ /** Map of HTTP parameters that this collection contains. */
+ private HashMap parameters;
+
+ public BasicHttpParams() {
+ super();
+ }
+
+ public Object getParameter(final String name) {
+ // See if the parameter has been explicitly defined
+ Object param = null;
+ if (this.parameters != null) {
+ param = this.parameters.get(name);
+ }
+ return param;
+ }
+
+ public HttpParams setParameter(final String name, final Object value) {
+ if (this.parameters == null) {
+ this.parameters = new HashMap();
+ }
+ this.parameters.put(name, value);
+ return this;
+ }
+
+ public boolean removeParameter(String name) {
+ if (this.parameters == null) {
+ return false;
+ }
+ //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 name
+ * @param value parameter value
+ */
+ public void setParameters(final String[] names, final Object value) {
+ for (int i = 0; i < names.length; i++) {
+ setParameter(names[i], value);
+ }
+ }
+
+ public boolean isParameterSet(final String name) {
+ return getParameter(name) != null;
+ }
+
+ public boolean isParameterSetLocally(final String name) {
+ return this.parameters != null && this.parameters.get(name) != null;
+ }
+
+ /**
+ * Removes all parameters from this collection.
+ */
+ public void clear() {
+ this.parameters = null;
+ }
+
+ /**
+ * Creates a copy of these parameters.
+ * The implementation here instantiates {@link BasicHttpParams},
+ * then calls {@link #copyParams(HttpParams)} to populate the copy.
+ *
+ * @return a new set of params holding a copy of the
+ * <i>local</i> parameters in this object.
+ */
+ public HttpParams copy() {
+ BasicHttpParams clone = new BasicHttpParams();
+ copyParams(clone);
+ return clone;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ BasicHttpParams clone = (BasicHttpParams) super.clone();
+ copyParams(clone);
+ return clone;
+ }
+
+ /**
+ * Copies the locally defined parameters to the argument parameters.
+ * This method is called from {@link #copy()}.
+ *
+ * @param target the parameters to which to copy
+ */
+ protected void copyParams(HttpParams target) {
+ if (this.parameters == null)
+ return;
+
+ Iterator iter = parameters.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry me = (Map.Entry) iter.next();
+ if (me.getKey() instanceof String)
+ target.setParameter((String)me.getKey(), me.getValue());
+ }
+ }
+
+}
diff --git a/src/org/apache/http/params/CoreConnectionPNames.java b/src/org/apache/http/params/CoreConnectionPNames.java
new file mode 100644
index 0000000..a2dec8b
--- /dev/null
+++ b/src/org/apache/http/params/CoreConnectionPNames.java
@@ -0,0 +1,131 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/CoreConnectionPNames.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+
+/**
+ * Defines parameter names for connections in HttpCore.
+ *
+ * @version $Revision: 576077 $
+ *
+ * @since 4.0
+ */
+public interface CoreConnectionPNames {
+
+ /**
+ * Defines the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the
+ * timeout for waiting for data. A timeout value of zero is interpreted as an infinite
+ * timeout. This value is used when no socket timeout is set in the
+ * method parameters.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ * @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.
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ * @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.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ */
+ 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 <tt>0</tt> implies that the option is disabled.
+ * Value <tt>-1</tt> implies that the JRE default is used. The setting only affects
+ * socket close.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ * @see java.net.SocketOptions#SO_LINGER
+ */
+ public static final String SO_LINGER = "http.socket.linger";
+
+ /**
+ * Determines the timeout until a connection is etablished. A value of zero
+ * means the timeout is not used. The default value is zero.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ */
+ public static final String CONNECTION_TIMEOUT = "http.connection.timeout";
+
+ /**
+ * Determines whether stale connection check is to be used. Disabling
+ * stale connection check may result in slight performance improvement
+ * at the risk of getting an I/O error when executing a request over a
+ * connection that has been closed at the server side.
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ 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.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ */
+ 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.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ */
+ public static final String MAX_HEADER_COUNT = "http.connection.max-header-count";
+
+}
diff --git a/src/org/apache/http/params/CoreProtocolPNames.java b/src/org/apache/http/params/CoreProtocolPNames.java
new file mode 100644
index 0000000..a42c5de
--- /dev/null
+++ b/src/org/apache/http/params/CoreProtocolPNames.java
@@ -0,0 +1,132 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/CoreProtocolPNames.java $
+ * $Revision: 576077 $
+ * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+
+/**
+ * Defines parameter names for protocol execution in HttpCore.
+ *
+ * @version $Revision: 576077 $
+ *
+ * @since 4.0
+ */
+public interface CoreProtocolPNames {
+
+ /**
+ * Defines the {@link org.apache.http.ProtocolVersion protocol version}
+ * used per default.
+ * <p>
+ * This parameter expects a value of type
+ * {@link org.apache.http.ProtocolVersion}.
+ * </p>
+ */
+ public static final String PROTOCOL_VERSION = "http.protocol.version";
+
+ /**
+ * Defines the charset to be used for encoding HTTP protocol elements.
+ * <p>
+ * This parameter expects a value of type {@link String}.
+ * </p>
+ */
+ public static final String HTTP_ELEMENT_CHARSET = "http.protocol.element-charset";
+
+ /**
+ * Defines the charset to be used per default for encoding content body.
+ * <p>
+ * This parameter expects a value of type {@link String}.
+ * </p>
+ */
+ public static final String HTTP_CONTENT_CHARSET = "http.protocol.content-charset";
+
+ /**
+ * Defines the content of the <tt>User-Agent</tt> header.
+ * <p>
+ * This parameter expects a value of type {@link String}.
+ * </p>
+ */
+ public static final String USER_AGENT = "http.useragent";
+
+ /**
+ * Defines the content of the <tt>Server</tt> header.
+ * <p>
+ * This parameter expects a value of type {@link String}.
+ * </p>
+ */
+ public static final String ORIGIN_SERVER = "http.origin-server";
+
+ /**
+ * Defines whether responses with an invalid <tt>Transfer-Encoding</tt> header should be
+ * rejected.
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ public static final String STRICT_TRANSFER_ENCODING = "http.protocol.strict-transfer-encoding";
+
+ /**
+ * <p>
+ * Activates 'Expect: 100-Continue' handshake for the
+ * entity enclosing methods. The purpose of the 'Expect: 100-Continue'
+ * handshake 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.
+ * </p>
+ *
+ * <p>
+ * The use of the 'Expect: 100-continue' handshake can result in
+ * noticable peformance improvement for entity enclosing requests
+ * (such as POST and PUT) that require the target server's
+ * authentication.
+ * </p>
+ *
+ * <p>
+ * '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.
+ * </p>
+ *
+ * This parameter expects a value of type {@link Boolean}.
+ */
+ public static final String USE_EXPECT_CONTINUE = "http.protocol.expect-continue";
+
+ /**
+ * <p>
+ * Defines the maximum period of time in milliseconds the client should spend
+ * waiting for a 100-continue response.
+ * </p>
+ *
+ * This parameter expects a value of type {@link Integer}.
+ */
+ public static final String WAIT_FOR_CONTINUE = "http.protocol.wait-for-continue";
+
+}
diff --git a/src/org/apache/http/params/DefaultedHttpParams.java b/src/org/apache/http/params/DefaultedHttpParams.java
new file mode 100644
index 0000000..ce33247
--- /dev/null
+++ b/src/org/apache/http/params/DefaultedHttpParams.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/DefaultedHttpParams.java $
+ * $Revision: 610763 $
+ * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * {@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.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 610763 $
+ */
+public final class DefaultedHttpParams extends AbstractHttpParams {
+
+ private final HttpParams local;
+ private final HttpParams defaults;
+
+ public DefaultedHttpParams(final HttpParams local, final HttpParams defaults) {
+ super();
+ if (local == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ this.local = local;
+ this.defaults = defaults;
+ }
+
+ /**
+ * Creates a copy of the local collection with the same default
+ */
+ public HttpParams copy() {
+ 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
+ * <i>does not</i> modify the default collection.
+ */
+ public boolean removeParameter(final String name) {
+ return this.local.removeParameter(name);
+ }
+
+ /**
+ * Sets the parameter in the local collection. This method <i>does not</i>
+ * modify the default collection.
+ */
+ public HttpParams setParameter(final String name, final Object value) {
+ return this.local.setParameter(name, value);
+ }
+
+ public HttpParams getDefaults() {
+ return this.defaults;
+ }
+
+}
diff --git a/src/org/apache/http/params/HttpAbstractParamBean.java b/src/org/apache/http/params/HttpAbstractParamBean.java
new file mode 100644
index 0000000..8701a99
--- /dev/null
+++ b/src/org/apache/http/params/HttpAbstractParamBean.java
@@ -0,0 +1,44 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpAbstractParamBean.java $
+ * $Revision: 593937 $
+ * $Date: 2007-11-11 10:44:12 -0800 (Sun, 11 Nov 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+public abstract class HttpAbstractParamBean {
+
+ protected final HttpParams params;
+
+ public HttpAbstractParamBean (final HttpParams params) {
+ if (params == null)
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ this.params = params;
+ }
+
+}
diff --git a/src/org/apache/http/params/HttpConnectionParamBean.java b/src/org/apache/http/params/HttpConnectionParamBean.java
new file mode 100644
index 0000000..0b61346
--- /dev/null
+++ b/src/org/apache/http/params/HttpConnectionParamBean.java
@@ -0,0 +1,64 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpConnectionParamBean.java $
+ * $Revision: 593937 $
+ * $Date: 2007-11-11 10:44:12 -0800 (Sun, 11 Nov 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+public class HttpConnectionParamBean extends HttpAbstractParamBean {
+
+ public HttpConnectionParamBean (final HttpParams params) {
+ super(params);
+ }
+
+ public void setSoTimeout (int soTimeout) {
+ HttpConnectionParams.setSoTimeout(params, soTimeout);
+ }
+
+ public void setTcpNoDelay (boolean tcpNoDelay) {
+ HttpConnectionParams.setTcpNoDelay(params, tcpNoDelay);
+ }
+
+ public void setSocketBufferSize (int socketBufferSize) {
+ HttpConnectionParams.setSocketBufferSize(params, socketBufferSize);
+ }
+
+ public void setLinger (int linger) {
+ HttpConnectionParams.setLinger(params, linger);
+ }
+
+ public void setConnectionTimeout (int connectionTimeout) {
+ HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
+ }
+
+ public void setStaleCheckingEnabled (boolean staleCheckingEnabled) {
+ HttpConnectionParams.setStaleCheckingEnabled(params, staleCheckingEnabled);
+ }
+
+}
diff --git a/src/org/apache/http/params/HttpConnectionParams.java b/src/org/apache/http/params/HttpConnectionParams.java
new file mode 100644
index 0000000..7918a66
--- /dev/null
+++ b/src/org/apache/http/params/HttpConnectionParams.java
@@ -0,0 +1,224 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpConnectionParams.java $
+ * $Revision: 576089 $
+ * $Date: 2007-09-16 05:39:56 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+/**
+ * An adaptor for accessing connection parameters in {@link HttpParams}.
+ * <br/>
+ * Note that the <i>implements</i> relation to {@link CoreConnectionPNames}
+ * is for compatibility with existing application code only. References to
+ * the parameter names should use the interface, not this class.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576089 $
+ *
+ * @since 4.0
+ */
+public final class HttpConnectionParams implements CoreConnectionPNames {
+
+ /**
+ */
+ private HttpConnectionParams() {
+ super();
+ }
+
+ /**
+ * Returns the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the
+ * timeout for waiting for data. A timeout value of zero is interpreted as an infinite
+ * timeout. This value is used when no socket timeout is set in the
+ * method parameters.
+ *
+ * @return timeout in milliseconds
+ */
+ public static int getSoTimeout(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0);
+ }
+
+ /**
+ * Sets the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the
+ * timeout for waiting for data. A timeout value of zero is interpreted as an infinite
+ * timeout. This value is used when no socket timeout is set in the
+ * method parameters.
+ *
+ * @param timeout Timeout in milliseconds
+ */
+ public static void setSoTimeout(final HttpParams params, int timeout) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
+
+ }
+
+ /**
+ * Tests if Nagle's algorithm is to be used.
+ *
+ * @return <tt>true</tt> if the Nagle's algorithm is to NOT be used
+ * (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
+ */
+ public static boolean getTcpNoDelay(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getBooleanParameter
+ (CoreConnectionPNames.TCP_NODELAY, true);
+ }
+
+ /**
+ * 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.
+ *
+ * @param value <tt>true</tt> if the Nagle's algorithm is to NOT be used
+ * (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
+ */
+ public static void setTcpNoDelay(final HttpParams params, boolean value) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, value);
+ }
+
+ public static int getSocketBufferSize(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getIntParameter
+ (CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1);
+ }
+
+ public static void setSocketBufferSize(final HttpParams params, int size) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, size);
+ }
+
+ /**
+ * Returns linger-on-close timeout. Value <tt>0</tt> implies that the option is
+ * disabled. Value <tt>-1</tt> implies that the JRE default is used.
+ *
+ * @return the linger-on-close timeout
+ */
+ public static int getLinger(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1);
+ }
+
+ /**
+ * Returns linger-on-close timeout. This option disables/enables immediate return
+ * from a close() of a TCP Socket. Enabling this option with a non-zero Integer
+ * timeout means that a close() will block pending the transmission and
+ * acknowledgement of all data written to the peer, at which point the socket is
+ * closed gracefully. Value <tt>0</tt> implies that the option is
+ * disabled. Value <tt>-1</tt> implies that the JRE default is used.
+ *
+ * @param value the linger-on-close timeout
+ */
+ public static void setLinger(final HttpParams params, int value) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setIntParameter(CoreConnectionPNames.SO_LINGER, value);
+ }
+
+ /**
+ * Returns the timeout until a connection is etablished. A value of zero
+ * means the timeout is not used. The default value is zero.
+ *
+ * @return timeout in milliseconds.
+ */
+ public static int getConnectionTimeout(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getIntParameter
+ (CoreConnectionPNames.CONNECTION_TIMEOUT, 0);
+ }
+
+ /**
+ * Sets the timeout until a connection is etablished. A value of zero
+ * means the timeout is not used. The default value is zero.
+ *
+ * @param timeout Timeout in milliseconds.
+ */
+ public static void setConnectionTimeout(final HttpParams params, int timeout) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setIntParameter
+ (CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
+ }
+
+ /**
+ * Tests whether stale connection check is to be used. Disabling
+ * stale connection check may result in slight performance improvement
+ * at the risk of getting an I/O error when executing a request over a
+ * connection that has been closed at the server side.
+ *
+ * @return <tt>true</tt> if stale connection check is to be used,
+ * <tt>false</tt> otherwise.
+ */
+ public static boolean isStaleCheckingEnabled(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getBooleanParameter
+ (CoreConnectionPNames.STALE_CONNECTION_CHECK, true);
+ }
+
+ /**
+ * Defines whether stale connection check is to be used. Disabling
+ * stale connection check may result in slight performance improvement
+ * at the risk of getting an I/O error when executing a request over a
+ * connection that has been closed at the server side.
+ *
+ * @param value <tt>true</tt> if stale connection check is to be used,
+ * <tt>false</tt> otherwise.
+ */
+ public static void setStaleCheckingEnabled(final HttpParams params, boolean value) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setBooleanParameter
+ (CoreConnectionPNames.STALE_CONNECTION_CHECK, value);
+ }
+
+}
diff --git a/src/org/apache/http/params/HttpParams.java b/src/org/apache/http/params/HttpParams.java
new file mode 100644
index 0000000..ba901a2
--- /dev/null
+++ b/src/org/apache/http/params/HttpParams.java
@@ -0,0 +1,187 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpParams.java $
+ * $Revision: 610763 $
+ * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+/**
+ * Represents a collection of HTTP protocol and framework parameters.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 610763 $
+ *
+ * @since 4.0
+ */
+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,
+ * <code>null</code> if the parameter is not set or if it
+ * is explicitly set to <code>null</code>
+ *
+ * @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 <code>true</code>.
+ *
+ * @param name parameter name
+ *
+ * @return <tt>true</tt> if the parameter is set to value <tt>true</tt>,
+ * <tt>false</tt> if it is not set or set to <code>false</code>
+ */
+ boolean isParameterTrue(String name);
+
+ /**
+ * Checks if a boolean parameter is not set or <code>false</code>.
+ *
+ * @param name parameter name
+ *
+ * @return <tt>true</tt> if the parameter is either not set or
+ * set to value <tt>false</tt>,
+ * <tt>false</tt> if it is set to <code>true</code>
+ */
+ boolean isParameterFalse(String name);
+
+}
diff --git a/src/org/apache/http/params/HttpProtocolParamBean.java b/src/org/apache/http/params/HttpProtocolParamBean.java
new file mode 100644
index 0000000..6273430
--- /dev/null
+++ b/src/org/apache/http/params/HttpProtocolParamBean.java
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpProtocolParamBean.java $
+ * $Revision: 593937 $
+ * $Date: 2007-11-11 10:44:12 -0800 (Sun, 11 Nov 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.HttpVersion;
+
+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 (boolean useExpectContinue) {
+ HttpProtocolParams.setUseExpectContinue(params, useExpectContinue);
+ }
+
+}
diff --git a/src/org/apache/http/params/HttpProtocolParams.java b/src/org/apache/http/params/HttpProtocolParams.java
new file mode 100644
index 0000000..71e9c67
--- /dev/null
+++ b/src/org/apache/http/params/HttpProtocolParams.java
@@ -0,0 +1,176 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpProtocolParams.java $
+ * $Revision: 576089 $
+ * $Date: 2007-09-16 05:39:56 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.params;
+
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * This class implements an adaptor around the {@link HttpParams} interface
+ * to simplify manipulation of the HTTP protocol specific parameters.
+ * <br/>
+ * Note that the <i>implements</i> relation to {@link CoreProtocolPNames}
+ * is for compatibility with existing application code only. References to
+ * the parameter names should use the interface, not this class.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576089 $
+ *
+ * @since 4.0
+ *
+ * @see CoreProtocolPNames
+ */
+public final class HttpProtocolParams implements CoreProtocolPNames {
+
+ /**
+ */
+ private HttpProtocolParams() {
+ super();
+ }
+
+ /**
+ * Returns the charset to be used for writing HTTP headers.
+ * @return The charset
+ */
+ public static String getHttpElementCharset(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ String charset = (String) params.getParameter
+ (CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
+ if (charset == null) {
+ charset = HTTP.DEFAULT_PROTOCOL_CHARSET;
+ }
+ return charset;
+ }
+
+ /**
+ * Sets the charset to be used for writing HTTP headers.
+ * @param charset The charset
+ */
+ public static void setHttpElementCharset(final HttpParams params, final String charset) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, charset);
+ }
+
+ /**
+ * Returns the default charset to be used for writing content body,
+ * when no charset explicitly specified.
+ * @return The charset
+ */
+ public static String getContentCharset(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ String charset = (String) params.getParameter
+ (CoreProtocolPNames.HTTP_CONTENT_CHARSET);
+ if (charset == null) {
+ charset = HTTP.DEFAULT_CONTENT_CHARSET;
+ }
+ return charset;
+ }
+
+ /**
+ * Sets the default charset to be used for writing content body,
+ * when no charset explicitly specified.
+ * @param charset The charset
+ */
+ public static void setContentCharset(final HttpParams params, final String charset) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, charset);
+ }
+
+ /**
+ * Returns {@link ProtocolVersion protocol version} to be used per default.
+ *
+ * @return {@link ProtocolVersion protocol version}
+ */
+ public static ProtocolVersion getVersion(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ Object param = params.getParameter
+ (CoreProtocolPNames.PROTOCOL_VERSION);
+ if (param == null) {
+ return HttpVersion.HTTP_1_1;
+ }
+ return (ProtocolVersion)param;
+ }
+
+ /**
+ * Assigns the {@link ProtocolVersion protocol version} to be used by the
+ * HTTP methods that this collection of parameters applies to.
+ *
+ * @param version the {@link ProtocolVersion protocol version}
+ */
+ public static void setVersion(final HttpParams params, final ProtocolVersion version) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, version);
+ }
+
+ public static String getUserAgent(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return (String) params.getParameter(CoreProtocolPNames.USER_AGENT);
+ }
+
+ public static void setUserAgent(final HttpParams params, final String useragent) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setParameter(CoreProtocolPNames.USER_AGENT, useragent);
+ }
+
+ public static boolean useExpectContinue(final HttpParams params) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ return params.getBooleanParameter
+ (CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
+ }
+
+ public static void setUseExpectContinue(final HttpParams params, boolean b) {
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ params.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, b);
+ }
+}
diff --git a/src/org/apache/http/params/package.html b/src/org/apache/http/params/package.html
new file mode 100644
index 0000000..3943053
--- /dev/null
+++ b/src/org/apache/http/params/package.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+The parameterization framework for HTTP components.
+
+</body>
+</html>
diff --git a/src/org/apache/http/protocol/BasicHttpContext.java b/src/org/apache/http/protocol/BasicHttpContext.java
new file mode 100644
index 0000000..9b4e2b3
--- /dev/null
+++ b/src/org/apache/http/protocol/BasicHttpContext.java
@@ -0,0 +1,95 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/BasicHttpContext.java $
+ * $Revision: 654882 $
+ * $Date: 2008-05-09 09:58:59 -0700 (Fri, 09 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Default implementation of the {@link HttpContext HttpContext}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 654882 $
+ *
+ * @since 4.0
+ */
+public class BasicHttpContext implements HttpContext {
+
+ private final HttpContext parentContext;
+ private Map map = null;
+
+ public BasicHttpContext() {
+ this(null);
+ }
+
+ public BasicHttpContext(final HttpContext parentContext) {
+ super();
+ this.parentContext = parentContext;
+ }
+
+ public Object getAttribute(final String id) {
+ if (id == null) {
+ throw new IllegalArgumentException("Id may not be null");
+ }
+ Object obj = null;
+ if (this.map != null) {
+ 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) {
+ if (id == null) {
+ throw new IllegalArgumentException("Id may not be null");
+ }
+ if (this.map == null) {
+ this.map = new HashMap();
+ }
+ this.map.put(id, obj);
+ }
+
+ public Object removeAttribute(final String id) {
+ if (id == null) {
+ throw new IllegalArgumentException("Id may not be null");
+ }
+ if (this.map != null) {
+ return this.map.remove(id);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/BasicHttpProcessor.java b/src/org/apache/http/protocol/BasicHttpProcessor.java
new file mode 100644
index 0000000..3caec72
--- /dev/null
+++ b/src/org/apache/http/protocol/BasicHttpProcessor.java
@@ -0,0 +1,337 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/BasicHttpProcessor.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+
+/**
+ * Keeps lists of interceptors for processing requests and responses.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Andrea Selva
+ *
+ * @version $Revision: 613298 $
+ *
+ * @since 4.0
+ */
+public final class BasicHttpProcessor implements
+ HttpProcessor, HttpRequestInterceptorList, HttpResponseInterceptorList, Cloneable {
+
+ protected List requestInterceptors = null;
+ protected List responseInterceptors = null;
+
+
+ // non-Javadoc, see interface HttpRequestInterceptorList
+ public void addRequestInterceptor(final HttpRequestInterceptor itcp) {
+
+ if (itcp == null) {
+ return;
+ }
+ if (this.requestInterceptors == null) {
+ this.requestInterceptors = new ArrayList();
+ }
+ this.requestInterceptors.add(itcp);
+ }
+
+ // non-Javadoc, see interface HttpRequestInterceptorList
+ public void addRequestInterceptor(final HttpRequestInterceptor itcp,
+ int index) {
+ if (index < 0) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ if (itcp == null) {
+ return;
+ }
+
+ if (this.requestInterceptors == null) {
+ if (index > 0) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ this.requestInterceptors = new ArrayList();
+ }
+ this.requestInterceptors.add(index, itcp);
+ }
+
+
+ public void addResponseInterceptor(HttpResponseInterceptor itcp,
+ int index) {
+ if (index < 0) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ if (itcp == null) {
+ return;
+ }
+
+ if (this.responseInterceptors == null) {
+ if (index > 0) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ this.responseInterceptors = new ArrayList();
+ }
+ this.responseInterceptors.add(index, itcp);
+ }
+
+
+ // non-Javadoc, see interface HttpRequestInterceptorList
+ public void removeRequestInterceptorByClass(final Class clazz) {
+ if (this.requestInterceptors == null) {
+ return;
+ }
+ for (Iterator it = this.requestInterceptors.iterator();
+ it.hasNext(); ) {
+ Object request = it.next();
+ if (request.getClass().equals(clazz)) {
+ it.remove();
+ }
+ }
+ }
+
+ // non-Javadoc, see interface HttpResponseInterceptorList
+ public void removeResponseInterceptorByClass(final Class clazz) {
+ if (this.responseInterceptors == null) {
+ return;
+ }
+ for (Iterator it = this.responseInterceptors.iterator();
+ it.hasNext(); ) {
+ Object request = it.next();
+ if (request.getClass().equals(clazz)) {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * Same as {@link #addRequestInterceptor(HttpRequestInterceptor) addRequestInterceptor}.
+ *
+ * @param interceptor the interceptor to add
+ */
+ public final
+ void addInterceptor(final HttpRequestInterceptor interceptor) {
+ addRequestInterceptor(interceptor);
+ }
+
+ public final
+ void addInterceptor(final HttpRequestInterceptor interceptor,
+ int index) {
+ addRequestInterceptor(interceptor, index);
+ }
+
+
+ // non-Javadoc, see interface HttpRequestInterceptorList
+ public int getRequestInterceptorCount() {
+ return (this.requestInterceptors == null) ?
+ 0 : this.requestInterceptors.size();
+ }
+
+
+ // non-Javadoc, see interface HttpRequestInterceptorList
+ public HttpRequestInterceptor getRequestInterceptor(int index) {
+
+ if ((this.requestInterceptors == null) ||
+ (index < 0) || (index >= this.requestInterceptors.size()))
+ return null;
+
+ return (HttpRequestInterceptor) this.requestInterceptors.get(index);
+ }
+
+
+ // non-Javadoc, see interface HttpRequestInterceptorList
+ public void clearRequestInterceptors() {
+ this.requestInterceptors = null;
+ }
+
+
+
+ // non-Javadoc, see interface HttpResponseInterceptorList
+ public void addResponseInterceptor(final HttpResponseInterceptor itcp) {
+ if (itcp == null) {
+ return;
+ }
+ if (this.responseInterceptors == null) {
+ this.responseInterceptors = new ArrayList();
+ }
+ this.responseInterceptors.add(itcp);
+ }
+
+ /**
+ * Same as {@link #addResponseInterceptor(HttpResponseInterceptor) addResponseInterceptor}.
+ *
+ * @param interceptor the interceptor to add
+ */
+ public final
+ void addInterceptor(final HttpResponseInterceptor interceptor) {
+ addResponseInterceptor(interceptor);
+ }
+
+ public final void addInterceptor(final HttpResponseInterceptor interceptor,
+ int index) {
+ addResponseInterceptor(interceptor, index);
+ }
+
+
+
+ // non-Javadoc, see interface HttpResponseInterceptorList
+ public int getResponseInterceptorCount() {
+ return (this.responseInterceptors == null) ?
+ 0 : this.responseInterceptors.size();
+ }
+
+
+ // non-Javadoc, see interface HttpResponseInterceptorList
+ public HttpResponseInterceptor getResponseInterceptor(int index) {
+
+ if ((this.responseInterceptors == null) ||
+ (index < 0) || (index >= this.responseInterceptors.size()))
+ return null;
+
+ return (HttpResponseInterceptor) this.responseInterceptors.get(index);
+ }
+
+
+ // non-Javadoc, see interface HttpResponseInterceptorList
+ public void clearResponseInterceptors() {
+ this.responseInterceptors = null;
+ }
+
+
+ /**
+ * 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) {
+ if (list == null) {
+ throw new IllegalArgumentException("List must not be null.");
+ }
+ if (this.requestInterceptors != null) {
+ this.requestInterceptors.clear();
+ }
+ if (this.responseInterceptors != null) {
+ this.responseInterceptors.clear();
+ }
+ for (int i = 0; i < list.size(); i++) {
+ Object obj = list.get(i);
+ 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();
+ }
+
+ // non-Javadoc, see interface HttpRequestInterceptor (via HttpProcessor)
+ public void process(
+ final HttpRequest request,
+ final HttpContext context)
+ throws IOException, HttpException {
+ if (this.requestInterceptors != null) {
+ for (int i = 0; i < this.requestInterceptors.size(); i++) {
+ HttpRequestInterceptor interceptor =
+ (HttpRequestInterceptor) this.requestInterceptors.get(i);
+ interceptor.process(request, context);
+ }
+ }
+ }
+
+ // non-Javadoc, see interface HttpResponseInterceptor (via HttpProcessor)
+ public void process(
+ final HttpResponse response,
+ final HttpContext context)
+ throws IOException, HttpException {
+ if (this.responseInterceptors != null) {
+ for (int i = 0; i < this.responseInterceptors.size(); i++) {
+ HttpResponseInterceptor interceptor =
+ (HttpResponseInterceptor) this.responseInterceptors.get(i);
+ interceptor.process(response, context);
+ }
+ }
+ }
+
+ protected void copyInterceptors(final BasicHttpProcessor target) {
+ if (this.requestInterceptors != null) {
+ target.requestInterceptors =
+ new ArrayList(this.requestInterceptors);
+ }
+ if (this.responseInterceptors != null) {
+ target.responseInterceptors =
+ new ArrayList(this.responseInterceptors);
+ }
+ }
+
+ /**
+ * Creates a copy of this instance
+ *
+ * @return new instance of the BasicHttpProcessor
+ */
+ public BasicHttpProcessor copy() {
+ BasicHttpProcessor clone = new BasicHttpProcessor();
+ copyInterceptors(clone);
+ return clone;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ BasicHttpProcessor clone = (BasicHttpProcessor) super.clone();
+ copyInterceptors(clone);
+ return clone;
+ }
+
+}
diff --git a/src/org/apache/http/protocol/DefaultedHttpContext.java b/src/org/apache/http/protocol/DefaultedHttpContext.java
new file mode 100644
index 0000000..986f1a6
--- /dev/null
+++ b/src/org/apache/http/protocol/DefaultedHttpContext.java
@@ -0,0 +1,79 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/DefaultedHttpContext.java $
+ * $Revision: 654882 $
+ * $Date: 2008-05-09 09:58:59 -0700 (Fri, 09 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * {@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.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 654882 $
+ */
+public final class DefaultedHttpContext implements HttpContext {
+
+ private final HttpContext local;
+ private final HttpContext defaults;
+
+ public DefaultedHttpContext(final HttpContext local, final HttpContext defaults) {
+ super();
+ if (local == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+ this.local = local;
+ this.defaults = defaults;
+ }
+
+ public Object getAttribute(final String id) {
+ 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;
+ }
+
+}
diff --git a/src/org/apache/http/protocol/ExecutionContext.java b/src/org/apache/http/protocol/ExecutionContext.java
new file mode 100644
index 0000000..d14acb5
--- /dev/null
+++ b/src/org/apache/http/protocol/ExecutionContext.java
@@ -0,0 +1,52 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ExecutionContext.java $
+ * $Revision: 558154 $
+ * $Date: 2007-07-20 14:29:02 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * {@link HttpContext Context} attribute names for protocol execution.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 558154 $
+ *
+ * @since 4.0
+ */
+public interface ExecutionContext {
+
+ public static final String HTTP_CONNECTION = "http.connection";
+ public static final String HTTP_REQUEST = "http.request";
+ public static final String HTTP_RESPONSE = "http.response";
+ public static final String HTTP_TARGET_HOST = "http.target_host";
+ public static final String HTTP_PROXY_HOST = "http.proxy_host";
+ public static final String HTTP_REQ_SENT = "http.request_sent";
+
+}
diff --git a/src/org/apache/http/protocol/HTTP.java b/src/org/apache/http/protocol/HTTP.java
new file mode 100644
index 0000000..de76ca6
--- /dev/null
+++ b/src/org/apache/http/protocol/HTTP.java
@@ -0,0 +1,99 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HTTP.java $
+ * $Revision: 555989 $
+ * $Date: 2007-07-13 06:33:39 -0700 (Fri, 13 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * Constants and static helpers related to the HTTP protocol.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 555989 $
+ *
+ * @since 4.0
+ */
+public final class HTTP {
+
+ public static final int CR = 13; // <US-ASCII CR, carriage return (13)>
+ public static final int LF = 10; // <US-ASCII LF, linefeed (10)>
+ public static final int SP = 32; // <US-ASCII SP, space (32)>
+ public static final int HT = 9; // <US-ASCII HT, horizontal-tab (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";
+
+ /** Common charset definitions */
+ public static final String UTF_8 = "UTF-8";
+ public static final String UTF_16 = "UTF-16";
+ public static final String US_ASCII = "US-ASCII";
+ public static final String ASCII = "ASCII";
+ public static final String ISO_8859_1 = "ISO-8859-1";
+
+ /** Default charsets */
+ public static final String DEFAULT_CONTENT_CHARSET = ISO_8859_1;
+ public static final String DEFAULT_PROTOCOL_CHARSET = US_ASCII;
+
+ /** Content type definitions */
+ public final static String OCTET_STREAM_TYPE = "application/octet-stream";
+ public final static String PLAIN_TEXT_TYPE = "text/plain";
+ public final static String CHARSET_PARAM = "; charset=";
+
+ /** Default content type */
+ public final static String DEFAULT_CONTENT_TYPE = OCTET_STREAM_TYPE;
+
+ public static boolean isWhitespace(char ch) {
+ return ch == SP || ch == HT || ch == CR || ch == LF;
+ }
+
+ private HTTP() {
+ }
+
+}
diff --git a/src/org/apache/http/protocol/HttpContext.java b/src/org/apache/http/protocol/HttpContext.java
new file mode 100644
index 0000000..bcf36fd
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpContext.java
@@ -0,0 +1,58 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpContext.java $
+ * $Revision: 558111 $
+ * $Date: 2007-07-20 13:01:50 -0700 (Fri, 20 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * A context for executing a request.
+ * The context is used to tie together the request, the response,
+ * and optional application data. It is also used for internal data.
+ * Attribute names starting with the prefix "http." are
+ * {@link #RESERVED_PREFIX reserved} for internal data.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 558111 $
+ *
+ * @since 4.0
+ */
+public interface HttpContext {
+
+ /** The prefix reserved for use by HTTP components. "http." */
+ public static final String RESERVED_PREFIX = "http.";
+
+ Object getAttribute(String id);
+
+ void setAttribute(String id, Object obj);
+
+ Object removeAttribute(String id);
+
+}
diff --git a/src/org/apache/http/protocol/HttpDateGenerator.java b/src/org/apache/http/protocol/HttpDateGenerator.java
new file mode 100644
index 0000000..bfb0863
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpDateGenerator.java
@@ -0,0 +1,81 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpDateGenerator.java $
+ * $Revision: 548066 $
+ * $Date: 2007-06-17 09:51:55 -0700 (Sun, 17 Jun 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+
+/**
+ * Generates a date in the format required by the HTTP protocol.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 548066 $
+ *
+ * @since 4.0
+ */
+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");
+
+
+ private final DateFormat dateformat;
+
+ private long dateAsLong = 0L;
+ private String dateAsText = null;
+
+ public HttpDateGenerator() {
+ super();
+ this.dateformat = new SimpleDateFormat(PATTERN_RFC1123, Locale.US);
+ this.dateformat.setTimeZone(GMT);
+ }
+
+ public synchronized String getCurrentDate() {
+ 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/src/org/apache/http/protocol/HttpExpectationVerifier.java b/src/org/apache/http/protocol/HttpExpectationVerifier.java
new file mode 100644
index 0000000..9fa4316
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpExpectationVerifier.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpExpectationVerifier.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+
+/**
+ * Defines an interface to verify whether an incoming HTTP request meets
+ * the target server's expectations.
+ *<p>
+ * The Expect request-header field is used to indicate that particular
+ * server behaviors are required by the client.
+ *</p>
+ *<pre>
+ * Expect = "Expect" ":" 1#expectation
+ *
+ * expectation = "100-continue" | expectation-extension
+ * expectation-extension = token [ "=" ( token | quoted-string )
+ * *expect-params ]
+ * expect-params = ";" token [ "=" ( token | quoted-string ) ]
+ *</pre>
+ *<p>
+ * 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.
+ *</p>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ *
+ * @since 4.0
+ */
+public interface HttpExpectationVerifier {
+
+ void verify(HttpRequest request, HttpResponse response, HttpContext context)
+ throws HttpException;
+
+}
diff --git a/src/org/apache/http/protocol/HttpProcessor.java b/src/org/apache/http/protocol/HttpProcessor.java
new file mode 100644
index 0000000..489220d
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpProcessor.java
@@ -0,0 +1,56 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpProcessor.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponseInterceptor;
+
+/**
+ * Performs interceptor processing of requests and responses.
+ * Specific interceptors typically interpret or update message headers,
+ * and they may wrap the message entity for example to implement a
+ * specific transport or content encoding.
+ * A <code>HttpProcessor</code> typically maintains a list of
+ * interceptors that will be applied to a request or response.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public interface HttpProcessor
+ extends HttpRequestInterceptor, HttpResponseInterceptor {
+
+ // no additional methods
+}
diff --git a/src/org/apache/http/protocol/HttpRequestExecutor.java b/src/org/apache/http/protocol/HttpRequestExecutor.java
new file mode 100644
index 0000000..71fa75a
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestExecutor.java
@@ -0,0 +1,322 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestExecutor.java $
+ * $Revision: 576073 $
+ * $Date: 2007-09-16 03:53:13 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+import java.net.ProtocolException;
+
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.params.CoreProtocolPNames;
+
+/**
+ * Sends HTTP requests and receives the responses.
+ * Takes care of request preprocessing and response postprocessing
+ * by the respective interceptors.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576073 $
+ *
+ * @since 4.0
+ */
+public class HttpRequestExecutor {
+
+ /**
+ * Create a new request executor.
+ */
+ public HttpRequestExecutor() {
+ super();
+ }
+
+ /**
+ * Decide whether a response comes with an entity.
+ * The implementation in this class is based on RFC 2616.
+ * Unknown methods and response codes are supposed to
+ * indicate responses with an entity.
+ * <br/>
+ * 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;
+ }
+ 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;
+ }
+
+ /**
+ * Synchronously send a request and obtain the response.
+ *
+ * @param request the request to send. It will be preprocessed.
+ * @param conn the open connection over which to send
+ *
+ * @return the response to the request, postprocessed
+ *
+ * @throws HttpException in case of a protocol or processing problem
+ * @throws IOException in case of an I/O problem
+ */
+ public HttpResponse execute(
+ final HttpRequest request,
+ final HttpClientConnection conn,
+ final HttpContext context)
+ throws IOException, HttpException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (conn == null) {
+ throw new IllegalArgumentException("Client connection may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ try {
+ HttpResponse response = doSendRequest(request, conn, context);
+ if (response == null) {
+ response = doReceiveResponse(request, conn, context);
+ }
+ return response;
+ } catch (IOException ex) {
+ conn.close();
+ throw ex;
+ } catch (HttpException ex) {
+ conn.close();
+ throw ex;
+ } catch (RuntimeException ex) {
+ conn.close();
+ throw ex;
+ }
+ }
+
+ /**
+ * Prepare a request for sending.
+ *
+ * @param request the request to prepare
+ * @param processor the processor to use
+ * @param context the context for sending the request
+ *
+ * @throws HttpException in case of a protocol or processing problem
+ * @throws IOException in case of an I/O problem
+ */
+ public void preProcess(
+ final HttpRequest request,
+ final HttpProcessor processor,
+ final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (processor == null) {
+ throw new IllegalArgumentException("HTTP processor may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+ processor.process(request, context);
+ }
+
+ /**
+ * Send a request over a 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
+ * <code>null</code> if the expect-continue handshake is not used
+ *
+ * @throws HttpException in case of a protocol or processing problem
+ * @throws IOException in case of an I/O problem
+ */
+ protected HttpResponse doSendRequest(
+ final HttpRequest request,
+ final HttpClientConnection conn,
+ final HttpContext context)
+ throws IOException, HttpException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (conn == null) {
+ throw new IllegalArgumentException("HTTP connection may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ HttpResponse response = null;
+ context.setAttribute(ExecutionContext.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.
+ int tms = request.getParams().getIntParameter(
+ CoreProtocolPNames.WAIT_FOR_CONTINUE, 2000);
+
+ if (conn.isResponseAvailable(tms)) {
+ response = conn.receiveResponseHeader();
+ if (canResponseHaveBody(request, response)) {
+ conn.receiveResponseEntity(response);
+ }
+ 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(ExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
+ return response;
+ }
+
+ /**
+ * Wait for and receive 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 final response, not yet post-processed
+ *
+ * @throws HttpException in case of a protocol or processing problem
+ * @throws IOException in case of an I/O problem
+ */
+ protected HttpResponse doReceiveResponse(
+ final HttpRequest request,
+ final HttpClientConnection conn,
+ final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (conn == null) {
+ throw new IllegalArgumentException("HTTP connection may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+
+ 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;
+
+ }
+
+ /**
+ * Finish a response.
+ * This includes post-processing of the response object.
+ * It does <i>not</i> read the response entity, if any.
+ * It does <i>not</i> allow for immediate re-use of the
+ * connection over which the response is coming in.
+ *
+ * @param response the response object to finish
+ * @param processor the processor to use
+ * @param context the context for post-processing the response
+ *
+ * @throws HttpException in case of a protocol or processing problem
+ * @throws IOException in case of an I/O problem
+ */
+ public void postProcess(
+ final HttpResponse response,
+ final HttpProcessor processor,
+ final HttpContext context)
+ throws HttpException, IOException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ if (processor == null) {
+ throw new IllegalArgumentException("HTTP processor may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+ processor.process(response, context);
+ }
+
+} // class HttpRequestExecutor
diff --git a/src/org/apache/http/protocol/HttpRequestHandler.java b/src/org/apache/http/protocol/HttpRequestHandler.java
new file mode 100644
index 0000000..7494353
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestHandler.java
@@ -0,0 +1,53 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestHandler.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+
+/**
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ *
+ * @since 4.0
+ */
+public interface HttpRequestHandler {
+
+ void handle(HttpRequest request, HttpResponse response, HttpContext context)
+ throws HttpException, IOException;
+
+}
diff --git a/src/org/apache/http/protocol/HttpRequestHandlerRegistry.java b/src/org/apache/http/protocol/HttpRequestHandlerRegistry.java
new file mode 100644
index 0000000..668d748
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestHandlerRegistry.java
@@ -0,0 +1,82 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestHandlerRegistry.java $
+ * $Revision: 630662 $
+ * $Date: 2008-02-24 11:40:51 -0800 (Sun, 24 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.Map;
+
+/**
+ * Maintains a map of HTTP request handlers keyed by a request URI pattern.
+ * {@link HttpRequestHandler} instances can be looked up by request URI
+ * using the {@link HttpRequestHandlerResolver} interface.<br/>
+ * Patterns may have three formats:
+ * <ul>
+ * <li><code>*</code></li>
+ * <li><code>*&lt;uri&gt;</code></li>
+ * <li><code>&lt;uri&gt;*</code></li>
+ * </ul>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 630662 $
+ */
+public class HttpRequestHandlerRegistry implements HttpRequestHandlerResolver {
+
+ private final UriPatternMatcher matcher;
+
+ public HttpRequestHandlerRegistry() {
+ matcher = new UriPatternMatcher();
+ }
+
+ public void register(final String pattern, final HttpRequestHandler handler) {
+ matcher.register(pattern, handler);
+ }
+
+ public void unregister(final String pattern) {
+ matcher.unregister(pattern);
+ }
+
+ public void setHandlers(final Map map) {
+ matcher.setHandlers(map);
+ }
+
+ public HttpRequestHandler lookup(final String requestURI) {
+ return (HttpRequestHandler) matcher.lookup(requestURI);
+ }
+
+ /**
+ * @deprecated
+ */
+ protected boolean matchUriRequestPattern(final String pattern, final String requestUri) {
+ return matcher.matchUriRequestPattern(pattern, requestUri);
+ }
+
+}
diff --git a/src/org/apache/http/protocol/HttpRequestHandlerResolver.java b/src/org/apache/http/protocol/HttpRequestHandlerResolver.java
new file mode 100644
index 0000000..be92deb
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestHandlerResolver.java
@@ -0,0 +1,46 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestHandlerResolver.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * Interface to be implemented by objects that can resolve
+ * {@link HttpRequestHandler} instances by request URI.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ */
+public interface HttpRequestHandlerResolver {
+
+ HttpRequestHandler lookup(String requestURI);
+
+}
diff --git a/src/org/apache/http/protocol/HttpRequestInterceptorList.java b/src/org/apache/http/protocol/HttpRequestInterceptorList.java
new file mode 100644
index 0000000..84ec761
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpRequestInterceptorList.java
@@ -0,0 +1,120 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpRequestInterceptorList.java $
+ * $Revision: 554903 $
+ * $Date: 2007-07-10 03:54:17 -0700 (Tue, 10 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.List;
+
+import org.apache.http.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}.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 554903 $
+ *
+ * @since 4.0
+ */
+public interface HttpRequestInterceptorList {
+
+ /**
+ * Appends a request interceptor to this list.
+ *
+ * @param itcp the request interceptor to add
+ */
+ void addRequestInterceptor(HttpRequestInterceptor itcp)
+ ;
+
+
+ /**
+ * Inserts a request interceptor at the specified index.
+ *
+ * @param itcp the request interceptor to add
+ * @param index the index to insert the interceptor at
+ */
+ void addRequestInterceptor(HttpRequestInterceptor itcp, 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
+ * <code>null</code> 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 itcps the list of request interceptors
+ */
+ void setInterceptors(List itcps)
+ ;
+
+
+} // interface HttpRequestInterceptorList
+
diff --git a/src/org/apache/http/protocol/HttpResponseInterceptorList.java b/src/org/apache/http/protocol/HttpResponseInterceptorList.java
new file mode 100644
index 0000000..8b5811b
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpResponseInterceptorList.java
@@ -0,0 +1,121 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpResponseInterceptorList.java $
+ * $Revision: 554903 $
+ * $Date: 2007-07-10 03:54:17 -0700 (Tue, 10 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+
+import java.util.List;
+
+import org.apache.http.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}.
+ *
+ * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
+ *
+ * @version $Revision: 554903 $
+ *
+ * @since 4.0
+ */
+public interface HttpResponseInterceptorList {
+
+ /**
+ * Appends a response interceptor to this list.
+ *
+ * @param itcp the response interceptor to add
+ */
+ void addResponseInterceptor(HttpResponseInterceptor itcp)
+ ;
+
+ /**
+ * Inserts a response interceptor at the specified index.
+ *
+ * @param itcp the response interceptor to add
+ * @param index the index to insert the interceptor at
+ */
+ void addResponseInterceptor(HttpResponseInterceptor itcp, 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
+ * <code>null</code> 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 itcps the list of response interceptors
+ */
+ void setInterceptors(List itcps)
+ ;
+
+
+} // interface HttpResponseInterceptorList
+
diff --git a/src/org/apache/http/protocol/HttpService.java b/src/org/apache/http/protocol/HttpService.java
new file mode 100644
index 0000000..991e931
--- /dev/null
+++ b/src/org/apache/http/protocol/HttpService.java
@@ -0,0 +1,249 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/HttpService.java $
+ * $Revision: 610763 $
+ * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.UnsupportedHttpVersionException;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.DefaultedHttpParams;
+import org.apache.http.util.EncodingUtils;
+
+/**
+ * Minimalistic server-side implementation of an HTTP processor.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 610763 $
+ */
+public class HttpService {
+
+ private HttpParams params = null;
+ private HttpProcessor processor = null;
+ private HttpRequestHandlerResolver handlerResolver = null;
+ private ConnectionReuseStrategy connStrategy = null;
+ private HttpResponseFactory responseFactory = null;
+ private HttpExpectationVerifier expectationVerifier = null;
+
+ /**
+ * 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
+ */
+ public HttpService(
+ final HttpProcessor proc,
+ final ConnectionReuseStrategy connStrategy,
+ final HttpResponseFactory responseFactory) {
+ super();
+ setHttpProcessor(proc);
+ setConnReuseStrategy(connStrategy);
+ setResponseFactory(responseFactory);
+ }
+
+ public void setHttpProcessor(final HttpProcessor processor) {
+ if (processor == null) {
+ throw new IllegalArgumentException("HTTP processor may not be null.");
+ }
+ this.processor = processor;
+ }
+
+ public void setConnReuseStrategy(final ConnectionReuseStrategy connStrategy) {
+ if (connStrategy == null) {
+ throw new IllegalArgumentException("Connection reuse strategy may not be null");
+ }
+ this.connStrategy = connStrategy;
+ }
+
+ public void setResponseFactory(final HttpResponseFactory responseFactory) {
+ if (responseFactory == null) {
+ throw new IllegalArgumentException("Response factory may not be null");
+ }
+ this.responseFactory = responseFactory;
+ }
+
+ public void setHandlerResolver(final HttpRequestHandlerResolver handlerResolver) {
+ this.handlerResolver = handlerResolver;
+ }
+
+ public void setExpectationVerifier(final HttpExpectationVerifier expectationVerifier) {
+ this.expectationVerifier = expectationVerifier;
+ }
+
+ public HttpParams getParams() {
+ return this.params;
+ }
+
+ public void setParams(final HttpParams params) {
+ this.params = params;
+ }
+
+ public void handleRequest(
+ final HttpServerConnection conn,
+ final HttpContext context) throws IOException, HttpException {
+
+ context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+
+ HttpResponse response = null;
+
+ try {
+
+ HttpRequest request = conn.receiveRequestHeader();
+ request.setParams(
+ new DefaultedHttpParams(request.getParams(), this.params));
+
+ ProtocolVersion ver =
+ request.getRequestLine().getProtocolVersion();
+ if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
+ // Downgrade protocol version if greater than HTTP/1.1
+ ver = HttpVersion.HTTP_1_1;
+ }
+
+ if (request instanceof HttpEntityEnclosingRequest) {
+
+ if (((HttpEntityEnclosingRequest) request).expectContinue()) {
+ response = this.responseFactory.newHttpResponse(ver,
+ HttpStatus.SC_CONTINUE, context);
+ response.setParams(
+ new DefaultedHttpParams(response.getParams(), this.params));
+
+ if (this.expectationVerifier != null) {
+ try {
+ this.expectationVerifier.verify(request, response, context);
+ } catch (HttpException ex) {
+ response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0,
+ HttpStatus.SC_INTERNAL_SERVER_ERROR, context);
+ response.setParams(
+ new DefaultedHttpParams(response.getParams(), this.params));
+ 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);
+ }
+ }
+
+ if (response == null) {
+ response = this.responseFactory.newHttpResponse(ver, HttpStatus.SC_OK, context);
+ response.setParams(
+ new DefaultedHttpParams(response.getParams(), this.params));
+
+ context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
+ context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);
+
+ this.processor.process(request, context);
+ doService(request, response, context);
+ }
+
+ // Make sure the request content is fully consumed
+ if (request instanceof HttpEntityEnclosingRequest) {
+ HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ }
+
+ } catch (HttpException ex) {
+ response = this.responseFactory.newHttpResponse
+ (HttpVersion.HTTP_1_0, HttpStatus.SC_INTERNAL_SERVER_ERROR,
+ context);
+ response.setParams(
+ new DefaultedHttpParams(response.getParams(), this.params));
+ handleException(ex, response);
+ }
+
+ this.processor.process(response, context);
+ conn.sendResponseHeader(response);
+ conn.sendResponseEntity(response);
+ conn.flush();
+
+ if (!this.connStrategy.keepAlive(response, context)) {
+ conn.close();
+ }
+ }
+
+ 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);
+ }
+ byte[] msg = EncodingUtils.getAsciiBytes(ex.getMessage());
+ ByteArrayEntity entity = new ByteArrayEntity(msg);
+ entity.setContentType("text/plain; charset=US-ASCII");
+ response.setEntity(entity);
+ }
+
+ protected void doService(
+ final HttpRequest request,
+ final HttpResponse response,
+ final HttpContext context) throws HttpException, IOException {
+ HttpRequestHandler handler = null;
+ if (this.handlerResolver != null) {
+ String requestURI = request.getRequestLine().getUri();
+ handler = this.handlerResolver.lookup(requestURI);
+ }
+ if (handler != null) {
+ handler.handle(request, response, context);
+ } else {
+ response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/RequestConnControl.java b/src/org/apache/http/protocol/RequestConnControl.java
new file mode 100644
index 0000000..0a7088c
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestConnControl.java
@@ -0,0 +1,67 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestConnControl.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+
+/**
+ * A request interceptor that suggests connection keep-alive to the server.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public class RequestConnControl implements HttpRequestInterceptor {
+
+ public RequestConnControl() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ 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/src/org/apache/http/protocol/RequestContent.java b/src/org/apache/http/protocol/RequestContent.java
new file mode 100644
index 0000000..745f604
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestContent.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestContent.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ProtocolException;
+
+/**
+ * A request interceptor that decides about the transport encoding.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @since 4.0
+ */
+public class RequestContent implements HttpRequestInterceptor {
+
+ public RequestContent() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (request instanceof HttpEntityEnclosingRequest) {
+ 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");
+ }
+ ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+ 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/src/org/apache/http/protocol/RequestDate.java b/src/org/apache/http/protocol/RequestDate.java
new file mode 100644
index 0000000..6462906
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestDate.java
@@ -0,0 +1,72 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestDate.java $
+ * $Revision: 555989 $
+ * $Date: 2007-07-13 06:33:39 -0700 (Fri, 13 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpRequestInterceptor;
+
+/**
+ * A request interceptor that adds a Date header.
+ * For use on the client side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 555989 $
+ *
+ * @since 4.0
+ */
+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 {
+ if (request == null) {
+ throw new IllegalArgumentException
+ ("HTTP request may not be null.");
+ }
+ if ((request instanceof HttpEntityEnclosingRequest) &&
+ !request.containsHeader(HTTP.DATE_HEADER)) {
+ String httpdate = DATE_GENERATOR.getCurrentDate();
+ request.setHeader(HTTP.DATE_HEADER, httpdate);
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/RequestExpectContinue.java b/src/org/apache/http/protocol/RequestExpectContinue.java
new file mode 100644
index 0000000..0799849
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestExpectContinue.java
@@ -0,0 +1,78 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestExpectContinue.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * A request interceptor that enables the expect-continue handshake.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @since 4.0
+ */
+public class RequestExpectContinue implements HttpRequestInterceptor {
+
+ public RequestExpectContinue() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (request instanceof HttpEntityEnclosingRequest) {
+ 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) {
+ ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+ if (HttpProtocolParams.useExpectContinue(request.getParams())
+ && !ver.lessEquals(HttpVersion.HTTP_1_0)) {
+ request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/RequestTargetHost.java b/src/org/apache/http/protocol/RequestTargetHost.java
new file mode 100644
index 0000000..9349a8a
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestTargetHost.java
@@ -0,0 +1,98 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestTargetHost.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.apache.http.HttpConnection;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ProtocolException;
+
+/**
+ * A request interceptor that sets the Host header for HTTP/1.1 requests.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @since 4.0
+ */
+public class RequestTargetHost implements HttpRequestInterceptor {
+
+ public RequestTargetHost() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+ if (!request.containsHeader(HTTP.TARGET_HOST)) {
+ HttpHost targethost = (HttpHost) context
+ .getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+ if (targethost == null) {
+ HttpConnection conn = (HttpConnection) context
+ .getAttribute(ExecutionContext.HTTP_CONNECTION);
+ if (conn instanceof HttpInetConnection) {
+ // Populate the context with a default HTTP host based on the
+ // inet address of the target host
+ InetAddress address = ((HttpInetConnection) conn).getRemoteAddress();
+ int port = ((HttpInetConnection) conn).getRemotePort();
+ if (address != null) {
+ targethost = new HttpHost(address.getHostName(), port);
+ }
+ }
+ if (targethost == null) {
+ ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+ 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/src/org/apache/http/protocol/RequestUserAgent.java b/src/org/apache/http/protocol/RequestUserAgent.java
new file mode 100644
index 0000000..5a3145f
--- /dev/null
+++ b/src/org/apache/http/protocol/RequestUserAgent.java
@@ -0,0 +1,69 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/RequestUserAgent.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.params.HttpProtocolParams;
+
+/**
+ * A request interceptor that adds a User-Agent header.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public class RequestUserAgent implements HttpRequestInterceptor {
+
+ public RequestUserAgent() {
+ super();
+ }
+
+ public void process(final HttpRequest request, final HttpContext context)
+ throws HttpException, IOException {
+ if (request == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (!request.containsHeader(HTTP.USER_AGENT)) {
+ String useragent = HttpProtocolParams.getUserAgent(request.getParams());
+ if (useragent != null) {
+ request.addHeader(HTTP.USER_AGENT, useragent);
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/ResponseConnControl.java b/src/org/apache/http/protocol/ResponseConnControl.java
new file mode 100644
index 0000000..2e535fe
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseConnControl.java
@@ -0,0 +1,104 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseConnControl.java $
+ * $Revision: 618017 $
+ * $Date: 2008-02-03 08:42:22 -0800 (Sun, 03 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+
+/**
+ * A response interceptor that suggests connection keep-alive to the client.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 618017 $
+ *
+ * @since 4.0
+ */
+public class ResponseConnControl implements HttpResponseInterceptor {
+
+ public ResponseConnControl() {
+ super();
+ }
+
+ public void process(final HttpResponse response, final HttpContext context)
+ throws HttpException, IOException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP response may not be null");
+ }
+ if (context == null) {
+ throw new IllegalArgumentException("HTTP context may not be null");
+ }
+ // Always drop connection after certain type of responses
+ 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;
+ }
+ // Always drop connection for HTTP/1.0 responses and below
+ // if the content body cannot be correctly delimited
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ 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
+ HttpRequest request = (HttpRequest)
+ context.getAttribute(ExecutionContext.HTTP_REQUEST);
+ if (request != null) {
+ Header header = request.getFirstHeader(HTTP.CONN_DIRECTIVE);
+ if (header != null) {
+ response.setHeader(HTTP.CONN_DIRECTIVE, header.getValue());
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/ResponseContent.java b/src/org/apache/http/protocol/ResponseContent.java
new file mode 100644
index 0000000..d1ac054
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseContent.java
@@ -0,0 +1,101 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseContent.java $
+ * $Revision: 573864 $
+ * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.ProtocolException;
+
+/**
+ * A response interceptor that sets up entity-related headers.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 573864 $
+ *
+ * @since 4.0
+ */
+public class ResponseContent implements HttpResponseInterceptor {
+
+ public ResponseContent() {
+ super();
+ }
+
+ public void process(final HttpResponse response, final HttpContext context)
+ throws HttpException, IOException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ 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");
+ }
+ ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ 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 {
+ 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/src/org/apache/http/protocol/ResponseDate.java b/src/org/apache/http/protocol/ResponseDate.java
new file mode 100644
index 0000000..431dc19
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseDate.java
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseDate.java $
+ * $Revision: 555989 $
+ * $Date: 2007-07-13 06:33:39 -0700 (Fri, 13 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+
+/**
+ * A response interceptor that adds a Date header.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 555989 $
+ *
+ * @since 4.0
+ */
+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 {
+ if (response == null) {
+ throw new IllegalArgumentException
+ ("HTTP response may not be null.");
+ }
+ int status = response.getStatusLine().getStatusCode();
+ if ((status >= HttpStatus.SC_OK) &&
+ !response.containsHeader(HTTP.DATE_HEADER)) {
+ String httpdate = DATE_GENERATOR.getCurrentDate();
+ response.setHeader(HTTP.DATE_HEADER, httpdate);
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/ResponseServer.java b/src/org/apache/http/protocol/ResponseServer.java
new file mode 100644
index 0000000..44df593
--- /dev/null
+++ b/src/org/apache/http/protocol/ResponseServer.java
@@ -0,0 +1,71 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/ResponseServer.java $
+ * $Revision: 576073 $
+ * $Date: 2007-09-16 03:53:13 -0700 (Sun, 16 Sep 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.params.CoreProtocolPNames;
+
+/**
+ * A response interceptor that adds a Server header.
+ * For use on the server side.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 576073 $
+ *
+ * @since 4.0
+ */
+public class ResponseServer implements HttpResponseInterceptor {
+
+ public ResponseServer() {
+ super();
+ }
+
+ public void process(final HttpResponse response, final HttpContext context)
+ throws HttpException, IOException {
+ if (response == null) {
+ throw new IllegalArgumentException("HTTP request may not be null");
+ }
+ if (!response.containsHeader(HTTP.SERVER_HEADER)) {
+ String s = (String) response.getParams().getParameter(
+ CoreProtocolPNames.ORIGIN_SERVER);
+ if (s != null) {
+ response.addHeader(HTTP.SERVER_HEADER, s);
+ }
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/SyncBasicHttpContext.java b/src/org/apache/http/protocol/SyncBasicHttpContext.java
new file mode 100644
index 0000000..b1a408b
--- /dev/null
+++ b/src/org/apache/http/protocol/SyncBasicHttpContext.java
@@ -0,0 +1,61 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/SyncBasicHttpContext.java $
+ * $Revision: 613298 $
+ * $Date: 2008-01-18 14:09:22 -0800 (Fri, 18 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+/**
+ * Thread-safe extension of the {@link BasicHttpContext}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 613298 $
+ *
+ * @since 4.0
+ */
+public class SyncBasicHttpContext extends BasicHttpContext {
+
+ public SyncBasicHttpContext(final HttpContext parentContext) {
+ super(parentContext);
+ }
+
+ public synchronized Object getAttribute(final String id) {
+ return super.getAttribute(id);
+ }
+
+ public synchronized void setAttribute(final String id, final Object obj) {
+ super.setAttribute(id, obj);
+ }
+
+ public synchronized Object removeAttribute(final String id) {
+ return super.removeAttribute(id);
+ }
+
+}
diff --git a/src/org/apache/http/protocol/UriPatternMatcher.java b/src/org/apache/http/protocol/UriPatternMatcher.java
new file mode 100644
index 0000000..2870d99
--- /dev/null
+++ b/src/org/apache/http/protocol/UriPatternMatcher.java
@@ -0,0 +1,127 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/UriPatternMatcher.java $
+ * $Revision: 630662 $
+ * $Date: 2008-02-24 11:40:51 -0800 (Sun, 24 Feb 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.protocol;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Maintains a map of objects keyed by a request URI pattern.
+ * Instances can be looked up by request URI.<br/>
+ * Patterns may have three formats:
+ * <ul>
+ * <li><code>*</code></li>
+ * <li><code>*&lt;uri&gt;</code></li>
+ * <li><code>&lt;uri&gt;*</code></li>
+ * </ul>
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 630662 $
+ */
+public class UriPatternMatcher {
+
+ private final Map handlerMap;
+
+ public UriPatternMatcher() {
+ super();
+ this.handlerMap = new HashMap();
+ }
+
+ public void register(final String pattern, final Object handler) {
+ if (pattern == null) {
+ throw new IllegalArgumentException("URI request pattern may not be null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("HTTP request handelr may not be null");
+ }
+ this.handlerMap.put(pattern, handler);
+ }
+
+ public void unregister(final String pattern) {
+ if (pattern == null) {
+ return;
+ }
+ this.handlerMap.remove(pattern);
+ }
+
+ public void setHandlers(final Map map) {
+ if (map == null) {
+ throw new IllegalArgumentException("Map of handlers may not be null");
+ }
+ this.handlerMap.clear();
+ this.handlerMap.putAll(map);
+ }
+
+ public Object lookup(String requestURI) {
+ if (requestURI == null) {
+ throw new IllegalArgumentException("Request URI may not be null");
+ }
+ //Strip away the query part part if found
+ int index = requestURI.indexOf("?");
+ if (index != -1) {
+ requestURI = requestURI.substring(0, index);
+ }
+
+ // direct match?
+ Object handler = this.handlerMap.get(requestURI);
+ if (handler == null) {
+ // pattern match?
+ String bestMatch = null;
+ for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
+ String pattern = (String) it.next();
+ if (matchUriRequestPattern(pattern, requestURI)) {
+ // we have a match. is it any better?
+ if (bestMatch == null
+ || (bestMatch.length() < pattern.length())
+ || (bestMatch.length() == pattern.length() && pattern.endsWith("*"))) {
+ handler = this.handlerMap.get(pattern);
+ bestMatch = pattern;
+ }
+ }
+ }
+ }
+ return handler;
+ }
+
+ protected boolean matchUriRequestPattern(final String pattern, final String requestUri) {
+ if (pattern.equals("*")) {
+ return true;
+ } else {
+ return
+ (pattern.endsWith("*") && requestUri.startsWith(pattern.substring(0, pattern.length() - 1))) ||
+ (pattern.startsWith("*") && requestUri.endsWith(pattern.substring(1, pattern.length())));
+ }
+ }
+
+}
diff --git a/src/org/apache/http/protocol/package.html b/src/org/apache/http/protocol/package.html
new file mode 100644
index 0000000..5056bcc
--- /dev/null
+++ b/src/org/apache/http/protocol/package.html
@@ -0,0 +1,112 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/protocol/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+HTTP protocol execution framework.
+
+Apart from simply sending and receiving messages, there are a lot
+of things to consider when communicating with HTTP. Many details
+such as transport encodings or connection management are handled
+by setting up or interpreting
+{@link org.apache.http.Header headers} in the messages.
+In order to relieve applications from the responsibility of
+implementing these nitty-gritty details of the protocol,
+HTTP components provides an execution framework that sets up
+some of the headers before sending a message, and interprets
+headers when a message has been received.
+<br/>
+An HTTP {@link org.apache.http.protocol.HttpProcessor processor}
+typically keeps lists of so-called interceptors that will be executed
+before a message is sent and after it has been received.
+An application should initialize a processor, set up the lists
+with the required and desired processors, and then communicate
+through that processor. There are four kinds of interceptors,
+depending on whether they act on
+{@link org.apache.http.HttpRequestInterceptor requests} or
+{@link org.apache.http.HttpResponseInterceptor responses},
+on the client or server side:
+
+<table border="1" cellpadding="5">
+<tr><th></th>
+ <th>Client</th>
+ <th>Server</th>
+</tr>
+<tr><th>Request</th>
+ <td>prepares headers before a request is sent</td>
+ <td>interprets headers when a request is received</td>
+</tr>
+<tr><th>Response</th>
+ <td>interprets headers when a response is received</td>
+ <td>prepares headers before a response is sent</td>
+</tr>
+</table>
+
+<p>
+{@link org.apache.http.protocol.HttpRequestExecutor HttpRequestExecutor}
+is a processor for the client side,
+{@link org.apache.http.protocol.HttpService HttpService}
+for the server side.
+On the client side, a {@link org.apache.http.protocol.HttpContext context}
+is used to tie together a request, the response to it, and other data
+that might be associated with the request execution. It is passed to
+the request executor whenever needed.
+</p>
+
+<p>
+<font size="+1">
+<i>
+Information about required and recommended interceptors for the
+client side will be provided elsewhere. For the time being, please
+refer to the comments in the example applications or ask on
+one of the mailing lists.
+</i>
+</font>
+</p>
+
+<p>
+<b>Note:</b>
+If you want to develop a server-side application, we recommend that
+you implement your application as a servlet running in a servlet engine
+like <a href="http://tomcat.apache.org/">Tomcat</a> or full-blown
+JSEE container like <a href="http://geronimo.apache.org/">Geronimo</a>.
+If you prefer to implement a server-side application based on our
+{@link org.apache.http.protocol.HttpService HttpService}, we'll
+assume that you know what you're doing and that you don't need
+help in figuring out which interceptors need to be configured.
+</p>
+
+
+</body>
+</html>
diff --git a/src/org/apache/http/svn.info b/src/org/apache/http/svn.info
new file mode 100644
index 0000000..100deb5
--- /dev/null
+++ b/src/org/apache/http/svn.info
@@ -0,0 +1,8 @@
+Repository Root: http://svn.apache.org/repos/asf
+Repository UUID: 13f79535-47bb-0310-9956-ffa450edef68
+Revision: 677354
+Node Kind: directory
+Schedule: normal
+Last Changed Author: olegk
+Last Changed Rev: 677250
+Last Changed Date: 2008-07-16 04:45:47 -0700 (Wed, 16 Jul 2008)
diff --git a/src/org/apache/http/util/ByteArrayBuffer.java b/src/org/apache/http/util/ByteArrayBuffer.java
new file mode 100644
index 0000000..01d6577
--- /dev/null
+++ b/src/org/apache/http/util/ByteArrayBuffer.java
@@ -0,0 +1,162 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/ByteArrayBuffer.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+/**
+ * A resizable byte array.
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public final class ByteArrayBuffer {
+
+ private byte[] buffer;
+ private int len;
+
+ public ByteArrayBuffer(int capacity) {
+ super();
+ if (capacity < 0) {
+ throw new IllegalArgumentException("Buffer capacity may not be negative");
+ }
+ this.buffer = new byte[capacity];
+ }
+
+ private void expand(int newlen) {
+ byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)];
+ System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
+ this.buffer = newbuffer;
+ }
+
+ public void append(final byte[] b, int off, int len) {
+ if (b == null) {
+ return;
+ }
+ if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) < 0) || ((off + len) > b.length)) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (len == 0) {
+ return;
+ }
+ int newlen = this.len + len;
+ if (newlen > this.buffer.length) {
+ expand(newlen);
+ }
+ System.arraycopy(b, off, this.buffer, this.len, len);
+ this.len = newlen;
+ }
+
+ public void append(int b) {
+ int newlen = this.len + 1;
+ if (newlen > this.buffer.length) {
+ expand(newlen);
+ }
+ this.buffer[this.len] = (byte)b;
+ this.len = newlen;
+ }
+
+ public void append(final char[] b, int off, int len) {
+ if (b == null) {
+ return;
+ }
+ if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) < 0) || ((off + len) > b.length)) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (len == 0) {
+ return;
+ }
+ int oldlen = this.len;
+ 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;
+ }
+
+ public void append(final CharArrayBuffer b, int off, int len) {
+ if (b == null) {
+ return;
+ }
+ append(b.buffer(), off, len);
+ }
+
+ public void clear() {
+ this.len = 0;
+ }
+
+ public byte[] toByteArray() {
+ byte[] b = new byte[this.len];
+ if (this.len > 0) {
+ System.arraycopy(this.buffer, 0, b, 0, this.len);
+ }
+ return b;
+ }
+
+ public int byteAt(int i) {
+ return this.buffer[i];
+ }
+
+ public int capacity() {
+ return this.buffer.length;
+ }
+
+ public int length() {
+ return this.len;
+ }
+
+ public byte[] buffer() {
+ return this.buffer;
+ }
+
+ public void setLength(int len) {
+ if (len < 0 || len > this.buffer.length) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.len = len;
+ }
+
+ public boolean isEmpty() {
+ return this.len == 0;
+ }
+
+ public boolean isFull() {
+ return this.len == this.buffer.length;
+ }
+
+}
diff --git a/src/org/apache/http/util/CharArrayBuffer.java b/src/org/apache/http/util/CharArrayBuffer.java
new file mode 100644
index 0000000..b89f5d1
--- /dev/null
+++ b/src/org/apache/http/util/CharArrayBuffer.java
@@ -0,0 +1,264 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/CharArrayBuffer.java $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+import org.apache.http.protocol.HTTP;
+
+/**
+ * A resizable char array.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 496070 $
+ *
+ * @since 4.0
+ */
+public final class CharArrayBuffer {
+
+ private char[] buffer;
+ private int len;
+
+ public CharArrayBuffer(int capacity) {
+ super();
+ if (capacity < 0) {
+ throw new IllegalArgumentException("Buffer capacity may not be negative");
+ }
+ this.buffer = new char[capacity];
+ }
+
+ private void expand(int newlen) {
+ char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
+ System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
+ this.buffer = newbuffer;
+ }
+
+ public void append(final char[] b, int off, int len) {
+ if (b == null) {
+ return;
+ }
+ if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) < 0) || ((off + len) > b.length)) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (len == 0) {
+ return;
+ }
+ int newlen = this.len + len;
+ if (newlen > this.buffer.length) {
+ expand(newlen);
+ }
+ System.arraycopy(b, off, this.buffer, this.len, len);
+ this.len = newlen;
+ }
+
+ public void append(String str) {
+ if (str == null) {
+ str = "null";
+ }
+ int strlen = str.length();
+ int newlen = this.len + strlen;
+ if (newlen > this.buffer.length) {
+ expand(newlen);
+ }
+ str.getChars(0, strlen, this.buffer, this.len);
+ this.len = newlen;
+ }
+
+ public void append(final CharArrayBuffer b, int off, int len) {
+ if (b == null) {
+ return;
+ }
+ append(b.buffer, off, len);
+ }
+
+ public void append(final CharArrayBuffer b) {
+ if (b == null) {
+ return;
+ }
+ append(b.buffer,0, b.len);
+ }
+
+ public void append(char ch) {
+ int newlen = this.len + 1;
+ if (newlen > this.buffer.length) {
+ expand(newlen);
+ }
+ this.buffer[this.len] = ch;
+ this.len = newlen;
+ }
+
+ public void append(final byte[] b, int off, int len) {
+ if (b == null) {
+ return;
+ }
+ if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) < 0) || ((off + len) > b.length)) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (len == 0) {
+ return;
+ }
+ int oldlen = this.len;
+ int newlen = oldlen + len;
+ if (newlen > this.buffer.length) {
+ expand(newlen);
+ }
+ for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
+ int ch = b[i1];
+ if (ch < 0) {
+ ch = 256 + ch;
+ }
+ this.buffer[i2] = (char) ch;
+ }
+ this.len = newlen;
+ }
+
+ public void append(final ByteArrayBuffer b, int off, int len) {
+ if (b == null) {
+ return;
+ }
+ append(b.buffer(), off, len);
+ }
+
+ public void append(final Object obj) {
+ append(String.valueOf(obj));
+ }
+
+ public void clear() {
+ this.len = 0;
+ }
+
+ public char[] toCharArray() {
+ char[] b = new char[this.len];
+ if (this.len > 0) {
+ System.arraycopy(this.buffer, 0, b, 0, this.len);
+ }
+ return b;
+ }
+
+ public char charAt(int i) {
+ return this.buffer[i];
+ }
+
+ public char[] buffer() {
+ return this.buffer;
+ }
+
+ public int capacity() {
+ return this.buffer.length;
+ }
+
+ public int length() {
+ return this.len;
+ }
+
+ public void ensureCapacity(int required) {
+ int available = this.buffer.length - this.len;
+ if (required > available) {
+ expand(this.len + required);
+ }
+ }
+
+ public void setLength(int len) {
+ if (len < 0 || len > this.buffer.length) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.len = len;
+ }
+
+ public boolean isEmpty() {
+ return this.len == 0;
+ }
+
+ public boolean isFull() {
+ return this.len == this.buffer.length;
+ }
+
+ public int indexOf(int ch, int beginIndex, int endIndex) {
+ if (beginIndex < 0) {
+ beginIndex = 0;
+ }
+ 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;
+ }
+
+ public int indexOf(int ch) {
+ return indexOf(ch, 0, this.len);
+ }
+
+ public String substring(int beginIndex, int endIndex) {
+ if (beginIndex < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (endIndex > this.len) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (beginIndex > endIndex) {
+ throw new IndexOutOfBoundsException();
+ }
+ return new String(this.buffer, beginIndex, endIndex - beginIndex);
+ }
+
+ public String substringTrimmed(int beginIndex, int endIndex) {
+ if (beginIndex < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (endIndex > this.len) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (beginIndex > endIndex) {
+ throw new IndexOutOfBoundsException();
+ }
+ 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);
+ }
+
+ public String toString() {
+ return new String(this.buffer, 0, this.len);
+ }
+
+}
diff --git a/src/org/apache/http/util/EncodingUtils.java b/src/org/apache/http/util/EncodingUtils.java
new file mode 100644
index 0000000..a1b3f44
--- /dev/null
+++ b/src/org/apache/http/util/EncodingUtils.java
@@ -0,0 +1,185 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/EncodingUtils.java $
+ * $Revision: 503413 $
+ * $Date: 2007-02-04 06:22:14 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.util;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.http.protocol.HTTP;
+
+/**
+ * The home for utility methods that handle various encoding tasks.
+ *
+ * @author Michael Becke
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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,
+ int offset,
+ int length,
+ String charset
+ ) {
+
+ if (data == null) {
+ throw new IllegalArgumentException("Parameter may not be null");
+ }
+
+ if (charset == null || charset.length() == 0) {
+ throw new IllegalArgumentException("charset may not be null or empty");
+ }
+
+ try {
+ return new String(data, offset, length, charset);
+ } catch (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) {
+ if (data == null) {
+ throw new IllegalArgumentException("Parameter may not be null");
+ }
+ 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) {
+
+ if (data == null) {
+ throw new IllegalArgumentException("data may not be null");
+ }
+
+ if (charset == null || charset.length() == 0) {
+ throw new IllegalArgumentException("charset may not be null or empty");
+ }
+
+ try {
+ return data.getBytes(charset);
+ } catch (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) {
+
+ if (data == null) {
+ throw new IllegalArgumentException("Parameter may not be null");
+ }
+
+ try {
+ return data.getBytes(HTTP.US_ASCII);
+ } catch (UnsupportedEncodingException e) {
+ throw new Error("HttpClient requires ASCII support");
+ }
+ }
+
+ /**
+ * 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, int offset, int length) {
+
+ if (data == null) {
+ throw new IllegalArgumentException("Parameter may not be null");
+ }
+
+ try {
+ return new String(data, offset, length, HTTP.US_ASCII);
+ } catch (UnsupportedEncodingException e) {
+ throw new Error("HttpClient requires ASCII support");
+ }
+ }
+
+ /**
+ * 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) {
+ if (data == null) {
+ throw new IllegalArgumentException("Parameter may not be null");
+ }
+ return getAsciiString(data, 0, data.length);
+ }
+
+ /**
+ * This class should not be instantiated.
+ */
+ private EncodingUtils() {
+ }
+
+}
diff --git a/src/org/apache/http/util/EntityUtils.java b/src/org/apache/http/util/EntityUtils.java
new file mode 100644
index 0000000..f9a7cf3
--- /dev/null
+++ b/src/org/apache/http/util/EntityUtils.java
@@ -0,0 +1,149 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/EntityUtils.java $
+ * $Revision: 569637 $
+ * $Date: 2007-08-25 00:36:59 -0700 (Sat, 25 Aug 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Static helpers for dealing with {@link HttpEntity entities}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision: 569637 $
+ *
+ * @since 4.0
+ */
+public final class EntityUtils {
+
+ /** Disabled default constructor. */
+ private EntityUtils() {
+ }
+
+ public static byte[] toByteArray(final HttpEntity entity) throws IOException {
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+ InputStream instream = entity.getContent();
+ if (instream == null) {
+ return new byte[] {};
+ }
+ if (entity.getContentLength() > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
+ }
+ int i = (int)entity.getContentLength();
+ if (i < 0) {
+ i = 4096;
+ }
+ ByteArrayBuffer buffer = new ByteArrayBuffer(i);
+ try {
+ byte[] tmp = new byte[4096];
+ int l;
+ while((l = instream.read(tmp)) != -1) {
+ buffer.append(tmp, 0, l);
+ }
+ } finally {
+ instream.close();
+ }
+ return buffer.toByteArray();
+ }
+
+ public static String getContentCharSet(final HttpEntity entity)
+ throws ParseException {
+
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+ String charset = null;
+ if (entity.getContentType() != null) {
+ HeaderElement values[] = entity.getContentType().getElements();
+ if (values.length > 0) {
+ NameValuePair param = values[0].getParameterByName("charset");
+ if (param != null) {
+ charset = param.getValue();
+ }
+ }
+ }
+ return charset;
+ }
+
+ public static String toString(
+ final HttpEntity entity, final String defaultCharset) throws IOException, ParseException {
+ if (entity == null) {
+ throw new IllegalArgumentException("HTTP entity may not be null");
+ }
+ InputStream instream = entity.getContent();
+ if (instream == null) {
+ return "";
+ }
+ if (entity.getContentLength() > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
+ }
+ int i = (int)entity.getContentLength();
+ if (i < 0) {
+ i = 4096;
+ }
+ String charset = getContentCharSet(entity);
+ if (charset == null) {
+ charset = defaultCharset;
+ }
+ if (charset == null) {
+ charset = HTTP.DEFAULT_CONTENT_CHARSET;
+ }
+ Reader reader = new InputStreamReader(instream, charset);
+ CharArrayBuffer buffer = new CharArrayBuffer(i);
+ try {
+ char[] tmp = new char[1024];
+ int l;
+ while((l = reader.read(tmp)) != -1) {
+ buffer.append(tmp, 0, l);
+ }
+ } finally {
+ reader.close();
+ }
+ return buffer.toString();
+ }
+
+ public static String toString(final HttpEntity entity)
+ throws IOException, ParseException {
+ return toString(entity, null);
+ }
+
+}
diff --git a/src/org/apache/http/util/ExceptionUtils.java b/src/org/apache/http/util/ExceptionUtils.java
new file mode 100644
index 0000000..c7fdccd
--- /dev/null
+++ b/src/org/apache/http/util/ExceptionUtils.java
@@ -0,0 +1,85 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/ExceptionUtils.java $
+ * $Revision: 613003 $
+ * $Date: 2008-01-17 15:00:42 -0800 (Thu, 17 Jan 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+package org.apache.http.util;
+
+import java.lang.reflect.Method;
+
+/**
+ * The home for utility methods that handle various exception-related tasks.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
+ *
+ * @since 4.0
+ */
+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 <code>Method<code> allowing access to
+ * {@link Throwable#initCause(Throwable) initCause} method of {@link Throwable},
+ * or <code>null</code> if the method
+ * does not exist.
+ *
+ * @return A <code>Method<code> for <code>Throwable.initCause</code>, or
+ * <code>null</code> if unavailable.
+ */
+ static private Method getInitCauseMethod() {
+ try {
+ Class[] paramsClasses = new Class[] { Throwable.class };
+ return Throwable.class.getMethod("initCause", paramsClasses);
+ } catch (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(Throwable throwable, Throwable cause) {
+ if (INIT_CAUSE_METHOD != null) {
+ try {
+ INIT_CAUSE_METHOD.invoke(throwable, new Object[] { cause });
+ } catch (Exception e) {
+ // Well, with no logging, the only option is to munch the exception
+ }
+ }
+ }
+
+ private ExceptionUtils() {
+ }
+
+}
diff --git a/src/org/apache/http/util/LangUtils.java b/src/org/apache/http/util/LangUtils.java
new file mode 100644
index 0000000..a1dee8f
--- /dev/null
+++ b/src/org/apache/http/util/LangUtils.java
@@ -0,0 +1,88 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/LangUtils.java $
+ * $Revision: 503413 $
+ * $Date: 2007-02-04 06:22:14 -0800 (Sun, 04 Feb 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+/**
+ * A set of utility methods to help produce consistent
+ * {@link Object#equals equals} and {@link Object#hashCode hashCode} methods.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @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);
+ }
+
+ public static boolean equals(final Object obj1, final Object obj2) {
+ return obj1 == null ? obj2 == null : obj1.equals(obj2);
+ }
+
+ public static boolean equals(final Object[] a1, final Object[] a2) {
+ if (a1 == null) {
+ if (a2 == null) {
+ return true;
+ } else {
+ return false;
+ }
+ } 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/src/org/apache/http/util/VersionInfo.java b/src/org/apache/http/util/VersionInfo.java
new file mode 100644
index 0000000..0c95594
--- /dev/null
+++ b/src/org/apache/http/util/VersionInfo.java
@@ -0,0 +1,317 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/VersionInfo.java $
+ * $Revision: 554888 $
+ * $Date: 2007-07-10 02:46:36 -0700 (Tue, 10 Jul 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ArrayList;
+
+
+/**
+ * Provides access to version information for HTTP components.
+ * Instances of this class provide version information for a single module
+ * or informal unit, as explained
+ * <a href="http://wiki.apache.org/jakarta-httpclient/HttpComponents">here</a>.
+ * Static methods are used to extract version information from property
+ * files that are automatically packaged with HTTP component release JARs.
+ * <br/>
+ * 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.
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ * @author and others
+ */
+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 <code>null</code>
+ * @param release the release, or <code>null</code>
+ * @param time the build time, or <code>null</code>
+ * @param clsldr the class loader, or <code>null</code>
+ */
+ protected VersionInfo(String pckg, String module,
+ String release, String time, String clsldr) {
+ if (pckg == null) {
+ throw new IllegalArgumentException
+ ("Package identifier must not be null.");
+ }
+
+ 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 <code>null</code>
+ */
+ 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 <code>null</code>
+ */
+ 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 <code>null</code>
+ */
+ 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 <code>null</code>
+ */
+ public final String getTimestamp() {
+ return infoTimestamp;
+ }
+
+ /**
+ * Obtains the classloader used to read the version information.
+ * This is just the <code>toString</code> 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 <code>null</code>
+ */
+ public final String getClassloader() {
+ return infoClassloader;
+ }
+
+
+ /**
+ * Provides the version information in human-readable format.
+ *
+ * @return a string holding this version information
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer
+ (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
+ * <code>null</code> for the thread context classloader
+ *
+ * @return the version information for all packages found,
+ * never <code>null</code>
+ */
+ public final static VersionInfo[] loadVersionInfo(String[] pckgs,
+ ClassLoader clsldr) {
+ if (pckgs == null) {
+ throw new IllegalArgumentException
+ ("Package identifier list must not be null.");
+ }
+
+ ArrayList vil = new ArrayList(pckgs.length);
+ for (int i=0; i<pckgs.length; i++) {
+ VersionInfo vi = loadVersionInfo(pckgs[i], clsldr);
+ if (vi != null)
+ vil.add(vi);
+ }
+
+ return (VersionInfo[]) vil.toArray(new VersionInfo[vil.size()]);
+ }
+
+
+ /**
+ * Loads version information for a package.
+ *
+ * @param pckg the package for which to load version information,
+ * for example "org.apache.http".
+ * The package name should NOT end with a dot.
+ * @param clsldr the classloader to load from, or
+ * <code>null</code> for the thread context classloader
+ *
+ * @return the version information for the argument package, or
+ * <code>null</code> if not available
+ */
+ public final static VersionInfo loadVersionInfo(final String pckg,
+ ClassLoader clsldr) {
+ if (pckg == null) {
+ throw new IllegalArgumentException
+ ("Package identifier must not be null.");
+ }
+
+ if (clsldr == null)
+ clsldr = Thread.currentThread().getContextClassLoader();
+
+ Properties vip = null; // version info properties, if available
+ try {
+ // org.apache.http becomes
+ // org/apache/http/version.properties
+ InputStream is = clsldr.getResourceAsStream
+ (pckg.replace('.', '/') + "/" + VERSION_PROPERTY_FILE);
+ if (is != null) {
+ try {
+ Properties props = new Properties();
+ props.load(is);
+ vip = props;
+ } finally {
+ is.close();
+ }
+ }
+ } catch (IOException ex) {
+ // shamelessly munch this exception
+ }
+
+ VersionInfo result = null;
+ if (vip != null)
+ result = fromMap(pckg, vip, clsldr);
+
+ 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 <code>null</code>
+ *
+ * @return the version information
+ */
+ protected final static VersionInfo fromMap(String pckg, Map info,
+ ClassLoader clsldr) {
+ if (pckg == null) {
+ throw new IllegalArgumentException
+ ("Package identifier must not be null.");
+ }
+
+ 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);
+ }
+
+} // class VersionInfo
diff --git a/src/org/apache/http/util/package.html b/src/org/apache/http/util/package.html
new file mode 100644
index 0000000..19d97b3
--- /dev/null
+++ b/src/org/apache/http/util/package.html
@@ -0,0 +1,45 @@
+<html>
+<head>
+<!--
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/util/package.html $
+ * $Revision: 496070 $
+ * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists 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://www.apache.org/>.
+ *
+ */
+-->
+</head>
+<body>
+Mostly utility classes with static helper methods for various purposes.
+
+The helper classes for resizable
+{@link org.apache.http.util.ByteArrayBuffer byte} and
+{@link org.apache.http.util.CharArrayBuffer char} arrays
+do not fall into this category.
+
+</body>
+</html>