| /* |
| * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.net.httpserver; |
| |
| import java.io.*; |
| import java.net.*; |
| import com.sun.net.httpserver.*; |
| import com.sun.net.httpserver.spi.*; |
| |
| class ChunkedInputStream extends LeftOverInputStream { |
| ChunkedInputStream (ExchangeImpl t, InputStream src) { |
| super (t, src); |
| } |
| |
| private int remaining; |
| |
| /* true when a chunk header needs to be read */ |
| |
| private boolean needToReadHeader = true; |
| |
| static char CR = '\r'; |
| static char LF = '\n'; |
| |
| private int numeric (char[] arr, int nchars) throws IOException { |
| assert arr.length >= nchars; |
| int len = 0; |
| for (int i=0; i<nchars; i++) { |
| char c = arr[i]; |
| int val=0; |
| if (c>='0' && c <='9') { |
| val = c - '0'; |
| } else if (c>='a' && c<= 'f') { |
| val = c - 'a' + 10; |
| } else if (c>='A' && c<= 'F') { |
| val = c - 'A' + 10; |
| } else { |
| throw new IOException ("invalid chunk length"); |
| } |
| len = len * 16 + val; |
| } |
| return len; |
| } |
| |
| /* read the chunk header line and return the chunk length |
| * any chunk extensions are ignored |
| */ |
| private int readChunkHeader () throws IOException { |
| boolean gotCR = false; |
| char c; |
| char[] len_arr = new char [16]; |
| int len_size = 0; |
| boolean end_of_len = false; |
| |
| while ((c=(char)in.read())!= -1) { |
| if (len_size == len_arr.length -1) { |
| throw new IOException ("invalid chunk header"); |
| } |
| if (gotCR) { |
| if (c == LF) { |
| int l = numeric (len_arr, len_size); |
| return l; |
| } else { |
| gotCR = false; |
| } |
| if (!end_of_len) { |
| len_arr[len_size++] = c; |
| } |
| } else { |
| if (c == CR) { |
| gotCR = true; |
| } else if (c == ';') { |
| end_of_len = true; |
| } else if (!end_of_len) { |
| len_arr[len_size++] = c; |
| } |
| } |
| } |
| throw new IOException ("end of stream reading chunk header"); |
| } |
| |
| protected int readImpl (byte[]b, int off, int len) throws IOException { |
| if (eof) { |
| return -1; |
| } |
| if (needToReadHeader) { |
| remaining = readChunkHeader(); |
| if (remaining == 0) { |
| eof = true; |
| consumeCRLF(); |
| return -1; |
| } |
| needToReadHeader = false; |
| } |
| if (len > remaining) { |
| len = remaining; |
| } |
| int n = in.read(b, off, len); |
| if (n > -1) { |
| remaining -= n; |
| } |
| if (remaining == 0) { |
| needToReadHeader = true; |
| consumeCRLF(); |
| } |
| return n; |
| } |
| |
| private void consumeCRLF () throws IOException { |
| char c; |
| c = (char)in.read(); /* CR */ |
| if (c != CR) { |
| throw new IOException ("invalid chunk end"); |
| } |
| c = (char)in.read(); /* LF */ |
| if (c != LF) { |
| throw new IOException ("invalid chunk end"); |
| } |
| } |
| |
| /** |
| * returns the number of bytes available to read in the current chunk |
| * which may be less than the real amount, but we'll live with that |
| * limitation for the moment. It only affects potential efficiency |
| * rather than correctness. |
| */ |
| public int available () throws IOException { |
| if (eof || closed) { |
| return 0; |
| } |
| int n = in.available(); |
| return n > remaining? remaining: n; |
| } |
| |
| /* called after the stream is closed to see if bytes |
| * have been read from the underlying channel |
| * and buffered internally |
| */ |
| public boolean isDataBuffered () throws IOException { |
| assert eof; |
| return in.available() > 0; |
| } |
| |
| public boolean markSupported () {return false;} |
| |
| public void mark (int l) { |
| } |
| |
| public void reset () throws IOException { |
| throw new IOException ("mark/reset not supported"); |
| } |
| } |