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);
|
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 cbuf;
|
||||||
|
|
||||||
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
|
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
|
||||||
|
@@ -86,6 +86,7 @@ class AsyncWebServerRequest {
|
|||||||
String _temp;
|
String _temp;
|
||||||
uint8_t _parseState;
|
uint8_t _parseState;
|
||||||
|
|
||||||
|
uint8_t _version;
|
||||||
WebRequestMethod _method;
|
WebRequestMethod _method;
|
||||||
String _url;
|
String _url;
|
||||||
String _host;
|
String _host;
|
||||||
@@ -142,6 +143,7 @@ class AsyncWebServerRequest {
|
|||||||
~AsyncWebServerRequest();
|
~AsyncWebServerRequest();
|
||||||
|
|
||||||
AsyncClient* client(){ return _client; }
|
AsyncClient* client(){ return _client; }
|
||||||
|
uint8_t version(){ return _version; }
|
||||||
WebRequestMethod method(){ return _method; }
|
WebRequestMethod method(){ return _method; }
|
||||||
String url(){ return _url; }
|
String url(){ return _url; }
|
||||||
String host(){ return _host; }
|
String host(){ return _host; }
|
||||||
@@ -166,6 +168,7 @@ class AsyncWebServerRequest {
|
|||||||
AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||||
AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len);
|
AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len);
|
||||||
AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback);
|
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);
|
AsyncResponseStream *beginResponseStream(String contentType, size_t len, size_t bufferSize=1460);
|
||||||
|
|
||||||
int headers(); // get header count
|
int headers(); // get header count
|
||||||
@@ -221,6 +224,8 @@ class AsyncWebServerResponse {
|
|||||||
AsyncWebHeader *_headers;
|
AsyncWebHeader *_headers;
|
||||||
String _contentType;
|
String _contentType;
|
||||||
size_t _contentLength;
|
size_t _contentLength;
|
||||||
|
bool _sendContentLength;
|
||||||
|
bool _chunked;
|
||||||
size_t _headLength;
|
size_t _headLength;
|
||||||
size_t _sentLength;
|
size_t _sentLength;
|
||||||
size_t _ackedLength;
|
size_t _ackedLength;
|
||||||
@@ -230,8 +235,10 @@ class AsyncWebServerResponse {
|
|||||||
public:
|
public:
|
||||||
AsyncWebServerResponse();
|
AsyncWebServerResponse();
|
||||||
virtual ~AsyncWebServerResponse();
|
virtual ~AsyncWebServerResponse();
|
||||||
|
virtual void setContentLength(size_t len);
|
||||||
|
virtual void setContentType(String type);
|
||||||
virtual void addHeader(String name, String value);
|
virtual void addHeader(String name, String value);
|
||||||
virtual String _assembleHead();
|
virtual String _assembleHead(uint8_t version);
|
||||||
virtual bool _finished();
|
virtual bool _finished();
|
||||||
virtual bool _failed();
|
virtual bool _failed();
|
||||||
virtual void _respond(AsyncWebServerRequest *request);
|
virtual void _respond(AsyncWebServerRequest *request);
|
||||||
|
@@ -52,14 +52,16 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse::AsyncWebServerResponse()
|
AsyncWebServerResponse::AsyncWebServerResponse()
|
||||||
:_code(0)
|
: _code(0)
|
||||||
,_headers(NULL)
|
, _headers(NULL)
|
||||||
,_contentType()
|
, _contentType()
|
||||||
,_contentLength(0)
|
, _contentLength(0)
|
||||||
,_headLength(0)
|
, _sendContentLength(true)
|
||||||
,_sentLength(0)
|
, _chunked(false)
|
||||||
,_ackedLength(0)
|
, _headLength(0)
|
||||||
,_state(RESPONSE_SETUP)
|
, _sentLength(0)
|
||||||
|
, _ackedLength(0)
|
||||||
|
, _state(RESPONSE_SETUP)
|
||||||
{
|
{
|
||||||
addHeader("Connection","close");
|
addHeader("Connection","close");
|
||||||
addHeader("Access-Control-Allow-Origin","*");
|
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){
|
void AsyncWebServerResponse::addHeader(String name, String value){
|
||||||
AsyncWebHeader *header = new AsyncWebHeader(name, value);
|
AsyncWebHeader *header = new AsyncWebHeader(name, value);
|
||||||
if(_headers == NULL){
|
if(_headers == NULL){
|
||||||
@@ -84,12 +96,19 @@ void AsyncWebServerResponse::addHeader(String name, String value){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String AsyncWebServerResponse::_assembleHead(){
|
String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
||||||
String out = "HTTP/1.1 " + String(_code) + " " + _responseCodeToString(_code) + "\r\n";
|
if(version){
|
||||||
out += "Content-Length: " + String(_contentLength) + "\r\n";
|
addHeader("Accept-Ranges","none");
|
||||||
if(_contentType.length()){
|
if(_chunked)
|
||||||
out += "Content-Type: " + _contentType + "\r\n";
|
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;
|
AsyncWebHeader *h;
|
||||||
while(_headers != NULL){
|
while(_headers != NULL){
|
||||||
h = _headers;
|
h = _headers;
|
||||||
@@ -123,7 +142,7 @@ AsyncBasicResponse::AsyncBasicResponse(int code, String contentType, String cont
|
|||||||
|
|
||||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
||||||
_state = RESPONSE_HEADERS;
|
_state = RESPONSE_HEADERS;
|
||||||
String out = _assembleHead();
|
String out = _assembleHead(request->version());
|
||||||
size_t outLen = out.length();
|
size_t outLen = out.length();
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
if(!_contentLength && space >= outLen){
|
if(!_contentLength && space >= outLen){
|
||||||
@@ -192,7 +211,7 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
|
|||||||
request->send(500);
|
request->send(500);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_head = _assembleHead();
|
_head = _assembleHead(request->version());
|
||||||
_state = RESPONSE_HEADERS;
|
_state = RESPONSE_HEADERS;
|
||||||
size_t outLen = _head.length();
|
size_t outLen = _head.length();
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
@@ -217,18 +236,42 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
|||||||
_ackedLength += len;
|
_ackedLength += len;
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
if(_state == RESPONSE_CONTENT){
|
if(_state == RESPONSE_CONTENT){
|
||||||
size_t remaining = _contentLength - _sentLength;
|
size_t outLen;
|
||||||
size_t outLen = (remaining > space)?space:remaining;
|
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);
|
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)
|
if(outLen)
|
||||||
request->client()->write((const char*)buf, outLen);
|
outLen = request->client()->write((const char*)buf, outLen);
|
||||||
_sentLength += outLen;
|
_sentLength += outLen;
|
||||||
free(buf);
|
free(buf);
|
||||||
if(_sentLength == _contentLength){
|
|
||||||
|
if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || _sentLength == _contentLength){
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
}
|
}
|
||||||
return outLen;
|
return outLen;
|
||||||
|
|
||||||
} else if(_state == RESPONSE_HEADERS){
|
} else if(_state == RESPONSE_HEADERS){
|
||||||
size_t outLen = _head.length();
|
size_t outLen = _head.length();
|
||||||
if(space >= outLen){
|
if(space >= outLen){
|
||||||
@@ -243,8 +286,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
|||||||
return out.length();
|
return out.length();
|
||||||
}
|
}
|
||||||
} else if(_state == RESPONSE_WAIT_ACK){
|
} else if(_state == RESPONSE_WAIT_ACK){
|
||||||
if(_ackedLength >= (_headLength+_contentLength)){
|
if(!_sendContentLength || _ackedLength >= (_headLength+_contentLength)){
|
||||||
_state = RESPONSE_END;
|
_state = RESPONSE_END;
|
||||||
|
if(!_chunked && !_sendContentLength)
|
||||||
|
request->client()->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -329,6 +374,8 @@ AsyncCallbackResponse::AsyncCallbackResponse(String contentType, size_t len, Aws
|
|||||||
_code = 200;
|
_code = 200;
|
||||||
_content = callback;
|
_content = callback;
|
||||||
_contentLength = len;
|
_contentLength = len;
|
||||||
|
if(!len)
|
||||||
|
_sendContentLength = false;
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +383,23 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
|
|||||||
return _content(data, 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)
|
* 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){
|
AsyncResponseStream::AsyncResponseStream(String contentType, size_t len, size_t bufferSize){
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_contentLength = len;
|
_contentLength = len;
|
||||||
|
if(!len)
|
||||||
|
_sendContentLength = false;
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
_content = new cbuf(bufferSize);
|
_content = new cbuf(bufferSize);
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
|||||||
, _interestingHeaders(new StringArray())
|
, _interestingHeaders(new StringArray())
|
||||||
, _temp()
|
, _temp()
|
||||||
, _parseState(0)
|
, _parseState(0)
|
||||||
|
, _version(0)
|
||||||
, _method(HTTP_ANY)
|
, _method(HTTP_ANY)
|
||||||
, _url()
|
, _url()
|
||||||
, _host()
|
, _host()
|
||||||
@@ -229,6 +230,9 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_temp = _temp.substring(_temp.indexOf(' ')+1);
|
||||||
|
if(_temp.startsWith("HTTP/1.1"))
|
||||||
|
_version = 1;
|
||||||
_temp = String();
|
_temp = String();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -620,6 +624,12 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(String contentType
|
|||||||
return new AsyncCallbackResponse(contentType, len, callback);
|
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){
|
AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(String contentType, size_t len, size_t bufferSize){
|
||||||
return new AsyncResponseStream(contentType, len, bufferSize);
|
return new AsyncResponseStream(contentType, len, bufferSize);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user