From db0b81b4a06462e81f6c64af08022084119f7481 Mon Sep 17 00:00:00 2001 From: redkale Date: Fri, 16 Aug 2024 12:00:19 +0800 Subject: [PATCH] readChunkedBody --- .../org/redkale/net/http/HttpRequest.java | 137 ++++++++++++++++-- .../redkale/test/http/RestSleepService.java | 6 + .../org/redkale/test/http/RestSleepTest.java | 12 ++ 3 files changed, 144 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/redkale/net/http/HttpRequest.java b/src/main/java/org/redkale/net/http/HttpRequest.java index 273a1a2cb..e16c9a6a2 100644 --- a/src/main/java/org/redkale/net/http/HttpRequest.java +++ b/src/main/java/org/redkale/net/http/HttpRequest.java @@ -120,6 +120,8 @@ public class HttpRequest extends Request { // @since 2.8.0 protected boolean chunked = false; + // 是否已读\r + protected boolean chunkedCR = false; protected int chunkedLength = -1; @@ -174,6 +176,7 @@ public class HttpRequest extends Request { protected String locale; + // 主要给chunked和unzip用的 private ByteArray array; private String lastPathString; @@ -367,7 +370,7 @@ public class HttpRequest extends Request { this.headerBytes = httpLast.headerBytes; this.headerParsed = httpLast.headerParsed; this.headers.setAll(httpLast.headers); - } else if (context.lazyHeaders) { // 非lazy必须要读header,会有Content-Length + } else if (context.lazyHeaders && getmethod) { // 非GET必须要读header,会有Content-Length int rs = loadHeaderBytes(buffer); if (rs != 0) { buffer.clear(); @@ -418,11 +421,6 @@ public class HttpRequest extends Request { } return lr > 0 ? lr : 0; } - // keep-alive=true: Content-Length和chunk必然是二选一。 - // keep-alive=false: Content-Length可有可无. - if (keepAlive && this.contentLength < 0) { - return -1; - } // 文件上传、HTTP1.0或Connection:close if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) { bytes.put(buffer, buffer.remaining()); @@ -430,6 +428,10 @@ public class HttpRequest extends Request { this.readState = READ_STATE_END; if (bytes.isEmpty()) { this.bodyParsed = true; // no body data + } else if (!getmethod && this.contentLength < 0 && keepAlive) { + // keep-alive=true: Content-Length和chunk必然是二选一。 + // keep-alive=false: Content-Length可有可无. + return -1; } } // 暂不考虑是keep-alive且存在body却没有指定Content-Length的情况 @@ -438,11 +440,123 @@ public class HttpRequest extends Request { // TODO 待实现 private int readChunkedBody(final ByteBuffer buf) { - if (this.chunkedLength < 0) { - int size; + final ByteBuffer buffer = buf; + int remain = buffer.remaining(); + if (this.chunkedLength < 0) { // 需要读取length + ByteArray input = array(); + input.clear(); + if (this.chunkedHalfLenBytes != null) { + input.put(this.chunkedHalfLenBytes); + this.chunkedHalfLenBytes = null; + } + for (; ; ) { + if (remain-- < 1) { + buffer.clear(); + if (input.length() > 0) { + this.chunkedHalfLenBytes = input.getBytes(); + } + return 1; + } + byte b = buffer.get(); + if (b == '\n') { + break; + } + input.put(b); + } + this.chunkedLength = parseHexLength(input); + this.chunkedCurrOffset = 0; + this.chunkedCR = false; } - ByteArray bodyBytes = this.body; - return -1; + if (this.chunkedLength == 0) { + if (remain < 1) { + buffer.clear(); + return 1; + } + if (!this.chunkedCR) { // 读\r + remain--; + if (buffer.get() != '\r') { + throw new RedkaleException("invalid chunk end"); + } + this.chunkedCR = true; + if (remain < 1) { + buffer.clear(); + return 1; + } + } + // 读\n + remain--; + if (buffer.get() != '\n') { + throw new RedkaleException("invalid chunk end"); + } + this.readState = READ_STATE_END; + if (this.body.isEmpty()) { + this.bodyParsed = true; // no body data + } + return 0; + } else { + ByteArray bodyBytes = this.body; + if (this.chunkedCurrOffset < this.chunkedLength) { + for (; ; ) { + if (remain-- < 1) { + buffer.clear(); + return 1; + } + byte b = buffer.get(); + bodyBytes.put(b); + this.chunkedCurrOffset++; + if (this.chunkedCurrOffset == this.chunkedLength) { + this.chunkedCR = false; + break; + } + } + } + if (remain < 1) { + buffer.clear(); + return 1; + } + // 读\r + if (!this.chunkedCR) { + remain--; + if (buffer.get() != '\r') { + throw new RedkaleException("invalid chunk end"); + } + this.chunkedCR = true; + if (remain < 1) { + buffer.clear(); + return 1; + } + } + // 读\n + remain--; + if (buffer.get() != '\n') { + throw new RedkaleException("invalid chunk end"); + } + this.chunkedLength = -1; + // 继续读下一个chunk + return readChunkedBody(buffer); + } + } + + private static int parseHexLength(ByteArray input) { + int count = input.length(); + int len = 0; + for (int i = 0; i < count; i++) { + byte c = input.get(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 if (c == '\r' || c == ';') { // ;后面是注释内容 + break; + } else { + throw new RedkaleException("invalid chunk length"); + } + len = len * 16 + val; + } + return len; } private int loadHeaderBytes(final ByteBuffer buf) { @@ -1155,6 +1269,7 @@ public class HttpRequest extends Request { this.maybews = false; this.expect = false; this.chunked = false; + this.chunkedCR = false; this.chunkedLength = -1; this.chunkedCurrOffset = -1; this.chunkedHalfLenBytes = null; @@ -1242,7 +1357,7 @@ public class HttpRequest extends Request { body.put(out.toByteArray()); } } catch (Exception e) { - // do nothing + throw new RedkaleException("invalid encoding content"); } } diff --git a/src/test/java/org/redkale/test/http/RestSleepService.java b/src/test/java/org/redkale/test/http/RestSleepService.java index 62611277a..cf9931ae1 100644 --- a/src/test/java/org/redkale/test/http/RestSleepService.java +++ b/src/test/java/org/redkale/test/http/RestSleepService.java @@ -5,6 +5,7 @@ */ package org.redkale.test.http; +import org.redkale.net.http.RestBody; import org.redkale.net.http.RestService; import org.redkale.service.AbstractService; import org.redkale.util.Utility; @@ -33,4 +34,9 @@ public class RestSleepService extends AbstractService { Utility.sleep(500); return "ok500"; } + + public String sleepName(@RestBody String name) { + System.out.println("获取到的名字: " + name); + return "sleep: " + name; + } } diff --git a/src/test/java/org/redkale/test/http/RestSleepTest.java b/src/test/java/org/redkale/test/http/RestSleepTest.java index e4cd35076..6709ee8ad 100644 --- a/src/test/java/org/redkale/test/http/RestSleepTest.java +++ b/src/test/java/org/redkale/test/http/RestSleepTest.java @@ -63,6 +63,18 @@ public class RestSleepTest { + "GET /test/sleep500 HTTP/1.1\r\n" + "Connection: Keep-Alive\r\n" + "Content-Length: 0\r\n" + + "\r\n" + + "GET /test/sleepName HTTP/1.1\r\n" + + "Connection: Keep-Alive\r\n" + + "Transfer-Encoding: chunked\r\n" + + "\r\n" + + "4\r\n" + + "this\r\n" + + "6\r\n" + + " is a \r\n" + + "5\r\n" + + "name!\r\n" + + "0\r\n" + "\r\n") .getBytes()); InputStream in = socket.getInputStream();