From d09ae9707d6fbaad0ebccb0ae48fd87ce32dd98d Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Mon, 10 Aug 2015 19:44:09 +0000 Subject: [PATCH] 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 --- .../http/auth/util/ByteArrayBuilder.java | 212 +++++++++++++++++ .../apache/http/impl/auth/BasicScheme.java | 22 +- .../apache/http/impl/auth/DigestScheme.java | 92 ++++---- .../http/auth/util/TestByteArrayBuilder.java | 214 ++++++++++++++++++ .../http/impl/auth/TestDigestScheme.java | 2 +- 5 files changed, 495 insertions(+), 47 deletions(-) create mode 100644 httpclient/src/main/java/org/apache/http/auth/util/ByteArrayBuilder.java create mode 100644 httpclient/src/test/java/org/apache/http/auth/util/TestByteArrayBuilder.java diff --git a/httpclient/src/main/java/org/apache/http/auth/util/ByteArrayBuilder.java b/httpclient/src/main/java/org/apache/http/auth/util/ByteArrayBuilder.java new file mode 100644 index 000000000..ae446df7a --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/auth/util/ByteArrayBuilder.java @@ -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 + * . + * + */ +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"; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java index f75e0ac92..e5ee0ee33 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java @@ -51,10 +51,10 @@ import org.apache.http.auth.Credentials; import org.apache.http.auth.CredentialsProvider; import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.auth.util.ByteArrayBuilder; import org.apache.http.protocol.HttpContext; import org.apache.http.util.Args; import org.apache.http.util.CharsetUtils; -import org.apache.http.util.EncodingUtils; /** * Basic authentication scheme as defined in RFC 2617. @@ -68,7 +68,10 @@ public class BasicScheme implements AuthScheme, Serializable { private final Map paramMap; private transient Charset charset; + private transient ByteArrayBuilder buffer; + private transient Base64 base64codec; private boolean complete; + private String username; private String password; @@ -160,12 +163,17 @@ public String generateAuthResponse( final HttpHost host, final HttpRequest request, final HttpContext context) throws AuthenticationException { - final StringBuilder buffer = new StringBuilder(); - buffer.append(this.username); - buffer.append(":"); - buffer.append(this.password); - final Base64 base64codec = new Base64(0); - final byte[] encodedCreds = base64codec.encode(EncodingUtils.getBytes(buffer.toString(), charset.name())); + if (this.buffer == null) { + this.buffer = new ByteArrayBuilder(64).charset(this.charset); + } else { + this.buffer.reset(); + } + 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); } diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java index 441ae3873..ca4236eb1 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.Serializable; +import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.Principal; import java.security.SecureRandom; @@ -41,6 +42,7 @@ import java.util.Set; import java.util.StringTokenizer; +import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpHost; @@ -54,12 +56,13 @@ import org.apache.http.auth.Credentials; import org.apache.http.auth.CredentialsProvider; import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.auth.util.ByteArrayBuilder; import org.apache.http.message.BasicHeaderValueFormatter; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HttpContext; import org.apache.http.util.Args; 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. @@ -84,7 +87,7 @@ public class DigestScheme implements AuthScheme, Serializable { * Hexa values used when creating 32 character long digest in HTTP DigestScheme * in case of authentication. * - * @see #encode(byte[]) + * @see #formatHex(byte[]) */ private static final char[] HEXADECIMAL = { '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 paramMap; private boolean complete; + private transient ByteArrayBuilder buffer; + private String lastNonce; private long nounceCount; private String cnonce; - private String a1; - private String a2; + private byte[] a1; + private byte[] a2; private String username; 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); } - String charset = this.paramMap.get("charset"); + final String charsetName = this.paramMap.get("charset"); + Charset charset = charsetName != null ? CharsetUtils.lookup(charsetName) : null; if (charset == null) { - charset = "ISO-8859-1"; + charset = Consts.ISO_8859_1; } String digAlg = algorithm; @@ -275,16 +281,24 @@ private String createDigestResponse(final HttpRequest request) throws Authentica cnonce = null; lastNonce = nonce; } - final StringBuilder sb = new StringBuilder(256); + + final StringBuilder sb = new StringBuilder(8); final Formatter formatter = new Formatter(sb, Locale.US); - formatter.format("%08x", Long.valueOf(nounceCount)); + formatter.format("%08x", nounceCount); formatter.close(); final String nc = sb.toString(); if (cnonce == null) { - cnonce = createCnonce(); + cnonce = formatHex(createCnonce()); } + if (buffer == null) { + buffer = new ByteArrayBuilder(); + } else { + buffer.reset(); + } + buffer.charset(charset); + a1 = null; a2 = null; // 3.2.2.2: Calculating digest @@ -294,24 +308,23 @@ private String createDigestResponse(final HttpRequest request) throws Authentica // ":" unq(cnonce-value) // calculated one per session - sb.setLength(0); - sb.append(username).append(':').append(realm).append(':').append(password); - final String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset))); - sb.setLength(0); - sb.append(checksum).append(':').append(nonce).append(':').append(cnonce); - a1 = sb.toString(); + buffer.append(username).append(":").append(realm).append(":").append(password); + final String checksum = formatHex(digester.digest(this.buffer.toByteArray())); + buffer.reset(); + buffer.append(checksum).append(":").append(nonce).append(":").append(cnonce); + a1 = buffer.toByteArray(); } else { // unq(username-value) ":" unq(realm-value) ":" passwd - sb.setLength(0); - sb.append(username).append(':').append(realm).append(':').append(password); - a1 = sb.toString(); + buffer.append(username).append(":").append(realm).append(":").append(password); + a1 = buffer.toByteArray(); } - final String hasha1 = encode(digester.digest(EncodingUtils.getBytes(a1, charset))); + final String hasha1 = formatHex(digester.digest(a1)); + buffer.reset(); if (qop == QOP_AUTH) { // Method ":" digest-uri-value - a2 = method + ':' + uri; + a2 = buffer.append(method).append(":").append(uri).toByteArray(); } else if (qop == QOP_AUTH_INT) { // Method ":" digest-uri-value ":" H(entity-body) 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 (qopset.contains("auth")) { qop = QOP_AUTH; - a2 = method + ':' + uri; + a2 = buffer.append(method).append(":").append(uri).toByteArray(); } else { throw new AuthenticationException("Qop auth-int cannot be used with " + "a non-repeatable entity"); @@ -337,30 +350,31 @@ private String createDigestResponse(final HttpRequest request) throws Authentica } catch (final IOException 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 { - 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 - final String digestValue; + final byte[] digestInput; if (qop == QOP_MISSING) { - sb.setLength(0); - sb.append(hasha1).append(':').append(nonce).append(':').append(hasha2); - digestValue = sb.toString(); + buffer.append(hasha1).append(":").append(nonce).append(":").append(hasha2); + digestInput = buffer.toByteArray(); } else { - sb.setLength(0); - sb.append(hasha1).append(':').append(nonce).append(':').append(nc).append(':') - .append(cnonce).append(':').append(qop == QOP_AUTH_INT ? "auth-int" : "auth") - .append(':').append(hasha2); - digestValue = sb.toString(); + buffer.append(hasha1).append(":").append(nonce).append(":").append(nc).append(":") + .append(cnonce).append(":").append(qop == QOP_AUTH_INT ? "auth-int" : "auth") + .append(":").append(hasha2); + digestInput = buffer.toByteArray(); } + buffer.reset(); - final String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue))); + final String digest = formatHex(digester.digest(digestInput)); final CharArrayBuffer buffer = new CharArrayBuffer(128); buffer.append("Digest "); @@ -401,11 +415,11 @@ String getCnonce() { } String getA1() { - return a1; + return a1 != null ? new String(a1, Consts.ASCII) : null; } 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 * @return encoded MD5, or null if encoding failed */ - static String encode(final byte[] binaryData) { + static String formatHex(final byte[] binaryData) { final int n = binaryData.length; final char[] buffer = new char[n * 2]; for (int i = 0; i < n; i++) { @@ -433,11 +447,11 @@ static String encode(final byte[] binaryData) { * * @return The cnonce value as String. */ - public static String createCnonce() { + static byte[] createCnonce() { final SecureRandom rnd = new SecureRandom(); final byte[] tmp = new byte[8]; rnd.nextBytes(tmp); - return encode(tmp); + return tmp; } @Override diff --git a/httpclient/src/test/java/org/apache/http/auth/util/TestByteArrayBuilder.java b/httpclient/src/test/java/org/apache/http/auth/util/TestByteArrayBuilder.java new file mode 100644 index 000000000..3ab527ab6 --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/auth/util/TestByteArrayBuilder.java @@ -0,0 +1,214 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package 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)); + } + +} diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java index 55b9bf184..3e26a7c98 100644 --- a/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java @@ -567,7 +567,7 @@ public void testHttpEntityDigest() throws Exception { digester.write(new byte[] { 'a', 'b', 'c'}); Assert.assertNull(digester.getDigest()); digester.close(); - Assert.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.encode(digester.getDigest())); + Assert.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.formatHex(digester.getDigest())); try { digester.write('a'); Assert.fail("IOException should have been thrown");