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 // @since 2.8.0
protected boolean chunked = false; protected boolean chunked = false;
// 是否已读\r
protected boolean chunkedCR = false;
protected int chunkedLength = -1; protected int chunkedLength = -1;
@@ -174,6 +176,7 @@ public class HttpRequest extends Request<HttpContext> {
protected String locale; protected String locale;
// 主要给chunked和unzip用的
private ByteArray array; private ByteArray array;
private String lastPathString; private String lastPathString;
@@ -367,7 +370,7 @@ public class HttpRequest extends Request<HttpContext> {
this.headerBytes = httpLast.headerBytes; this.headerBytes = httpLast.headerBytes;
this.headerParsed = httpLast.headerParsed; this.headerParsed = httpLast.headerParsed;
this.headers.setAll(httpLast.headers); 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); int rs = loadHeaderBytes(buffer);
if (rs != 0) { if (rs != 0) {
buffer.clear(); buffer.clear();
@@ -418,11 +421,6 @@ public class HttpRequest extends Request<HttpContext> {
} }
return lr > 0 ? lr : 0; 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 // 文件上传、HTTP1.0或Connection:close
if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) { if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) {
bytes.put(buffer, buffer.remaining()); bytes.put(buffer, buffer.remaining());
@@ -430,6 +428,10 @@ public class HttpRequest extends Request<HttpContext> {
this.readState = READ_STATE_END; this.readState = READ_STATE_END;
if (bytes.isEmpty()) { if (bytes.isEmpty()) {
this.bodyParsed = true; // no body data 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的情况 // 暂不考虑是keep-alive且存在body却没有指定Content-Length的情况
@@ -438,11 +440,123 @@ public class HttpRequest extends Request<HttpContext> {
// TODO 待实现 // TODO 待实现
private int readChunkedBody(final ByteBuffer buf) { private int readChunkedBody(final ByteBuffer buf) {
if (this.chunkedLength < 0) { final ByteBuffer buffer = buf;
int size; 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; if (this.chunkedLength == 0) {
return -1; 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) { private int loadHeaderBytes(final ByteBuffer buf) {
@@ -1155,6 +1269,7 @@ public class HttpRequest extends Request<HttpContext> {
this.maybews = false; this.maybews = false;
this.expect = false; this.expect = false;
this.chunked = false; this.chunked = false;
this.chunkedCR = false;
this.chunkedLength = -1; this.chunkedLength = -1;
this.chunkedCurrOffset = -1; this.chunkedCurrOffset = -1;
this.chunkedHalfLenBytes = null; this.chunkedHalfLenBytes = null;
@@ -1242,7 +1357,7 @@ public class HttpRequest extends Request<HttpContext> {
body.put(out.toByteArray()); body.put(out.toByteArray());
} }
} catch (Exception e) { } catch (Exception e) {
// do nothing throw new RedkaleException("invalid encoding content");
} }
} }

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.test.http; package org.redkale.test.http;
import org.redkale.net.http.RestBody;
import org.redkale.net.http.RestService; import org.redkale.net.http.RestService;
import org.redkale.service.AbstractService; import org.redkale.service.AbstractService;
import org.redkale.util.Utility; import org.redkale.util.Utility;
@@ -33,4 +34,9 @@ public class RestSleepService extends AbstractService {
Utility.sleep(500); Utility.sleep(500);
return "ok500"; 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" + "GET /test/sleep500 HTTP/1.1\r\n"
+ "Connection: Keep-Alive\r\n" + "Connection: Keep-Alive\r\n"
+ "Content-Length: 0\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") + "\r\n")
.getBytes()); .getBytes());
InputStream in = socket.getInputStream(); InputStream in = socket.getInputStream();