forked from me-no-dev/ESPAsyncWebServer
Add HTTP/1.1 chunked response and allow stream and callback responses to not have content length
ContentLength and ContentType have setters (before you send the response)
This commit is contained in:
@@ -57,6 +57,15 @@ class AsyncCallbackResponse: public AsyncAbstractResponse {
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen);
|
||||
};
|
||||
|
||||
class AsyncChunkedResponse: public AsyncAbstractResponse {
|
||||
private:
|
||||
AwsResponseFiller _content;
|
||||
public:
|
||||
AsyncChunkedResponse(String contentType, AwsResponseFiller callback);
|
||||
bool _sourceValid(){ return !!(_content); }
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen);
|
||||
};
|
||||
|
||||
class cbuf;
|
||||
|
||||
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
|
||||
|
@@ -86,6 +86,7 @@ class AsyncWebServerRequest {
|
||||
String _temp;
|
||||
uint8_t _parseState;
|
||||
|
||||
uint8_t _version;
|
||||
WebRequestMethod _method;
|
||||
String _url;
|
||||
String _host;
|
||||
@@ -142,6 +143,7 @@ class AsyncWebServerRequest {
|
||||
~AsyncWebServerRequest();
|
||||
|
||||
AsyncClient* client(){ return _client; }
|
||||
uint8_t version(){ return _version; }
|
||||
WebRequestMethod method(){ return _method; }
|
||||
String url(){ return _url; }
|
||||
String host(){ return _host; }
|
||||
@@ -166,6 +168,7 @@ class AsyncWebServerRequest {
|
||||
AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||
AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len);
|
||||
AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback);
|
||||
AsyncWebServerResponse *beginChunkedResponse(String contentType, AwsResponseFiller callback);
|
||||
AsyncResponseStream *beginResponseStream(String contentType, size_t len, size_t bufferSize=1460);
|
||||
|
||||
int headers(); // get header count
|
||||
@@ -221,6 +224,8 @@ class AsyncWebServerResponse {
|
||||
AsyncWebHeader *_headers;
|
||||
String _contentType;
|
||||
size_t _contentLength;
|
||||
bool _sendContentLength;
|
||||
bool _chunked;
|
||||
size_t _headLength;
|
||||
size_t _sentLength;
|
||||
size_t _ackedLength;
|
||||
@@ -230,8 +235,10 @@ class AsyncWebServerResponse {
|
||||
public:
|
||||
AsyncWebServerResponse();
|
||||
virtual ~AsyncWebServerResponse();
|
||||
virtual void setContentLength(size_t len);
|
||||
virtual void setContentType(String type);
|
||||
virtual void addHeader(String name, String value);
|
||||
virtual String _assembleHead();
|
||||
virtual String _assembleHead(uint8_t version);
|
||||
virtual bool _finished();
|
||||
virtual bool _failed();
|
||||
virtual void _respond(AsyncWebServerRequest *request);
|
||||
|
@@ -52,14 +52,16 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) {
|
||||
}
|
||||
|
||||
AsyncWebServerResponse::AsyncWebServerResponse()
|
||||
:_code(0)
|
||||
,_headers(NULL)
|
||||
,_contentType()
|
||||
,_contentLength(0)
|
||||
,_headLength(0)
|
||||
,_sentLength(0)
|
||||
,_ackedLength(0)
|
||||
,_state(RESPONSE_SETUP)
|
||||
: _code(0)
|
||||
, _headers(NULL)
|
||||
, _contentType()
|
||||
, _contentLength(0)
|
||||
, _sendContentLength(true)
|
||||
, _chunked(false)
|
||||
, _headLength(0)
|
||||
, _sentLength(0)
|
||||
, _ackedLength(0)
|
||||
, _state(RESPONSE_SETUP)
|
||||
{
|
||||
addHeader("Connection","close");
|
||||
addHeader("Access-Control-Allow-Origin","*");
|
||||
@@ -73,6 +75,16 @@ AsyncWebServerResponse::~AsyncWebServerResponse(){
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::setContentLength(size_t len){
|
||||
if(_state == RESPONSE_SETUP)
|
||||
_contentLength = len;
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::setContentType(String type){
|
||||
if(_state == RESPONSE_SETUP)
|
||||
_contentType = type;
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::addHeader(String name, String value){
|
||||
AsyncWebHeader *header = new AsyncWebHeader(name, value);
|
||||
if(_headers == NULL){
|
||||
@@ -84,12 +96,19 @@ void AsyncWebServerResponse::addHeader(String name, String value){
|
||||
}
|
||||
}
|
||||
|
||||
String AsyncWebServerResponse::_assembleHead(){
|
||||
String out = "HTTP/1.1 " + String(_code) + " " + _responseCodeToString(_code) + "\r\n";
|
||||
out += "Content-Length: " + String(_contentLength) + "\r\n";
|
||||
if(_contentType.length()){
|
||||
out += "Content-Type: " + _contentType + "\r\n";
|
||||
String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
||||
if(version){
|
||||
addHeader("Accept-Ranges","none");
|
||||
if(_chunked)
|
||||
addHeader("Transfer-Encoding","chunked");
|
||||
}
|
||||
String out = "HTTP/1." + String(version) + " " + String(_code) + " " + _responseCodeToString(_code) + "\r\n";
|
||||
if(_sendContentLength)
|
||||
out += "Content-Length: " + String(_contentLength) + "\r\n";
|
||||
|
||||
if(_contentType.length())
|
||||
out += "Content-Type: " + _contentType + "\r\n";
|
||||
|
||||
AsyncWebHeader *h;
|
||||
while(_headers != NULL){
|
||||
h = _headers;
|
||||
@@ -123,7 +142,7 @@ AsyncBasicResponse::AsyncBasicResponse(int code, String contentType, String cont
|
||||
|
||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
||||
_state = RESPONSE_HEADERS;
|
||||
String out = _assembleHead();
|
||||
String out = _assembleHead(request->version());
|
||||
size_t outLen = out.length();
|
||||
size_t space = request->client()->space();
|
||||
if(!_contentLength && space >= outLen){
|
||||
@@ -192,7 +211,7 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
|
||||
request->send(500);
|
||||
return;
|
||||
}
|
||||
_head = _assembleHead();
|
||||
_head = _assembleHead(request->version());
|
||||
_state = RESPONSE_HEADERS;
|
||||
size_t outLen = _head.length();
|
||||
size_t space = request->client()->space();
|
||||
@@ -217,18 +236,42 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
_ackedLength += len;
|
||||
size_t space = request->client()->space();
|
||||
if(_state == RESPONSE_CONTENT){
|
||||
size_t remaining = _contentLength - _sentLength;
|
||||
size_t outLen = (remaining > space)?space:remaining;
|
||||
size_t outLen;
|
||||
size_t readLen = 0;
|
||||
|
||||
if(_chunked || !_sendContentLength){
|
||||
outLen = space;
|
||||
} else {
|
||||
size_t remaining = _contentLength - _sentLength;
|
||||
outLen = (remaining > space)?space:remaining;
|
||||
}
|
||||
uint8_t *buf = (uint8_t *)malloc(outLen);
|
||||
outLen = _fillBuffer(buf, outLen);
|
||||
|
||||
if(_chunked){
|
||||
readLen = _fillBuffer(buf, outLen - 8);
|
||||
char pre[6];
|
||||
sprintf(pre, "%x\r\n", readLen);
|
||||
size_t preLen = strlen(pre);
|
||||
memmove(buf+preLen, buf, preLen);
|
||||
for(size_t i=0; i<preLen; i++)
|
||||
buf[i] = pre[i];
|
||||
outLen = preLen + readLen;
|
||||
buf[outLen++] = '\r';
|
||||
buf[outLen++] = '\n';
|
||||
} else {
|
||||
outLen = _fillBuffer(buf, outLen);
|
||||
}
|
||||
|
||||
if(outLen)
|
||||
request->client()->write((const char*)buf, outLen);
|
||||
outLen = request->client()->write((const char*)buf, outLen);
|
||||
_sentLength += outLen;
|
||||
free(buf);
|
||||
if(_sentLength == _contentLength){
|
||||
|
||||
if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || _sentLength == _contentLength){
|
||||
_state = RESPONSE_WAIT_ACK;
|
||||
}
|
||||
return outLen;
|
||||
|
||||
} else if(_state == RESPONSE_HEADERS){
|
||||
size_t outLen = _head.length();
|
||||
if(space >= outLen){
|
||||
@@ -243,8 +286,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
return out.length();
|
||||
}
|
||||
} else if(_state == RESPONSE_WAIT_ACK){
|
||||
if(_ackedLength >= (_headLength+_contentLength)){
|
||||
if(!_sendContentLength || _ackedLength >= (_headLength+_contentLength)){
|
||||
_state = RESPONSE_END;
|
||||
if(!_chunked && !_sendContentLength)
|
||||
request->client()->close();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -329,6 +374,8 @@ AsyncCallbackResponse::AsyncCallbackResponse(String contentType, size_t len, Aws
|
||||
_code = 200;
|
||||
_content = callback;
|
||||
_contentLength = len;
|
||||
if(!len)
|
||||
_sendContentLength = false;
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
@@ -336,6 +383,23 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
return _content(data, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Chunked Response
|
||||
* */
|
||||
|
||||
AsyncChunkedResponse::AsyncChunkedResponse(String contentType, AwsResponseFiller callback){
|
||||
_code = 200;
|
||||
_content = callback;
|
||||
_contentLength = 0;
|
||||
_contentType = contentType;
|
||||
_sendContentLength = false;
|
||||
_chunked = true;
|
||||
}
|
||||
|
||||
size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
return _content(data, len);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Response Stream (You can print/write/printf to it, up to the contentLen bytes)
|
||||
@@ -344,6 +408,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||
AsyncResponseStream::AsyncResponseStream(String contentType, size_t len, size_t bufferSize){
|
||||
_code = 200;
|
||||
_contentLength = len;
|
||||
if(!len)
|
||||
_sendContentLength = false;
|
||||
_contentType = contentType;
|
||||
_content = new cbuf(bufferSize);
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
||||
, _interestingHeaders(new StringArray())
|
||||
, _temp()
|
||||
, _parseState(0)
|
||||
, _version(0)
|
||||
, _method(HTTP_ANY)
|
||||
, _url()
|
||||
, _host()
|
||||
@@ -229,6 +230,9 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
||||
}
|
||||
}
|
||||
|
||||
_temp = _temp.substring(_temp.indexOf(' ')+1);
|
||||
if(_temp.startsWith("HTTP/1.1"))
|
||||
_version = 1;
|
||||
_temp = String();
|
||||
return true;
|
||||
}
|
||||
@@ -620,6 +624,12 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(String contentType
|
||||
return new AsyncCallbackResponse(contentType, len, callback);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(String contentType, AwsResponseFiller callback){
|
||||
if(_version)
|
||||
return new AsyncChunkedResponse(contentType, callback);
|
||||
return new AsyncCallbackResponse(contentType, 0, callback);
|
||||
}
|
||||
|
||||
AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(String contentType, size_t len, size_t bufferSize){
|
||||
return new AsyncResponseStream(contentType, len, bufferSize);
|
||||
}
|
||||
|
Reference in New Issue
Block a user