1 /* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.incubator.http.internal.websocket; 27 28 import jdk.incubator.http.internal.common.Log; 29 30 import java.nio.ByteBuffer; 31 import java.nio.CharBuffer; 32 import java.nio.charset.CharacterCodingException; 33 import java.nio.charset.CharsetDecoder; 34 import java.nio.charset.CoderResult; 35 import java.nio.charset.CodingErrorAction; 36 37 import static java.nio.charset.StandardCharsets.UTF_8; 38 import static jdk.incubator.http.internal.common.Utils.EMPTY_BYTEBUFFER; 39 40 final class UTF8AccumulatingDecoder { 41 42 private final CharsetDecoder decoder = UTF_8.newDecoder(); 43 44 { 45 decoder.onMalformedInput(CodingErrorAction.REPORT); 46 decoder.onUnmappableCharacter(CodingErrorAction.REPORT); 47 } 48 49 private ByteBuffer leftovers = EMPTY_BYTEBUFFER; 50 51 CharBuffer decode(ByteBuffer in, boolean endOfInput) 52 throws CharacterCodingException 53 { 54 ByteBuffer b; 55 int rem = leftovers.remaining(); 56 if (rem != 0) { 57 // We won't need this wasteful allocation & copying when JDK-8155222 58 // has been resolved 59 b = ByteBuffer.allocate(rem + in.remaining()); 60 b.put(leftovers).put(in).flip(); 61 } else { 62 b = in; 63 } 64 CharBuffer out = CharBuffer.allocate(b.remaining()); 65 CoderResult r = decoder.decode(b, out, endOfInput); 66 if (r.isError()) { 67 r.throwException(); 68 } 69 if (b.hasRemaining()) { 70 leftovers = ByteBuffer.allocate(b.remaining()).put(b).flip(); 71 } else { 72 leftovers = EMPTY_BYTEBUFFER; 73 } 74 // Since it's UTF-8, the assumption is leftovers.remaining() < 4 75 // (i.e. small). Otherwise a shared buffer should be used 76 if (!(leftovers.remaining() < 4)) { 77 Log.logError("The size of decoding leftovers is greater than expected: {0}", 78 leftovers.remaining()); 79 } 80 b.position(b.limit()); // As if we always read to the end 81 // Decoder promises that in the case of endOfInput == true: 82 // "...any remaining undecoded input will be treated as being 83 // malformed" 84 assert !(endOfInput && leftovers.hasRemaining()) : endOfInput + ", " + leftovers; 85 if (endOfInput) { 86 r = decoder.flush(out); 87 decoder.reset(); 88 if (r.isOverflow()) { 89 // FIXME: for now I know flush() does nothing. But the 90 // implementation of UTF8 decoder might change. And if now 91 // flush() is a no-op, it is not guaranteed to remain so in 92 // the future 93 throw new InternalError("Not yet implemented"); 94 } 95 } 96 return out.flip(); 97 } 98 }