History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/net/www/http/ChunkedInputStream.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/net/www/http/ChunkedInputStream.java+771−0
@@ -0,0 +1,771 @@
1+/*
2+ * Copyright (c) 1999, 2006, 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+package sun.net.www.http;
26+
27+import java.io.*;
28+import java.util.*;
29+
30+import sun.net.*;
31+import sun.net.www.*;
32+
33+/**
34+ * A <code>ChunkedInputStream</code> provides a stream for reading a body of
35+ * a http message that can be sent as a series of chunks, each with its own
36+ * size indicator. Optionally the last chunk can be followed by trailers
37+ * containing entity-header fields.
38+ * <p>
39+ * A <code>ChunkedInputStream</code> is also <code>Hurryable</code> so it
40+ * can be hurried to the end of the stream if the bytes are available on
41+ * the underlying stream.
42+ */
43+public
44+class ChunkedInputStream extends InputStream implements Hurryable {
45+
46+ /**
47+ * The underlying stream
48+ */
49+ private InputStream in;
50+
51+ /**
52+ * The <code>HttpClient</code> that should be notified when the chunked stream has
53+ * completed.
54+ */
55+ private HttpClient hc;
56+
57+ /**
58+ * The <code>MessageHeader</code> that is populated with any optional trailer
59+ * that appear after the last chunk.
60+ */
61+ private MessageHeader responses;
62+
63+ /**
64+ * The size, in bytes, of the chunk that is currently being read.
65+ * This size is only valid if the current position in the underlying
66+ * input stream is inside a chunk (ie: state == STATE_READING_CHUNK).
67+ */
68+ private int chunkSize;
69+
70+ /**
71+ * The number of bytes read from the underlying stream for the current
72+ * chunk. This value is always in the range <code>0</code> through to
73+ * <code>chunkSize</code>
74+ */
75+ private int chunkRead;
76+
77+ /**
78+ * The internal buffer array where chunk data is available for the
79+ * application to read.
80+ */
81+ private byte chunkData[] = new byte[4096];
82+
83+ /**
84+ * The current position in the buffer. It contains the index
85+ * of the next byte to read from <code>chunkData</code>
86+ */
87+ private int chunkPos;
88+
89+ /**
90+ * The index one greater than the index of the last valid byte in the
91+ * buffer. This value is always in the range <code>0</code> through
92+ * <code>chunkData.length</code>.
93+ */
94+ private int chunkCount;
95+
96+ /**
97+ * The internal buffer where bytes from the underlying stream can be
98+ * read. It may contain bytes representing chunk-size, chunk-data, or
99+ * trailer fields.
100+ */
101+ private byte rawData[] = new byte[32];
102+
103+ /**
104+ * The current position in the buffer. It contains the index
105+ * of the next byte to read from <code>rawData</code>
106+ */
107+ private int rawPos;
108+
109+ /**
110+ * The index one greater than the index of the last valid byte in the
111+ * buffer. This value is always in the range <code>0</code> through
112+ * <code>rawData.length</code>.
113+ */
114+ private int rawCount;
115+
116+ /**
117+ * Indicates if an error was encountered when processing the chunked
118+ * stream.
119+ */
120+ private boolean error;
121+
122+ /**
123+ * Indicates if the chunked stream has been closed using the
124+ * <code>close</code> method.
125+ */
126+ private boolean closed;
127+
128+ /**
129+ * State to indicate that next field should be :-
130+ * chunk-size [ chunk-extension ] CRLF
131+ */
132+ static final int STATE_AWAITING_CHUNK_HEADER = 1;
133+
134+ /**
135+ * State to indicate that we are currently reading the chunk-data.
136+ */
137+ static final int STATE_READING_CHUNK = 2;
138+
139+ /**
140+ * Indicates that a chunk has been completely read and the next
141+ * fields to be examine should be CRLF
142+ */
143+ static final int STATE_AWAITING_CHUNK_EOL = 3;
144+
145+ /**
146+ * Indicates that all chunks have been read and the next field
147+ * should be optional trailers or an indication that the chunked
148+ * stream is complete.
149+ */
150+ static final int STATE_AWAITING_TRAILERS = 4;
151+
152+ /**
153+ * State to indicate that the chunked stream is complete and
154+ * no further bytes should be read from the underlying stream.
155+ */
156+ static final int STATE_DONE = 5;
157+
158+ /**
159+ * Indicates the current state.
160+ */
161+ private int state;
162+
163+
164+ /**
165+ * Check to make sure that this stream has not been closed.
166+ */
167+ private void ensureOpen() throws IOException {
168+ if (closed) {
169+ throw new IOException("stream is closed");
170+ }
171+ }
172+
173+
174+ /**
175+ * Ensures there is <code>size</code> bytes available in
176+ * <code>rawData</code>. This requires that we either
177+ * shift the bytes in use to the begining of the buffer
178+ * or allocate a large buffer with sufficient space available.
179+ */
180+ private void ensureRawAvailable(int size) {
181+ if (rawCount + size > rawData.length) {
182+ int used = rawCount - rawPos;
183+ if (used + size > rawData.length) {
184+ byte tmp[] = new byte[used + size];
185+ if (used > 0) {
186+ System.arraycopy(rawData, rawPos, tmp, 0, used);
187+ }
188+ rawData = tmp;
189+ } else {
190+ if (used > 0) {
191+ System.arraycopy(rawData, rawPos, rawData, 0, used);
192+ }
193+ }
194+ rawCount = used;
195+ rawPos = 0;
196+ }
197+ }
198+
199+
200+ /**
201+ * Close the underlying input stream by either returning it to the
202+ * keep alive cache or closing the stream.
203+ * <p>
204+ * As a chunked stream is inheritly persistent (see HTTP 1.1 RFC) the
205+ * underlying stream can be returned to the keep alive cache if the
206+ * stream can be completely read without error.
207+ */
208+ private void closeUnderlying() throws IOException {
209+ if (in == null) {
210+ return;
211+ }
212+
213+ if (!error && state == STATE_DONE) {
214+ hc.finished();
215+ } else {
216+ if (!hurry()) {
217+ hc.closeServer();
218+ }
219+ }
220+
221+ in = null;
222+ }
223+
224+ /**
225+ * Attempt to read the remainder of a chunk directly into the
226+ * caller's buffer.
227+ * <p>
228+ * Return the number of bytes read.
229+ */
230+ private int fastRead(byte[] b, int off, int len) throws IOException {
231+
232+ // assert state == STATE_READING_CHUNKS;
233+
234+ int remaining = chunkSize - chunkRead;
235+ int cnt = (remaining < len) ? remaining : len;
236+ if (cnt > 0) {
237+ int nread;
238+ try {
239+ nread = in.read(b, off, cnt);
240+ } catch (IOException e) {
241+ error = true;
242+ throw e;
243+ }
244+ if (nread > 0) {
245+ chunkRead += nread;
246+ if (chunkRead >= chunkSize) {
247+ state = STATE_AWAITING_CHUNK_EOL;
248+ }
249+ return nread;
250+ }
251+ error = true;
252+ throw new IOException("Premature EOF");
253+ } else {
254+ return 0;
255+ }
256+ }
257+
258+ /**
259+ * Process any outstanding bytes that have already been read into
260+ * <code>rawData</code>.
261+ * <p>
262+ * The parsing of the chunked stream is performed as a state machine with
263+ * <code>state</code> representing the current state of the processing.
264+ * <p>
265+ * Returns when either all the outstanding bytes in rawData have been
266+ * processed or there is insufficient bytes available to continue
267+ * processing. When the latter occurs <code>rawPos</code> will not have
268+ * been updated and thus the processing can be restarted once further
269+ * bytes have been read into <code>rawData</code>.
270+ */
271+ private void processRaw() throws IOException {
272+ int pos;
273+ int i;
274+
275+ while (state != STATE_DONE) {
276+
277+ switch (state) {
278+
279+ /**
280+ * We are awaiting a line with a chunk header
281+ */
282+ case STATE_AWAITING_CHUNK_HEADER:
283+ /*
284+ * Find \n to indicate end of chunk header. If not found when there is
285+ * insufficient bytes in the raw buffer to parse a chunk header.
286+ */
287+ pos = rawPos;
288+ while (pos < rawCount) {
289+ if (rawData[pos] == '\n') {
290+ break;
291+ }
292+ pos++;
293+ }
294+ if (pos >= rawCount) {
295+ return;
296+ }
297+
298+ /*
299+ * Extract the chunk size from the header (ignoring extensions).
300+ */
301+ String header = new String(rawData, rawPos, pos-rawPos+1, "US-ASCII");
302+ for (i=0; i < header.length(); i++) {
303+ if (Character.digit(header.charAt(i), 16) == -1)
304+ break;
305+ }
306+ try {
307+ chunkSize = Integer.parseInt(header.substring(0, i), 16);
308+ } catch (NumberFormatException e) {
309+ error = true;
310+ throw new IOException("Bogus chunk size");
311+ }
312+
313+ /*
314+ * Chunk has been parsed so move rawPos to first byte of chunk
315+ * data.
316+ */
317+ rawPos = pos + 1;
318+ chunkRead = 0;
319+
320+ /*
321+ * A chunk size of 0 means EOF.
322+ */
323+ if (chunkSize > 0) {
324+ state = STATE_READING_CHUNK;
325+ } else {
326+ state = STATE_AWAITING_TRAILERS;
327+ }
328+ break;
329+
330+
331+ /**
332+ * We are awaiting raw entity data (some may have already been
333+ * read). chunkSize is the size of the chunk; chunkRead is the
334+ * total read from the underlying stream to date.
335+ */
336+ case STATE_READING_CHUNK :
337+ /* no data available yet */
338+ if (rawPos >= rawCount) {
339+ return;
340+ }
341+
342+ /*
343+ * Compute the number of bytes of chunk data available in the
344+ * raw buffer.
345+ */
346+ int copyLen = Math.min( chunkSize-chunkRead, rawCount-rawPos );
347+
348+ /*
349+ * Expand or compact chunkData if needed.
350+ */
351+ if (chunkData.length < chunkCount + copyLen) {
352+ int cnt = chunkCount - chunkPos;
353+ if (chunkData.length < cnt + copyLen) {
354+ byte tmp[] = new byte[cnt + copyLen];
355+ System.arraycopy(chunkData, chunkPos, tmp, 0, cnt);
356+ chunkData = tmp;
357+ } else {
358+ System.arraycopy(chunkData, chunkPos, chunkData, 0, cnt);
359+ }
360+ chunkPos = 0;
361+ chunkCount = cnt;
362+ }
363+
364+ /*
365+ * Copy the chunk data into chunkData so that it's available
366+ * to the read methods.
367+ */
368+ System.arraycopy(rawData, rawPos, chunkData, chunkCount, copyLen);
369+ rawPos += copyLen;
370+ chunkCount += copyLen;
371+ chunkRead += copyLen;
372+
373+ /*
374+ * If all the chunk has been copied into chunkData then the next
375+ * token should be CRLF.
376+ */
377+ if (chunkSize - chunkRead <= 0) {
378+ state = STATE_AWAITING_CHUNK_EOL;
379+ } else {
380+ return;
381+ }
382+ break;
383+
384+
385+ /**
386+ * Awaiting CRLF after the chunk
387+ */
388+ case STATE_AWAITING_CHUNK_EOL:
389+ /* not available yet */
390+ if (rawPos + 1 >= rawCount) {
391+ return;
392+ }
393+
394+ if (rawData[rawPos] != '\r') {
395+ error = true;
396+ throw new IOException("missing CR");
397+ }
398+ if (rawData[rawPos+1] != '\n') {
399+ error = true;
400+ throw new IOException("missing LF");
401+ }
402+ rawPos += 2;
403+
404+ /*
405+ * Move onto the next chunk
406+ */
407+ state = STATE_AWAITING_CHUNK_HEADER;
408+ break;
409+
410+
411+ /**
412+ * Last chunk has been read so not we're waiting for optional
413+ * trailers.
414+ */
415+ case STATE_AWAITING_TRAILERS:
416+
417+ /*
418+ * Do we have an entire line in the raw buffer?
419+ */
420+ pos = rawPos;
421+ while (pos < rawCount) {
422+ if (rawData[pos] == '\n') {
423+ break;
424+ }
425+ pos++;
426+ }
427+ if (pos >= rawCount) {
428+ return;
429+ }
430+
431+ if (pos == rawPos) {
432+ error = true;
433+ throw new IOException("LF should be proceeded by CR");
434+ }
435+ if (rawData[pos-1] != '\r') {
436+ error = true;
437+ throw new IOException("LF should be proceeded by CR");
438+ }
439+
440+ /*
441+ * Stream done so close underlying stream.
442+ */
443+ if (pos == (rawPos + 1)) {
444+
445+ state = STATE_DONE;
446+ closeUnderlying();
447+
448+ return;
449+ }
450+
451+ /*
452+ * Extract any tailers and append them to the message
453+ * headers.
454+ */
455+ String trailer = new String(rawData, rawPos, pos-rawPos, "US-ASCII");
456+ i = trailer.indexOf(':');
457+ if (i == -1) {
458+ throw new IOException("Malformed tailer - format should be key:value");
459+ }
460+ String key = (trailer.substring(0, i)).trim();
461+ String value = (trailer.substring(i+1, trailer.length())).trim();
462+
463+ responses.add(key, value);
464+
465+ /*
466+ * Move onto the next trailer.
467+ */
468+ rawPos = pos+1;
469+ break;
470+
471+ } /* switch */
472+ }
473+ }
474+
475+
476+ /**
477+ * Reads any available bytes from the underlying stream into
478+ * <code>rawData</code> and returns the number of bytes of
479+ * chunk data available in <code>chunkData</code> that the
480+ * application can read.
481+ */
482+ private int readAheadNonBlocking() throws IOException {
483+
484+ /*
485+ * If there's anything available on the underlying stream then we read
486+ * it into the raw buffer and process it. Processing ensures that any
487+ * available chunk data is made available in chunkData.
488+ */
489+ int avail = in.available();
490+ if (avail > 0) {
491+
492+ /* ensure that there is space in rawData to read the available */
493+ ensureRawAvailable(avail);
494+
495+ int nread;
496+ try {
497+ nread = in.read(rawData, rawCount, avail);
498+ } catch (IOException e) {
499+ error = true;
500+ throw e;
501+ }
502+ if (nread < 0) {
503+ error = true; /* premature EOF ? */
504+ return -1;
505+ }
506+ rawCount += nread;
507+
508+ /*
509+ * Process the raw bytes that have been read.
510+ */
511+ processRaw();
512+ }
513+
514+ /*
515+ * Return the number of chunked bytes available to read
516+ */
517+ return chunkCount - chunkPos;
518+ }
519+
520+ /**
521+ * Reads from the underlying stream until there is chunk data
522+ * available in <code>chunkData</code> for the application to
523+ * read.
524+ */
525+ private int readAheadBlocking() throws IOException {
526+
527+ do {
528+ /*
529+ * All of chunked response has been read to return EOF.
530+ */
531+ if (state == STATE_DONE) {
532+ return -1;
533+ }
534+
535+ /*
536+ * We must read into the raw buffer so make sure there is space
537+ * available. We use a size of 32 to avoid too much chunk data
538+ * being read into the raw buffer.
539+ */
540+ ensureRawAvailable(32);
541+ int nread;
542+ try {
543+ nread = in.read(rawData, rawCount, rawData.length-rawCount);
544+ } catch (IOException e) {
545+ error = true;
546+ throw e;
547+ }
548+
549+ /**
550+ * If we hit EOF it means there's a problem as we should never
551+ * attempt to read once the last chunk and trailers have been
552+ * received.
553+ */
554+ if (nread < 0) {
555+ error = true;
556+ throw new IOException("Premature EOF");
557+ }
558+
559+ /**
560+ * Process the bytes from the underlying stream
561+ */
562+ rawCount += nread;
563+ processRaw();
564+
565+ } while (chunkCount <= 0);
566+
567+ /*
568+ * Return the number of chunked bytes available to read
569+ */
570+ return chunkCount - chunkPos;
571+ }
572+
573+ /**
574+ * Read ahead in either blocking or non-blocking mode. This method
575+ * is typically used when we run out of available bytes in
576+ * <code>chunkData</code> or we need to determine how many bytes
577+ * are available on the input stream.
578+ */
579+ private int readAhead(boolean allowBlocking) throws IOException {
580+
581+ /*
582+ * Last chunk already received - return EOF
583+ */
584+ if (state == STATE_DONE) {
585+ return -1;
586+ }
587+
588+ /*
589+ * Reset position/count if data in chunkData is exhausted.
590+ */
591+ if (chunkPos >= chunkCount) {
592+ chunkCount = 0;
593+ chunkPos = 0;
594+ }
595+
596+ /*
597+ * Read ahead blocking or non-blocking
598+ */
599+ if (allowBlocking) {
600+ return readAheadBlocking();
601+ } else {
602+ return readAheadNonBlocking();
603+ }
604+ }
605+
606+ /**
607+ * Creates a <code>ChunkedInputStream</code> and saves its arguments, for
608+ * later use.
609+ *
610+ * @param in the underlying input stream.
611+ * @param hc the HttpClient
612+ * @param responses the MessageHeader that should be populated with optional
613+ * trailers.
614+ */
615+ public ChunkedInputStream(InputStream in, HttpClient hc, MessageHeader responses) throws IOException {
616+
617+ /* save arguments */
618+ this.in = in;
619+ this.responses = responses;
620+ this.hc = hc;
621+
622+ /*
623+ * Set our initial state to indicate that we are first starting to
624+ * look for a chunk header.
625+ */
626+ state = STATE_AWAITING_CHUNK_HEADER;
627+ }
628+
629+ /**
630+ * See
631+ * the general contract of the <code>read</code>
632+ * method of <code>InputStream</code>.
633+ *
634+ * @return the next byte of data, or <code>-1</code> if the end of the
635+ * stream is reached.
636+ * @exception IOException if an I/O error occurs.
637+ * @see java.io.FilterInputStream#in
638+ */
639+ public synchronized int read() throws IOException {
640+ ensureOpen();
641+ if (chunkPos >= chunkCount) {
642+ if (readAhead(true) <= 0) {
643+ return -1;
644+ }
645+ }
646+ return chunkData[chunkPos++] & 0xff;
647+ }
648+
649+
650+ /**
651+ * Reads bytes from this stream into the specified byte array, starting at
652+ * the given offset.
653+ *
654+ * @param b destination buffer.
655+ * @param off offset at which to start storing bytes.
656+ * @param len maximum number of bytes to read.
657+ * @return the number of bytes read, or <code>-1</code> if the end of
658+ * the stream has been reached.
659+ * @exception IOException if an I/O error occurs.
660+ */
661+ public synchronized int read(byte b[], int off, int len)
662+ throws IOException
663+ {
664+ ensureOpen();
665+ if ((off < 0) || (off > b.length) || (len < 0) ||
666+ ((off + len) > b.length) || ((off + len) < 0)) {
667+ throw new IndexOutOfBoundsException();
668+ } else if (len == 0) {
669+ return 0;
670+ }
671+
672+ int avail = chunkCount - chunkPos;
673+ if (avail <= 0) {
674+ /*
675+ * Optimization: if we're in the middle of the chunk read
676+ * directly from the underlying stream into the caller's
677+ * buffer
678+ */
679+ if (state == STATE_READING_CHUNK) {
680+ return fastRead( b, off, len );
681+ }
682+
683+ /*
684+ * We're not in the middle of a chunk so we must read ahead
685+ * until there is some chunk data available.
686+ */
687+ avail = readAhead(true);
688+ if (avail < 0) {
689+ return -1; /* EOF */
690+ }
691+ }
692+ int cnt = (avail < len) ? avail : len;
693+ System.arraycopy(chunkData, chunkPos, b, off, cnt);
694+ chunkPos += cnt;
695+
696+ return cnt;
697+ }
698+
699+ /**
700+ * Returns the number of bytes that can be read from this input
701+ * stream without blocking.
702+ *
703+ * @return the number of bytes that can be read from this input
704+ * stream without blocking.
705+ * @exception IOException if an I/O error occurs.
706+ * @see java.io.FilterInputStream#in
707+ */
708+ public synchronized int available() throws IOException {
709+ ensureOpen();
710+
711+ int avail = chunkCount - chunkPos;
712+ if(avail > 0) {
713+ return avail;
714+ }
715+
716+ avail = readAhead(false);
717+
718+ if (avail < 0) {
719+ return 0;
720+ } else {
721+ return avail;
722+ }
723+ }
724+
725+ /**
726+ * Close the stream by either returning the connection to the
727+ * keep alive cache or closing the underlying stream.
728+ * <p>
729+ * If the chunked response hasn't been completely read we
730+ * try to "hurry" to the end of the response. If this is
731+ * possible (without blocking) then the connection can be
732+ * returned to the keep alive cache.
733+ *
734+ * @exception IOException if an I/O error occurs.
735+ */
736+ public synchronized void close() throws IOException {
737+ if (closed) {
738+ return;
739+ }
740+ closeUnderlying();
741+ closed = true;
742+ }
743+
744+ /**
745+ * Hurry the input stream by reading everything from the underlying
746+ * stream. If the last chunk (and optional trailers) can be read without
747+ * blocking then the stream is considered hurried.
748+ * <p>
749+ * Note that if an error has occured or we can't get to last chunk
750+ * without blocking then this stream can't be hurried and should be
751+ * closed.
752+ */
753+ public synchronized boolean hurry() {
754+ if (in == null || error) {
755+ return false;
756+ }
757+
758+ try {
759+ readAhead(false);
760+ } catch (Exception e) {
761+ return false;
762+ }
763+
764+ if (error) {
765+ return false;
766+ }
767+
768+ return (state == STATE_DONE);
769+ }
770+
771+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t