mirror of
https://github.com/apache/httpcomponents-client.git
synced 2025-02-28 13:49:13 +00:00
ByteArrayBuilder class to build byte sequences; BasicScheme and DigestScheme optimized to generate less intermediate garbage
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1695156 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e52e269dc8
commit
d09ae9707d
@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on 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.util;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.CharacterCodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharsetEncoder;
|
||||||
|
import java.nio.charset.CoderResult;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
|
||||||
|
import org.apache.http.Consts;
|
||||||
|
import org.apache.http.annotation.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder class for sequences of bytes.
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public class ByteArrayBuilder {
|
||||||
|
|
||||||
|
private CharsetEncoder charsetEncoder;
|
||||||
|
private ByteBuffer buffer;
|
||||||
|
|
||||||
|
public ByteArrayBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder(final int initialCapacity) {
|
||||||
|
this.buffer = ByteBuffer.allocate(initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int capacity() {
|
||||||
|
return this.buffer != null ? this.buffer.capacity() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ByteBuffer ensureFreeCapacity(final ByteBuffer buffer, final int capacity) {
|
||||||
|
if (buffer == null) {
|
||||||
|
return ByteBuffer.allocate(capacity);
|
||||||
|
}
|
||||||
|
if (buffer.remaining() < capacity) {
|
||||||
|
final ByteBuffer newBuffer = ByteBuffer.allocate(buffer.position() + capacity);
|
||||||
|
buffer.flip();
|
||||||
|
newBuffer.put(buffer);
|
||||||
|
return newBuffer;
|
||||||
|
} else {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ByteBuffer encode(
|
||||||
|
final ByteBuffer buffer, final CharBuffer in, final CharsetEncoder encoder) throws CharacterCodingException {
|
||||||
|
|
||||||
|
final int capacity = (int) (in.remaining() * encoder.averageBytesPerChar());
|
||||||
|
ByteBuffer out = ensureFreeCapacity(buffer, capacity);
|
||||||
|
for (;;) {
|
||||||
|
CoderResult result = in.hasRemaining() ? encoder.encode(in, out, true) : CoderResult.UNDERFLOW;
|
||||||
|
if (result.isError()) {
|
||||||
|
result.throwException();
|
||||||
|
}
|
||||||
|
if (result.isUnderflow()) {
|
||||||
|
result = encoder.flush(out);
|
||||||
|
}
|
||||||
|
if (result.isUnderflow()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (result.isOverflow()) {
|
||||||
|
out = ensureFreeCapacity(out, capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensureFreeCapacity(final int freeCapacity) {
|
||||||
|
this.buffer = ensureFreeCapacity(this.buffer, freeCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAppend(final CharBuffer charBuffer) {
|
||||||
|
if (this.charsetEncoder == null) {
|
||||||
|
this.charsetEncoder = Consts.ASCII.newEncoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.IGNORE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
}
|
||||||
|
this.charsetEncoder.reset();
|
||||||
|
try {
|
||||||
|
this.buffer = encode(this.buffer, charBuffer, this.charsetEncoder);
|
||||||
|
} catch (CharacterCodingException ex) {
|
||||||
|
// Should never happen
|
||||||
|
throw new IllegalStateException("Unexpected character coding error", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder charset(final Charset charset) {
|
||||||
|
if (charset == null) {
|
||||||
|
this.charsetEncoder = null;
|
||||||
|
} else {
|
||||||
|
this.charsetEncoder = charset.newEncoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.IGNORE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder append(final byte[] b, final int off, final int len) {
|
||||||
|
if (b == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||||
|
((off + len) < 0) || ((off + len) > b.length)) {
|
||||||
|
throw new IndexOutOfBoundsException("off: " + off + " len: " + len + " b.length: " + b.length);
|
||||||
|
}
|
||||||
|
ensureFreeCapacity(len);
|
||||||
|
this.buffer.put(b, off, len);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder append(final byte[] b) {
|
||||||
|
if (b == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return append(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder append(final CharBuffer charBuffer) {
|
||||||
|
if (charBuffer == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
doAppend(charBuffer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder append(final char[] b, final int off, final int len) {
|
||||||
|
if (b == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||||
|
((off + len) < 0) || ((off + len) > b.length)) {
|
||||||
|
throw new IndexOutOfBoundsException("off: " + off + " len: " + len + " b.length: " + b.length);
|
||||||
|
}
|
||||||
|
return append(CharBuffer.wrap(b, off, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder append(final char[] b) {
|
||||||
|
if (b == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return append(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayBuilder append(final String s) {
|
||||||
|
if (s == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return append(CharBuffer.wrap(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer toByteBuffer() {
|
||||||
|
return this.buffer != null ? this.buffer.duplicate() : ByteBuffer.allocate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toByteArray() {
|
||||||
|
if (this.buffer == null) {
|
||||||
|
return new byte[] {};
|
||||||
|
} else {
|
||||||
|
this.buffer.flip();
|
||||||
|
final byte[] b = new byte[this.buffer.remaining()];
|
||||||
|
this.buffer.get(b);
|
||||||
|
this.buffer.clear();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
if (this.charsetEncoder != null) {
|
||||||
|
this.charsetEncoder.reset();
|
||||||
|
}
|
||||||
|
if (this.buffer != null) {
|
||||||
|
this.buffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.buffer != null ? this.buffer.toString() : "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -51,10 +51,10 @@
|
|||||||
import org.apache.http.auth.Credentials;
|
import org.apache.http.auth.Credentials;
|
||||||
import org.apache.http.auth.CredentialsProvider;
|
import org.apache.http.auth.CredentialsProvider;
|
||||||
import org.apache.http.auth.MalformedChallengeException;
|
import org.apache.http.auth.MalformedChallengeException;
|
||||||
|
import org.apache.http.auth.util.ByteArrayBuilder;
|
||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
import org.apache.http.util.Args;
|
import org.apache.http.util.Args;
|
||||||
import org.apache.http.util.CharsetUtils;
|
import org.apache.http.util.CharsetUtils;
|
||||||
import org.apache.http.util.EncodingUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic authentication scheme as defined in RFC 2617.
|
* Basic authentication scheme as defined in RFC 2617.
|
||||||
@ -68,7 +68,10 @@ public class BasicScheme implements AuthScheme, Serializable {
|
|||||||
|
|
||||||
private final Map<String, String> paramMap;
|
private final Map<String, String> paramMap;
|
||||||
private transient Charset charset;
|
private transient Charset charset;
|
||||||
|
private transient ByteArrayBuilder buffer;
|
||||||
|
private transient Base64 base64codec;
|
||||||
private boolean complete;
|
private boolean complete;
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@ -160,12 +163,17 @@ public String generateAuthResponse(
|
|||||||
final HttpHost host,
|
final HttpHost host,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final HttpContext context) throws AuthenticationException {
|
final HttpContext context) throws AuthenticationException {
|
||||||
final StringBuilder buffer = new StringBuilder();
|
if (this.buffer == null) {
|
||||||
buffer.append(this.username);
|
this.buffer = new ByteArrayBuilder(64).charset(this.charset);
|
||||||
buffer.append(":");
|
} else {
|
||||||
buffer.append(this.password);
|
this.buffer.reset();
|
||||||
final Base64 base64codec = new Base64(0);
|
}
|
||||||
final byte[] encodedCreds = base64codec.encode(EncodingUtils.getBytes(buffer.toString(), charset.name()));
|
this.buffer.append(this.username).append(":").append(this.password);
|
||||||
|
if (this.base64codec == null) {
|
||||||
|
this.base64codec = new Base64(0);
|
||||||
|
}
|
||||||
|
final byte[] encodedCreds = this.base64codec.encode(this.buffer.toByteArray());
|
||||||
|
this.buffer.reset();
|
||||||
return "Basic " + new String(encodedCreds, 0, encodedCreds.length, Consts.ASCII);
|
return "Basic " + new String(encodedCreds, 0, encodedCreds.length, Consts.ASCII);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
@ -41,6 +42,7 @@
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.apache.http.Consts;
|
||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
import org.apache.http.HttpEntityEnclosingRequest;
|
import org.apache.http.HttpEntityEnclosingRequest;
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
@ -54,12 +56,13 @@
|
|||||||
import org.apache.http.auth.Credentials;
|
import org.apache.http.auth.Credentials;
|
||||||
import org.apache.http.auth.CredentialsProvider;
|
import org.apache.http.auth.CredentialsProvider;
|
||||||
import org.apache.http.auth.MalformedChallengeException;
|
import org.apache.http.auth.MalformedChallengeException;
|
||||||
|
import org.apache.http.auth.util.ByteArrayBuilder;
|
||||||
import org.apache.http.message.BasicHeaderValueFormatter;
|
import org.apache.http.message.BasicHeaderValueFormatter;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
import org.apache.http.util.Args;
|
import org.apache.http.util.Args;
|
||||||
import org.apache.http.util.CharArrayBuffer;
|
import org.apache.http.util.CharArrayBuffer;
|
||||||
import org.apache.http.util.EncodingUtils;
|
import org.apache.http.util.CharsetUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Digest authentication scheme as defined in RFC 2617.
|
* Digest authentication scheme as defined in RFC 2617.
|
||||||
@ -84,7 +87,7 @@ public class DigestScheme implements AuthScheme, Serializable {
|
|||||||
* Hexa values used when creating 32 character long digest in HTTP DigestScheme
|
* Hexa values used when creating 32 character long digest in HTTP DigestScheme
|
||||||
* in case of authentication.
|
* in case of authentication.
|
||||||
*
|
*
|
||||||
* @see #encode(byte[])
|
* @see #formatHex(byte[])
|
||||||
*/
|
*/
|
||||||
private static final char[] HEXADECIMAL = {
|
private static final char[] HEXADECIMAL = {
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
|
||||||
@ -98,11 +101,13 @@ public class DigestScheme implements AuthScheme, Serializable {
|
|||||||
|
|
||||||
private final Map<String, String> paramMap;
|
private final Map<String, String> paramMap;
|
||||||
private boolean complete;
|
private boolean complete;
|
||||||
|
private transient ByteArrayBuilder buffer;
|
||||||
|
|
||||||
private String lastNonce;
|
private String lastNonce;
|
||||||
private long nounceCount;
|
private long nounceCount;
|
||||||
private String cnonce;
|
private String cnonce;
|
||||||
private String a1;
|
private byte[] a1;
|
||||||
private String a2;
|
private byte[] a2;
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
@ -251,9 +256,10 @@ private String createDigestResponse(final HttpRequest request) throws Authentica
|
|||||||
throw new AuthenticationException("None of the qop methods is supported: " + qoplist);
|
throw new AuthenticationException("None of the qop methods is supported: " + qoplist);
|
||||||
}
|
}
|
||||||
|
|
||||||
String charset = this.paramMap.get("charset");
|
final String charsetName = this.paramMap.get("charset");
|
||||||
|
Charset charset = charsetName != null ? CharsetUtils.lookup(charsetName) : null;
|
||||||
if (charset == null) {
|
if (charset == null) {
|
||||||
charset = "ISO-8859-1";
|
charset = Consts.ISO_8859_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
String digAlg = algorithm;
|
String digAlg = algorithm;
|
||||||
@ -275,16 +281,24 @@ private String createDigestResponse(final HttpRequest request) throws Authentica
|
|||||||
cnonce = null;
|
cnonce = null;
|
||||||
lastNonce = nonce;
|
lastNonce = nonce;
|
||||||
}
|
}
|
||||||
final StringBuilder sb = new StringBuilder(256);
|
|
||||||
|
final StringBuilder sb = new StringBuilder(8);
|
||||||
final Formatter formatter = new Formatter(sb, Locale.US);
|
final Formatter formatter = new Formatter(sb, Locale.US);
|
||||||
formatter.format("%08x", Long.valueOf(nounceCount));
|
formatter.format("%08x", nounceCount);
|
||||||
formatter.close();
|
formatter.close();
|
||||||
final String nc = sb.toString();
|
final String nc = sb.toString();
|
||||||
|
|
||||||
if (cnonce == null) {
|
if (cnonce == null) {
|
||||||
cnonce = createCnonce();
|
cnonce = formatHex(createCnonce());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buffer == null) {
|
||||||
|
buffer = new ByteArrayBuilder();
|
||||||
|
} else {
|
||||||
|
buffer.reset();
|
||||||
|
}
|
||||||
|
buffer.charset(charset);
|
||||||
|
|
||||||
a1 = null;
|
a1 = null;
|
||||||
a2 = null;
|
a2 = null;
|
||||||
// 3.2.2.2: Calculating digest
|
// 3.2.2.2: Calculating digest
|
||||||
@ -294,24 +308,23 @@ private String createDigestResponse(final HttpRequest request) throws Authentica
|
|||||||
// ":" unq(cnonce-value)
|
// ":" unq(cnonce-value)
|
||||||
|
|
||||||
// calculated one per session
|
// calculated one per session
|
||||||
sb.setLength(0);
|
buffer.append(username).append(":").append(realm).append(":").append(password);
|
||||||
sb.append(username).append(':').append(realm).append(':').append(password);
|
final String checksum = formatHex(digester.digest(this.buffer.toByteArray()));
|
||||||
final String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset)));
|
buffer.reset();
|
||||||
sb.setLength(0);
|
buffer.append(checksum).append(":").append(nonce).append(":").append(cnonce);
|
||||||
sb.append(checksum).append(':').append(nonce).append(':').append(cnonce);
|
a1 = buffer.toByteArray();
|
||||||
a1 = sb.toString();
|
|
||||||
} else {
|
} else {
|
||||||
// unq(username-value) ":" unq(realm-value) ":" passwd
|
// unq(username-value) ":" unq(realm-value) ":" passwd
|
||||||
sb.setLength(0);
|
buffer.append(username).append(":").append(realm).append(":").append(password);
|
||||||
sb.append(username).append(':').append(realm).append(':').append(password);
|
a1 = buffer.toByteArray();
|
||||||
a1 = sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String hasha1 = encode(digester.digest(EncodingUtils.getBytes(a1, charset)));
|
final String hasha1 = formatHex(digester.digest(a1));
|
||||||
|
buffer.reset();
|
||||||
|
|
||||||
if (qop == QOP_AUTH) {
|
if (qop == QOP_AUTH) {
|
||||||
// Method ":" digest-uri-value
|
// Method ":" digest-uri-value
|
||||||
a2 = method + ':' + uri;
|
a2 = buffer.append(method).append(":").append(uri).toByteArray();
|
||||||
} else if (qop == QOP_AUTH_INT) {
|
} else if (qop == QOP_AUTH_INT) {
|
||||||
// Method ":" digest-uri-value ":" H(entity-body)
|
// Method ":" digest-uri-value ":" H(entity-body)
|
||||||
HttpEntity entity = null;
|
HttpEntity entity = null;
|
||||||
@ -322,7 +335,7 @@ private String createDigestResponse(final HttpRequest request) throws Authentica
|
|||||||
// If the entity is not repeatable, try falling back onto QOP_AUTH
|
// If the entity is not repeatable, try falling back onto QOP_AUTH
|
||||||
if (qopset.contains("auth")) {
|
if (qopset.contains("auth")) {
|
||||||
qop = QOP_AUTH;
|
qop = QOP_AUTH;
|
||||||
a2 = method + ':' + uri;
|
a2 = buffer.append(method).append(":").append(uri).toByteArray();
|
||||||
} else {
|
} else {
|
||||||
throw new AuthenticationException("Qop auth-int cannot be used with " +
|
throw new AuthenticationException("Qop auth-int cannot be used with " +
|
||||||
"a non-repeatable entity");
|
"a non-repeatable entity");
|
||||||
@ -337,30 +350,31 @@ private String createDigestResponse(final HttpRequest request) throws Authentica
|
|||||||
} catch (final IOException ex) {
|
} catch (final IOException ex) {
|
||||||
throw new AuthenticationException("I/O error reading entity content", ex);
|
throw new AuthenticationException("I/O error reading entity content", ex);
|
||||||
}
|
}
|
||||||
a2 = method + ':' + uri + ':' + encode(entityDigester.getDigest());
|
a2 = buffer.append(method).append(":").append(uri)
|
||||||
|
.append(":").append(formatHex(entityDigester.getDigest())).toByteArray();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
a2 = method + ':' + uri;
|
a2 = buffer.append(method).append(":").append(uri).toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
final String hasha2 = encode(digester.digest(EncodingUtils.getBytes(a2, charset)));
|
final String hasha2 = formatHex(digester.digest(a2));
|
||||||
|
buffer.reset();
|
||||||
|
|
||||||
// 3.2.2.1
|
// 3.2.2.1
|
||||||
|
|
||||||
final String digestValue;
|
final byte[] digestInput;
|
||||||
if (qop == QOP_MISSING) {
|
if (qop == QOP_MISSING) {
|
||||||
sb.setLength(0);
|
buffer.append(hasha1).append(":").append(nonce).append(":").append(hasha2);
|
||||||
sb.append(hasha1).append(':').append(nonce).append(':').append(hasha2);
|
digestInput = buffer.toByteArray();
|
||||||
digestValue = sb.toString();
|
|
||||||
} else {
|
} else {
|
||||||
sb.setLength(0);
|
buffer.append(hasha1).append(":").append(nonce).append(":").append(nc).append(":")
|
||||||
sb.append(hasha1).append(':').append(nonce).append(':').append(nc).append(':')
|
.append(cnonce).append(":").append(qop == QOP_AUTH_INT ? "auth-int" : "auth")
|
||||||
.append(cnonce).append(':').append(qop == QOP_AUTH_INT ? "auth-int" : "auth")
|
.append(":").append(hasha2);
|
||||||
.append(':').append(hasha2);
|
digestInput = buffer.toByteArray();
|
||||||
digestValue = sb.toString();
|
|
||||||
}
|
}
|
||||||
|
buffer.reset();
|
||||||
|
|
||||||
final String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue)));
|
final String digest = formatHex(digester.digest(digestInput));
|
||||||
|
|
||||||
final CharArrayBuffer buffer = new CharArrayBuffer(128);
|
final CharArrayBuffer buffer = new CharArrayBuffer(128);
|
||||||
buffer.append("Digest ");
|
buffer.append("Digest ");
|
||||||
@ -401,11 +415,11 @@ String getCnonce() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getA1() {
|
String getA1() {
|
||||||
return a1;
|
return a1 != null ? new String(a1, Consts.ASCII) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getA2() {
|
String getA2() {
|
||||||
return a2;
|
return a2 != null ? new String(a2, Consts.ASCII) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -415,7 +429,7 @@ String getA2() {
|
|||||||
* @param binaryData array containing the digest
|
* @param binaryData array containing the digest
|
||||||
* @return encoded MD5, or <CODE>null</CODE> if encoding failed
|
* @return encoded MD5, or <CODE>null</CODE> if encoding failed
|
||||||
*/
|
*/
|
||||||
static String encode(final byte[] binaryData) {
|
static String formatHex(final byte[] binaryData) {
|
||||||
final int n = binaryData.length;
|
final int n = binaryData.length;
|
||||||
final char[] buffer = new char[n * 2];
|
final char[] buffer = new char[n * 2];
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
@ -433,11 +447,11 @@ static String encode(final byte[] binaryData) {
|
|||||||
*
|
*
|
||||||
* @return The cnonce value as String.
|
* @return The cnonce value as String.
|
||||||
*/
|
*/
|
||||||
public static String createCnonce() {
|
static byte[] createCnonce() {
|
||||||
final SecureRandom rnd = new SecureRandom();
|
final SecureRandom rnd = new SecureRandom();
|
||||||
final byte[] tmp = new byte[8];
|
final byte[] tmp = new byte[8];
|
||||||
rnd.nextBytes(tmp);
|
rnd.nextBytes(tmp);
|
||||||
return encode(tmp);
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.apache.http.auth.util;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.apache.http.Consts;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ByteArrayBuilder} test cases.
|
||||||
|
*/
|
||||||
|
public class TestByteArrayBuilder {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyBuffer() throws Exception {
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
final ByteBuffer byteBuffer = buffer.toByteBuffer();
|
||||||
|
Assert.assertNotNull(byteBuffer);
|
||||||
|
Assert.assertEquals(0, byteBuffer.capacity());
|
||||||
|
|
||||||
|
final byte[] bytes = buffer.toByteArray();
|
||||||
|
Assert.assertNotNull(bytes);
|
||||||
|
Assert.assertEquals(0, bytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAppendBytes() throws Exception {
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
buffer.append(new byte[]{1, 2, 3, 4, 5});
|
||||||
|
buffer.append(new byte[]{3, 4, 5, 6, 7, 8, 9, 10, 11}, 3, 5);
|
||||||
|
buffer.append((byte[]) null);
|
||||||
|
|
||||||
|
final byte[] bytes = buffer.toByteArray();
|
||||||
|
Assert.assertNotNull(bytes);
|
||||||
|
Assert.assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidAppendBytes() throws Exception {
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
buffer.append((byte[])null, 0, 0);
|
||||||
|
|
||||||
|
final byte[] tmp = new byte[] { 1, 2, 3, 4};
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, -1, 0);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 0, -1);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 0, 8);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 10, Integer.MAX_VALUE);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 2, 4);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEnsureCapacity() throws Exception {
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
buffer.ensureFreeCapacity(10);
|
||||||
|
Assert.assertEquals(10, buffer.capacity());
|
||||||
|
buffer.ensureFreeCapacity(5);
|
||||||
|
Assert.assertEquals(10, buffer.capacity());
|
||||||
|
buffer.append(new byte[]{1, 2, 3, 4, 5, 6, 7, 8});
|
||||||
|
buffer.ensureFreeCapacity(5);
|
||||||
|
Assert.assertEquals(13, buffer.capacity());
|
||||||
|
buffer.ensureFreeCapacity(15);
|
||||||
|
Assert.assertEquals(23, buffer.capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAppendText() throws Exception {
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
buffer.append(new char[]{'1', '2', '3', '4', '5'});
|
||||||
|
buffer.append(new char[]{'3', '4', '5', '6', '7', '8', '9', 'a', 'b'}, 3, 5);
|
||||||
|
buffer.append("bcd");
|
||||||
|
buffer.append("e");
|
||||||
|
buffer.append("f");
|
||||||
|
buffer.append((String) null);
|
||||||
|
buffer.append((char[]) null);
|
||||||
|
|
||||||
|
final byte[] bytes = buffer.toByteArray();
|
||||||
|
Assert.assertNotNull(bytes);
|
||||||
|
Assert.assertEquals("123456789abcdef", new String(bytes, Consts.ASCII));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidAppendChars() throws Exception {
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
buffer.append((char[])null, 0, 0);
|
||||||
|
|
||||||
|
final char[] tmp = new char[] { 1, 2, 3, 4};
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, -1, 0);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 0, -1);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 0, 8);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 10, Integer.MAX_VALUE);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
buffer.append(tmp, 2, 4);
|
||||||
|
Assert.fail("IndexOutOfBoundsException should have been thrown");
|
||||||
|
} catch (final IndexOutOfBoundsException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReset() throws Exception {
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
buffer.append("abcd");
|
||||||
|
buffer.append("e");
|
||||||
|
buffer.append("f");
|
||||||
|
|
||||||
|
final byte[] bytes1 = buffer.toByteArray();
|
||||||
|
Assert.assertNotNull(bytes1);
|
||||||
|
Assert.assertEquals("abcdef", new String(bytes1, Consts.ASCII));
|
||||||
|
|
||||||
|
buffer.reset();
|
||||||
|
|
||||||
|
final byte[] bytes2 = buffer.toByteArray();
|
||||||
|
Assert.assertNotNull(bytes2);
|
||||||
|
Assert.assertEquals("", new String(bytes2, Consts.ASCII));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNonAsciiCharset() throws Exception {
|
||||||
|
final int[] germanChars = { 0xE4, 0x2D, 0xF6, 0x2D, 0xFc };
|
||||||
|
final StringBuilder tmp = new StringBuilder();
|
||||||
|
for (final int germanChar : germanChars) {
|
||||||
|
tmp.append((char) germanChar);
|
||||||
|
}
|
||||||
|
final String umlauts = tmp.toString();
|
||||||
|
|
||||||
|
|
||||||
|
final ByteArrayBuilder buffer = new ByteArrayBuilder();
|
||||||
|
buffer.append(umlauts);
|
||||||
|
|
||||||
|
final byte[] bytes1 = buffer.toByteArray();
|
||||||
|
Assert.assertNotNull(bytes1);
|
||||||
|
Assert.assertEquals("?-?-?", new String(bytes1, Consts.ASCII));
|
||||||
|
|
||||||
|
buffer.reset();
|
||||||
|
buffer.charset(Consts.UTF_8);
|
||||||
|
buffer.append(umlauts);
|
||||||
|
|
||||||
|
final byte[] bytes2 = buffer.toByteArray();
|
||||||
|
Assert.assertNotNull(bytes2);
|
||||||
|
Assert.assertEquals(umlauts, new String(bytes2, Consts.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -567,7 +567,7 @@ public void testHttpEntityDigest() throws Exception {
|
|||||||
digester.write(new byte[] { 'a', 'b', 'c'});
|
digester.write(new byte[] { 'a', 'b', 'c'});
|
||||||
Assert.assertNull(digester.getDigest());
|
Assert.assertNull(digester.getDigest());
|
||||||
digester.close();
|
digester.close();
|
||||||
Assert.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.encode(digester.getDigest()));
|
Assert.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.formatHex(digester.getDigest()));
|
||||||
try {
|
try {
|
||||||
digester.write('a');
|
digester.write('a');
|
||||||
Assert.fail("IOException should have been thrown");
|
Assert.fail("IOException should have been thrown");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user