Hi, Jersey Folk:
We're using Jersey Client 1.13 with Apache Http Client 4.
We've noticed that when doing a PUT of StreamingOutput with Digest
authentication,
the connection that receives the initial 401 challenge is never released.
The problem seems to reside in
com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle()
If I add an abort() call immediately before the happy path return on line
186
if (r.getStatus() == 401 && !request.isAborted()) {
request.abort();
}
that change seems to fix the problem. (Notice the highlighted difference
between
the before and after debug streams after my signature.)
That change does pollute handle() with authentication concerns (but then,
preemptiveBasicAuth also has that issue).
Is there a way to solve this problem without modifying the
ApacheHttpClient4Handler source (or a better modification to the source)?
Thanks in advance,
Erik Hennum
BEFORE:
[DEBUG] ThreadSafeClientConnManager - Get connection: HttpRoute[{}->
http://localhost:8012], timeout = 0
[DEBUG] ConnPoolByRoute - [HttpRoute[{}->
http://localhost:8012]] total kept
alive: 0, total issued: 0, total allocated: 0 out of 20
[DEBUG] ConnPoolByRoute - No free connections
[HttpRoute[{}->
http://localhost:8012]][null]
[DEBUG] ConnPoolByRoute - Available capacity: 50 out of 50
[HttpRoute[{}->
http://localhost:8012]][null]
[DEBUG] ConnPoolByRoute - Creating new connection [HttpRoute[{}->
http://localhost:8012]]
[DEBUG] DefaultClientConnectionOperator - Connecting to localhost/
127.0.0.1:8012
[DEBUG] RequestAddCookies - CookieSpec selected: best-match
[DEBUG] RequestAuthCache - Auth cache not set in the context
[DEBUG] DefaultHttpClient - Attempt 1 to execute request
[DEBUG] DefaultClientConnection - Sending request: PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> Content-Type: application/xml
[DEBUG] headers - >> Transfer-Encoding: chunked
[DEBUG] headers - >> Host: localhost:8012
[DEBUG] headers - >> Connection: Keep-Alive
1 * Client out-bound request
1 > PUT
http://localhost:8012/v1/documents?category=content&uri=/test/testWrite1.xml
1 > Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<root foo="bar" xml:lang="en"><child/>mixed</root>
[DEBUG] DefaultClientConnection - Receiving response: HTTP/1.1 401
Unauthorized
[DEBUG] headers - << HTTP/1.1 401 Unauthorized
[DEBUG] headers - << WWW-Authenticate: Digest realm="public", qop="auth",
nonce="8d8766cd4a59ec4e42f98661ae2cfb7b", opaque="e05d2c24fc33c472"
[DEBUG] headers - << Content-type: application/xml
[DEBUG] headers - << Server: MarkLogic
[DEBUG] headers - << Content-Length: 211
[DEBUG] headers - << Connection: close
[DEBUG] DefaultHttpClient - Target requested authentication
[DEBUG] DefaultTargetAuthenticationHandler - Authentication schemes in the
order of preference: [negotiate, NTLM, Digest, Basic]
[DEBUG] DefaultTargetAuthenticationHandler - Challenge for negotiate
authentication scheme not available
[DEBUG] DefaultTargetAuthenticationHandler - Challenge for NTLM
authentication scheme not available
[DEBUG] DefaultTargetAuthenticationHandler - Digest authentication scheme
selected
[DEBUG] DefaultHttpClient - Authorization challenge processed
[DEBUG] DefaultHttpClient - Authentication scope: DIGEST 'public'_at_localhost
:8012
[DEBUG] DefaultHttpClient - Credentials not found
VVVVVVVVVVVVVVV
[DEBUG] ThreadSafeClientConnManager - Get connection: HttpRoute[{}->
http://localhost:8012], timeout = 0
[DEBUG] ConnPoolByRoute - [HttpRoute[{}->
http://localhost:8012]] total kept
alive: 0, total issued: 1, total allocated: 1 out of 20
[DEBUG] ConnPoolByRoute - No free connections
[HttpRoute[{}->
http://localhost:8012]][null]
[DEBUG] ConnPoolByRoute - Available capacity: 49 out of 50
[HttpRoute[{}->
http://localhost:8012]][null]
^^^^^^^^^^^^^^^^^^^^^^
[DEBUG] ConnPoolByRoute - Creating new connection [HttpRoute[{}->
http://localhost:8012]]
[DEBUG] DefaultClientConnectionOperator - Connecting to localhost/
127.0.0.1:8012
[DEBUG] RequestAddCookies - CookieSpec selected: best-match
[DEBUG] RequestAuthCache - Auth cache not set in the context
[DEBUG] DefaultHttpClient - Attempt 1 to execute request
[DEBUG] DefaultClientConnection - Sending request: PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> Content-Type: application/xml
[DEBUG] headers - >> Authorization: Digest
username="rest-writer",realm="public",nonce="8d8766cd4a59ec4e42f98661ae2cfb7b",opaque="e05d2c24fc33c472",qop=auth,uri="/v1/documents",cnonce="45eba063",nc=00000001,response="18f8c3417cca89a3bc6e62a40e2743cd"
[DEBUG] headers - >> Transfer-Encoding: chunked
[DEBUG] headers - >> Host: localhost:8012
[DEBUG] headers - >> Connection: Keep-Alive
1 * Client out-bound request
1 > PUT
http://localhost:8012/v1/documents?category=content&uri=/test/testWrite1.xml
1 > Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<root foo="bar" xml:lang="en"><child/>mixed</root>
<?xml version="1.0" encoding="UTF-8"?>
<root foo="bar" xml:lang="en"><child/>mixed</root>
[DEBUG] DefaultClientConnection - Receiving response: HTTP/1.1 204 Content
Updated
[DEBUG] headers - << HTTP/1.1 204 Content Updated
[DEBUG] headers - << Server: MarkLogic
[DEBUG] headers - << Content-Length: 0
[DEBUG] headers - << Connection: close
AFTER:
[DEBUG] ThreadSafeClientConnManager - Get connection: HttpRoute[{}->
http://localhost:8012], timeout = 0
[DEBUG] ConnPoolByRoute - [HttpRoute[{}->
http://localhost:8012]] total kept
alive: 0, total issued: 0, total allocated: 0 out of 20
[DEBUG] ConnPoolByRoute - No free connections
[HttpRoute[{}->
http://localhost:8012]][null]
[DEBUG] ConnPoolByRoute - Available capacity: 50 out of 50
[HttpRoute[{}->
http://localhost:8012]][null]
[DEBUG] ConnPoolByRoute - Creating new connection [HttpRoute[{}->
http://localhost:8012]]
[DEBUG] DefaultClientConnectionOperator - Connecting to localhost/
127.0.0.1:8012
[DEBUG] RequestAddCookies - CookieSpec selected: best-match
[DEBUG] RequestAuthCache - Auth cache not set in the context
[DEBUG] DefaultHttpClient - Attempt 1 to execute request
[DEBUG] DefaultClientConnection - Sending request: PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> Content-Type: application/xml
[DEBUG] headers - >> Transfer-Encoding: chunked
[DEBUG] headers - >> Host: localhost:8012
[DEBUG] headers - >> Connection: Keep-Alive
1 * Client out-bound request
1 > PUT
http://localhost:8012/v1/documents?category=content&uri=/test/testWrite1.xml
1 > Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<root foo="bar" xml:lang="en"><child/>mixed</root>
[DEBUG] DefaultClientConnection - Receiving response: HTTP/1.1 401
Unauthorized
[DEBUG] headers - << HTTP/1.1 401 Unauthorized
[DEBUG] headers - << WWW-Authenticate: Digest realm="public", qop="auth",
nonce="87f4589fadfe84bcd45fa63de82fb78a", opaque="d6cc3857b5f552ec"
[DEBUG] headers - << Content-type: application/xml
[DEBUG] headers - << Server: MarkLogic
[DEBUG] headers - << Content-Length: 211
[DEBUG] headers - << Connection: close
[DEBUG] DefaultHttpClient - Target requested authentication
[DEBUG] DefaultTargetAuthenticationHandler - Authentication schemes in the
order of preference: [negotiate, NTLM, Digest, Basic]
[DEBUG] DefaultTargetAuthenticationHandler - Challenge for negotiate
authentication scheme not available
[DEBUG] DefaultTargetAuthenticationHandler - Challenge for NTLM
authentication scheme not available
[DEBUG] DefaultTargetAuthenticationHandler - Digest authentication scheme
selected
[DEBUG] DefaultHttpClient - Authorization challenge processed
[DEBUG] DefaultHttpClient - Authentication scope: DIGEST 'public'_at_localhost
:8012
[DEBUG] DefaultHttpClient - Credentials not found
VVVVVVVVVVVVVVV
[DEBUG] DefaultClientConnection - Connection shut down
[DEBUG] ThreadSafeClientConnManager - Released connection is not reusable.
[DEBUG] ConnPoolByRoute - Releasing connection
[HttpRoute[{}->
http://localhost:8012]][null]
[DEBUG] ConnPoolByRoute - Notifying no-one, there are no waiting threads
[DEBUG] ThreadSafeClientConnManager - Get connection: HttpRoute[{}->
http://localhost:8012], timeout = 0
[DEBUG] ConnPoolByRoute - [HttpRoute[{}->
http://localhost:8012]] total kept
alive: 0, total issued: 0, total allocated: 0 out of 20
[DEBUG] ConnPoolByRoute - No free connections
[HttpRoute[{}->
http://localhost:8012]][null]
[DEBUG] ConnPoolByRoute - Available capacity: 50 out of 50
[HttpRoute[{}->
http://localhost:8012]][null]
^^^^^^^^^^^^^^^^^^^^^^
[DEBUG] ConnPoolByRoute - Creating new connection [HttpRoute[{}->
http://localhost:8012]]
[DEBUG] DefaultClientConnectionOperator - Connecting to localhost/
127.0.0.1:8012
[DEBUG] RequestAddCookies - CookieSpec selected: best-match
[DEBUG] RequestAuthCache - Auth cache not set in the context
[DEBUG] DefaultHttpClient - Attempt 1 to execute request
[DEBUG] DefaultClientConnection - Sending request: PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> PUT
/v1/documents?category=content&uri=/test/testWrite1.xml HTTP/1.1
[DEBUG] headers - >> Content-Type: application/xml
[DEBUG] headers - >> Authorization: Digest
username="rest-writer",realm="public",nonce="87f4589fadfe84bcd45fa63de82fb78a",opaque="d6cc3857b5f552ec",qop=auth,uri="/v1/documents",cnonce="2ef03f7b",nc=00000001,response="b9724b850a75a38d34e2ef9b51371780"
[DEBUG] headers - >> Transfer-Encoding: chunked
[DEBUG] headers - >> Host: localhost:8012
[DEBUG] headers - >> Connection: Keep-Alive
1 * Client out-bound request
1 > PUT
http://localhost:8012/v1/documents?category=content&uri=/test/testWrite1.xml
1 > Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<root foo="bar" xml:lang="en"><child/>mixed</root>
<?xml version="1.0" encoding="UTF-8"?>
<root foo="bar" xml:lang="en"><child/>mixed</root>
[DEBUG] DefaultClientConnection - Receiving response: HTTP/1.1 204 Content
Updated
[DEBUG] headers - << HTTP/1.1 204 Content Updated
[DEBUG] headers - << Server: MarkLogic
[DEBUG] headers - << Content-Length: 0
[DEBUG] headers - << Connection: close