< prev index next >

src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java

Print this page




  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 
  26 package jdk.incubator.http;
  27 
  28 import java.io.IOException;
  29 import java.nio.ByteBuffer;

  30 import java.util.Optional;
  31 import java.util.function.Consumer;
  32 import jdk.incubator.http.internal.common.Utils;
  33 
  34 /**
  35  * Implements chunked/fixed transfer encodings of HTTP/1.1 responses.
  36  *
  37  * Call pushBody() to read the body (blocking). Data and errors are provided
  38  * to given Consumers. After final buffer delivered, empty optional delivered
  39  */
  40 class ResponseContent {
  41 
  42     final HttpResponse.BodyProcessor<?> pusher;
  43     final HttpConnection connection;
  44     final int contentLength;
  45     ByteBuffer buffer;
  46     //ByteBuffer lastBufferUsed;
  47     final ResponseHeaders headers;
  48     private final Consumer<Optional<ByteBuffer>> dataConsumer;
  49     private final Consumer<IOException> errorConsumer;
  50     private final HttpClientImpl client;
  51     // this needs to run before we complete the body
  52     // so that connection can be returned to pool
  53     private final Runnable onFinished;
  54 
  55     ResponseContent(HttpConnection connection,
  56                     int contentLength,
  57                     ResponseHeaders h,
  58                     HttpResponse.BodyProcessor<?> userProcessor,
  59                     Consumer<Optional<ByteBuffer>> dataConsumer,
  60                     Consumer<IOException> errorConsumer,
  61                     Runnable onFinished)
  62     {
  63         this.pusher = (HttpResponse.BodyProcessor)userProcessor;
  64         this.connection = connection;
  65         this.contentLength = contentLength;
  66         this.headers = h;
  67         this.dataConsumer = dataConsumer;
  68         this.errorConsumer = errorConsumer;
  69         this.client = connection.client;
  70         this.onFinished = onFinished;
  71     }
  72 
  73     static final int LF = 10;
  74     static final int CR = 13;
  75     static final int SP = 0x20;
  76     static final int BUF_SIZE = 1024;
  77 
  78     boolean chunkedContent, chunkedContentInitialized;
  79 


 210 
 211     /**
 212      * Copies inbuf (numBytes from its position) to new buffer. The returned
 213      * buffer's position is zero and limit is at end (numBytes)
 214      */
 215     private ByteBuffer copyBuffer(ByteBuffer inbuf, int numBytes) {
 216         ByteBuffer b1 = Utils.getBuffer();
 217         assert b1.remaining() >= numBytes;
 218         byte[] b = b1.array();
 219         inbuf.get(b, 0, numBytes);
 220         b1.limit(numBytes);
 221         return b1;
 222     }
 223 
 224     private void pushBodyChunked(ByteBuffer b) throws IOException {
 225         chunkbuf = b;
 226         while (true) {
 227             ByteBuffer b1 = readChunkedBuffer();
 228             if (b1 != null) {
 229                 if (b1.hasRemaining()) {
 230                     dataConsumer.accept(Optional.of(b1));
 231                 }
 232             } else {
 233                 onFinished.run();
 234                 dataConsumer.accept(Optional.empty());
 235                 return;
 236             }
 237         }
 238     }
 239 
 240     private int toDigit(int b) throws IOException {
 241         if (b >= 0x30 && b <= 0x39) {
 242             return b - 0x30;
 243         }
 244         if (b >= 0x41 && b <= 0x46) {
 245             return b - 0x41 + 10;
 246         }
 247         if (b >= 0x61 && b <= 0x66) {
 248             return b - 0x61 + 10;
 249         }
 250         throw new IOException("Invalid chunk header byte " + b);
 251     }
 252 
 253     private void pushBodyFixed(ByteBuffer b) throws IOException {
 254         int remaining = contentLength;
 255         while (b.hasRemaining() && remaining > 0) {
 256             ByteBuffer buffer = Utils.getBuffer();
 257             int amount = Math.min(b.remaining(), remaining);
 258             Utils.copy(b, buffer, amount);
 259             remaining -= amount;
 260             buffer.flip();
 261             dataConsumer.accept(Optional.of(buffer));
 262         }
 263         while (remaining > 0) {
 264             ByteBuffer buffer = connection.read();
 265             if (buffer == null)
 266                 throw new IOException("connection closed");
 267 
 268             int bytesread = buffer.remaining();
 269             // assume for now that pipelining not implemented
 270             if (bytesread > remaining) {
 271                 throw new IOException("too many bytes read");
 272             }
 273             remaining -= bytesread;
 274             dataConsumer.accept(Optional.of(buffer));
 275         }
 276         onFinished.run();
 277         dataConsumer.accept(Optional.empty());
 278     }
 279 }


  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 
  26 package jdk.incubator.http;
  27 
  28 import java.io.IOException;
  29 import java.nio.ByteBuffer;
  30 import java.util.List;
  31 import java.util.Optional;
  32 import java.util.function.Consumer;
  33 import jdk.incubator.http.internal.common.Utils;
  34 
  35 /**
  36  * Implements chunked/fixed transfer encodings of HTTP/1.1 responses.
  37  *
  38  * Call pushBody() to read the body (blocking). Data and errors are provided
  39  * to given Consumers. After final buffer delivered, empty optional delivered
  40  */
  41 class ResponseContent {
  42 
  43     final HttpResponse.BodyProcessor<?> pusher;
  44     final HttpConnection connection;
  45     final int contentLength;
  46     ByteBuffer buffer;
  47     //ByteBuffer lastBufferUsed;
  48     final ResponseHeaders headers;
  49     private final Consumer<Optional<List<ByteBuffer>>> dataConsumer;
  50     private final Consumer<IOException> errorConsumer;
  51     private final HttpClientImpl client;
  52     // this needs to run before we complete the body
  53     // so that connection can be returned to pool
  54     private final Runnable onFinished;
  55 
  56     ResponseContent(HttpConnection connection,
  57                     int contentLength,
  58                     ResponseHeaders h,
  59                     HttpResponse.BodyProcessor<?> userProcessor,
  60                     Consumer<Optional<List<ByteBuffer>>> dataConsumer,
  61                     Consumer<IOException> errorConsumer,
  62                     Runnable onFinished)
  63     {
  64         this.pusher = (HttpResponse.BodyProcessor)userProcessor;
  65         this.connection = connection;
  66         this.contentLength = contentLength;
  67         this.headers = h;
  68         this.dataConsumer = dataConsumer;
  69         this.errorConsumer = errorConsumer;
  70         this.client = connection.client;
  71         this.onFinished = onFinished;
  72     }
  73 
  74     static final int LF = 10;
  75     static final int CR = 13;
  76     static final int SP = 0x20;
  77     static final int BUF_SIZE = 1024;
  78 
  79     boolean chunkedContent, chunkedContentInitialized;
  80 


 211 
 212     /**
 213      * Copies inbuf (numBytes from its position) to new buffer. The returned
 214      * buffer's position is zero and limit is at end (numBytes)
 215      */
 216     private ByteBuffer copyBuffer(ByteBuffer inbuf, int numBytes) {
 217         ByteBuffer b1 = Utils.getBuffer();
 218         assert b1.remaining() >= numBytes;
 219         byte[] b = b1.array();
 220         inbuf.get(b, 0, numBytes);
 221         b1.limit(numBytes);
 222         return b1;
 223     }
 224 
 225     private void pushBodyChunked(ByteBuffer b) throws IOException {
 226         chunkbuf = b;
 227         while (true) {
 228             ByteBuffer b1 = readChunkedBuffer();
 229             if (b1 != null) {
 230                 if (b1.hasRemaining()) {
 231                     dataConsumer.accept(Optional.of(List.of(b1)));
 232                 }
 233             } else {
 234                 onFinished.run();
 235                 dataConsumer.accept(Optional.empty());
 236                 return;
 237             }
 238         }
 239     }
 240 
 241     private int toDigit(int b) throws IOException {
 242         if (b >= 0x30 && b <= 0x39) {
 243             return b - 0x30;
 244         }
 245         if (b >= 0x41 && b <= 0x46) {
 246             return b - 0x41 + 10;
 247         }
 248         if (b >= 0x61 && b <= 0x66) {
 249             return b - 0x61 + 10;
 250         }
 251         throw new IOException("Invalid chunk header byte " + b);
 252     }
 253 
 254     private void pushBodyFixed(ByteBuffer b) throws IOException {
 255         int remaining = contentLength;
 256         while (b.hasRemaining() && remaining > 0) {
 257             ByteBuffer buffer = Utils.getBuffer();
 258             int amount = Math.min(b.remaining(), remaining);
 259             Utils.copy(b, buffer, amount);
 260             remaining -= amount;
 261             buffer.flip();
 262             dataConsumer.accept(Optional.of(List.of(buffer)));
 263         }
 264         while (remaining > 0) {
 265             ByteBuffer buffer = connection.read();
 266             if (buffer == null)
 267                 throw new IOException("connection closed");
 268 
 269             int bytesread = buffer.remaining();
 270             // assume for now that pipelining not implemented
 271             if (bytesread > remaining) {
 272                 throw new IOException("too many bytes read");
 273             }
 274             remaining -= bytesread;
 275             dataConsumer.accept(Optional.of(List.of(buffer)));
 276         }
 277         onFinished.run();
 278         dataConsumer.accept(Optional.empty());
 279     }
 280 }
< prev index next >