readChunkedBody

This commit is contained in:
redkale
2024-08-16 12:00:19 +08:00
parent e7ed4ae9bc
commit db0b81b4a0
3 changed files with 144 additions and 11 deletions

View File

@@ -120,6 +120,8 @@ public class HttpRequest extends Request<HttpContext> {
// @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<HttpContext> {
protected String locale;
// 主要给chunked和unzip用的
private ByteArray array;
private String lastPathString;
@@ -367,7 +370,7 @@ public class HttpRequest extends Request<HttpContext> {
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<HttpContext> {
}
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<HttpContext> {
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<HttpContext> {
// 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<HttpContext> {
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<HttpContext> {
body.put(out.toByteArray());
}
} catch (Exception e) {
// do nothing
throw new RedkaleException("invalid encoding content");
}
}

View File

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

View File

@@ -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();