< 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 >