readChunkedBody
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user