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:
Me No Dev
2016-01-27 18:46:49 +02:00
parent 4a5f64de03
commit 9296156ab5
4 changed files with 114 additions and 22 deletions

View File

@@ -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 {

View File

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

View File

@@ -56,6 +56,8 @@ AsyncWebServerResponse::AsyncWebServerResponse()
, _headers(NULL)
, _contentType()
, _contentLength(0)
, _sendContentLength(true)
, _chunked(false)
, _headLength(0)
, _sentLength(0)
, _ackedLength(0)
@@ -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 outLen;
size_t readLen = 0;
if(_chunked || !_sendContentLength){
outLen = space;
} else {
size_t remaining = _contentLength - _sentLength;
size_t outLen = (remaining > space)?space:remaining;
outLen = (remaining > space)?space:remaining;
}
uint8_t *buf = (uint8_t *)malloc(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);
}

View File

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