< prev index next >
1 /*
2 * Copyright (c) 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 */
24 package java.net.http;
25
26 import java.nio.ByteBuffer;
27 import java.nio.CharBuffer;
28 import java.nio.charset.CharacterCodingException;
29 import java.nio.charset.CharsetDecoder;
30 import java.nio.charset.CharsetEncoder;
31 import java.nio.charset.CoderResult;
32
33 import static java.nio.charset.StandardCharsets.UTF_8;
34
35 // The purpose of this class is to separate charset-related tasks from the main
36 // WebSocket logic, simplifying where possible.
37 //
38 // * Coders hide the differences between coding and flushing stages on the
39 // API level
40 // * Verifier abstracts the way the verification is performed
41 // (spoiler: it's a decoding into a throw-away buffer)
42 //
43 // Coding methods throw exceptions instead of returning coding result denoting
44 // errors, since any kind of handling and recovery is not expected.
45 final class CharsetToolkit {
46
47 private CharsetToolkit() { }
48
49 static final class Verifier {
50
51 private final CharsetDecoder decoder = UTF_8.newDecoder();
52 // A buffer used to check validity of UTF-8 byte stream by decoding it.
53 // The contents of this buffer are never used.
54 // The size is arbitrary, though it should probably be chosen from the
55 // performance perspective since it affects the total number of calls to
56 // decoder.decode() and amount of work in each of these calls
57 private final CharBuffer blackHole = CharBuffer.allocate(1024);
58
59 void verify(ByteBuffer in, boolean endOfInput)
60 throws CharacterCodingException {
61 while (true) {
62 // Since decoder.flush() cannot produce an error, it's not
63 // helpful for verification. Therefore this step is skipped.
64 CoderResult r = decoder.decode(in, blackHole, endOfInput);
65 if (r.isOverflow()) {
66 blackHole.clear();
67 } else if (r.isUnderflow()) {
68 break;
69 } else if (r.isError()) {
70 r.throwException();
71 } else {
72 // Should not happen
73 throw new InternalError();
74 }
75 }
76 }
77
78 Verifier reset() {
79 decoder.reset();
80 return this;
81 }
82 }
83
84 static final class Encoder {
85
86 private final CharsetEncoder encoder = UTF_8.newEncoder();
87 private boolean coding = true;
88
89 CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput)
90 throws CharacterCodingException {
91
92 if (coding) {
93 CoderResult r = encoder.encode(in, out, endOfInput);
94 if (r.isOverflow()) {
95 return r;
96 } else if (r.isUnderflow()) {
97 if (endOfInput) {
98 coding = false;
99 } else {
100 return r;
101 }
102 } else if (r.isError()) {
103 r.throwException();
104 } else {
105 // Should not happen
106 throw new InternalError();
107 }
108 }
109 assert !coding;
110 return encoder.flush(out);
111 }
112
113 Encoder reset() {
114 coding = true;
115 encoder.reset();
116 return this;
117 }
118 }
119
120 static CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
121 return UTF_8.newDecoder().decode(in);
122 }
123
124 static final class Decoder {
125
126 private final CharsetDecoder decoder = UTF_8.newDecoder();
127 private boolean coding = true; // Either coding or flushing
128
129 CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput)
130 throws CharacterCodingException {
131
132 if (coding) {
133 CoderResult r = decoder.decode(in, out, endOfInput);
134 if (r.isOverflow()) {
135 return r;
136 } else if (r.isUnderflow()) {
137 if (endOfInput) {
138 coding = false;
139 } else {
140 return r;
141 }
142 } else if (r.isError()) {
143 r.throwException();
144 } else {
145 // Should not happen
146 throw new InternalError();
147 }
148 }
149 assert !coding;
150 return decoder.flush(out);
151 }
152
153 Decoder reset() {
154 coding = true;
155 decoder.reset();
156 return this;
157 }
158 }
159 }
< prev index next >