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