src/java.base/share/classes/sun/net/www/http/ChunkedOutputStream.java
branchJDK-8229867-branch
changeset 57968 8595871a5446
parent 47216 71c04702a3d5
equal deleted inserted replaced
57966:e89c7aaf2906 57968:8595871a5446
     1 /*
     1 /*
     2  * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 package sun.net.www.http;
    25 package sun.net.www.http;
    26 
    26 
    27 import java.io.*;
    27 import java.io.*;
       
    28 import java.util.concurrent.locks.Lock;
       
    29 import java.util.concurrent.locks.ReentrantLock;
    28 
    30 
    29 /**
    31 /**
    30  * OutputStream that sends the output to the underlying stream using chunked
    32  * OutputStream that sends the output to the underlying stream using chunked
    31  * encoding as specified in RFC 2068.
    33  * encoding as specified in RFC 2068.
    32  */
    34  */
    58     private int preferredChunkDataSize;
    60     private int preferredChunkDataSize;
    59     private int preferedHeaderSize;
    61     private int preferedHeaderSize;
    60     private int preferredChunkGrossSize;
    62     private int preferredChunkGrossSize;
    61     /* header for a complete Chunk */
    63     /* header for a complete Chunk */
    62     private byte[] completeHeader;
    64     private byte[] completeHeader;
       
    65 
       
    66     private final Lock writeLock = new ReentrantLock();
    63 
    67 
    64     /* return the size of the header for a particular chunk size */
    68     /* return the size of the header for a particular chunk size */
    65     private static int getHeaderSize(int size) {
    69     private static int getHeaderSize(int size) {
    66         return (Integer.toHexString(size)).length() + CRLF_SIZE;
    70         return (Integer.toHexString(size)).length() + CRLF_SIZE;
    67     }
    71     }
   195     * chunk of preferredChunkSize size the data get stored as an incomplete
   199     * chunk of preferredChunkSize size the data get stored as an incomplete
   196     * chunk of a following format: {space for data length}{CRLF}{data}
   200     * chunk of a following format: {space for data length}{CRLF}{data}
   197     * The size of the data is of course smaller than preferredChunkSize.
   201     * The size of the data is of course smaller than preferredChunkSize.
   198     */
   202     */
   199     @Override
   203     @Override
   200     public synchronized void write(byte b[], int off, int len) {
   204     public void write(byte b[], int off, int len) {
   201         ensureOpen();
   205         writeLock.lock();
   202         if ((off < 0) || (off > b.length) || (len < 0) ||
   206         try {
   203             ((off + len) > b.length) || ((off + len) < 0)) {
   207             ensureOpen();
   204             throw new IndexOutOfBoundsException();
   208             if ((off < 0) || (off > b.length) || (len < 0) ||
   205         } else if (len == 0) {
   209                     ((off + len) > b.length) || ((off + len) < 0)) {
   206             return;
   210                 throw new IndexOutOfBoundsException();
   207         }
   211             } else if (len == 0) {
   208 
   212                 return;
   209         /* if b[] contains enough data then one loop cycle creates one complete
   213             }
   210          * data chunk with a header, body and a footer, and then flushes the
   214 
   211          * chunk to the underlying stream. Otherwise, the last loop cycle
   215             /* if b[] contains enough data then one loop cycle creates one complete
   212          * creates incomplete data chunk with empty header and with no footer
   216              * data chunk with a header, body and a footer, and then flushes the
   213          * and stores this incomplete chunk in an internal buffer buf[]
   217              * chunk to the underlying stream. Otherwise, the last loop cycle
   214          */
   218              * creates incomplete data chunk with empty header and with no footer
   215         int bytesToWrite = len;
   219              * and stores this incomplete chunk in an internal buffer buf[]
   216         int inputIndex = off;  /* the index of the byte[] currently being written */
   220              */
   217 
   221             int bytesToWrite = len;
   218         do {
   222             int inputIndex = off;  /* the index of the byte[] currently being written */
   219             /* enough data to complete a chunk */
   223 
   220             if (bytesToWrite >= spaceInCurrentChunk) {
   224             do {
   221 
   225                 /* enough data to complete a chunk */
   222                 /* header */
   226                 if (bytesToWrite >= spaceInCurrentChunk) {
   223                 for (int i=0; i<completeHeader.length; i++)
   227 
   224                     buf[i] = completeHeader[i];
   228                     /* header */
   225 
   229                     for (int i = 0; i < completeHeader.length; i++)
   226                 /* data */
   230                         buf[i] = completeHeader[i];
   227                 System.arraycopy(b, inputIndex, buf, count, spaceInCurrentChunk);
   231 
   228                 inputIndex += spaceInCurrentChunk;
   232                     /* data */
   229                 bytesToWrite -= spaceInCurrentChunk;
   233                     System.arraycopy(b, inputIndex, buf, count, spaceInCurrentChunk);
   230                 count += spaceInCurrentChunk;
   234                     inputIndex += spaceInCurrentChunk;
   231 
   235                     bytesToWrite -= spaceInCurrentChunk;
   232                 /* footer */
   236                     count += spaceInCurrentChunk;
   233                 buf[count++] = FOOTER[0];
   237 
   234                 buf[count++] = FOOTER[1];
   238                     /* footer */
   235                 spaceInCurrentChunk = 0; //chunk is complete
   239                     buf[count++] = FOOTER[0];
   236 
   240                     buf[count++] = FOOTER[1];
   237                 flush(false);
   241                     spaceInCurrentChunk = 0; //chunk is complete
   238                 if (checkError()){
   242 
   239                     break;
   243                     flush(false);
       
   244                     if (checkError()) {
       
   245                         break;
       
   246                     }
   240                 }
   247                 }
   241             }
   248 
   242 
   249                 /* not enough data to build a chunk */
   243             /* not enough data to build a chunk */
   250                 else {
   244             else {
   251                     /* header */
   245                 /* header */
   252                     /* do not write header if not enough bytes to build a chunk yet */
   246                 /* do not write header if not enough bytes to build a chunk yet */
   253 
   247 
   254                     /* data */
   248                 /* data */
   255                     System.arraycopy(b, inputIndex, buf, count, bytesToWrite);
   249                 System.arraycopy(b, inputIndex, buf, count, bytesToWrite);
   256                     count += bytesToWrite;
   250                 count += bytesToWrite;
   257                     size += bytesToWrite;
   251                 size += bytesToWrite;
   258                     spaceInCurrentChunk -= bytesToWrite;
   252                 spaceInCurrentChunk -= bytesToWrite;
   259                     bytesToWrite = 0;
   253                 bytesToWrite = 0;
   260 
   254 
   261                     /* footer */
   255                 /* footer */
   262                     /* do not write header if not enough bytes to build a chunk yet */
   256                 /* do not write header if not enough bytes to build a chunk yet */
   263                 }
   257             }
   264             } while (bytesToWrite > 0);
   258         } while (bytesToWrite > 0);
   265         } finally {
   259     }
   266             writeLock.unlock();
   260 
   267         }
   261     @Override
   268     }
   262     public synchronized void write(int _b) {
   269 
   263         byte b[] = {(byte)_b};
   270     @Override
   264         write(b, 0, 1);
   271     public void write(int _b) {
   265     }
   272         writeLock.lock();
   266 
   273         try {
   267     public synchronized void reset() {
   274             byte b[] = {(byte) _b};
   268         count = preferedHeaderSize;
   275             write(b, 0, 1);
   269         size = 0;
   276         } finally {
   270         spaceInCurrentChunk = preferredChunkDataSize;
   277             writeLock.unlock();
       
   278         }
       
   279     }
       
   280 
       
   281     public void reset() {
       
   282         writeLock.lock();
       
   283         try {
       
   284             count = preferedHeaderSize;
       
   285             size = 0;
       
   286             spaceInCurrentChunk = preferredChunkDataSize;
       
   287         } finally {
       
   288             writeLock.unlock();
       
   289         }
   271     }
   290     }
   272 
   291 
   273     public int size() {
   292     public int size() {
   274         return size;
   293         return size;
   275     }
   294     }
   276 
   295 
   277     @Override
   296     @Override
   278     public synchronized void close() {
   297     public void close() {
   279         ensureOpen();
   298         writeLock.lock();
   280 
   299         try {
   281         /* if we have buffer a chunked send it */
   300             ensureOpen();
   282         if (size > 0) {
   301 
       
   302             /* if we have buffer a chunked send it */
       
   303             if (size > 0) {
       
   304                 flush(true);
       
   305             }
       
   306 
       
   307             /* send a zero length chunk */
   283             flush(true);
   308             flush(true);
   284         }
   309 
   285 
   310             /* don't close the underlying stream */
   286         /* send a zero length chunk */
   311             out = null;
   287         flush(true);
   312         } finally {
   288 
   313             writeLock.unlock();
   289         /* don't close the underlying stream */
   314         }
   290         out = null;
   315     }
   291     }
   316 
   292 
   317     @Override
   293     @Override
   318     public void flush() {
   294     public synchronized void flush() {
   319         writeLock.lock();
   295         ensureOpen();
   320         try {
   296         if (size > 0) {
   321             ensureOpen();
   297             flush(true);
   322             if (size > 0) {
       
   323                 flush(true);
       
   324             }
       
   325         } finally {
       
   326             writeLock.unlock();
   298         }
   327         }
   299     }
   328     }
   300 }
   329 }