mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-12 01:53:07 +03:00
Stream::send() (#6979)
This commit is contained in:
@ -25,9 +25,22 @@
|
||||
|
||||
#include "ESP8266HTTPClient.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <StreamString.h>
|
||||
#include <StreamDev.h>
|
||||
#include <base64.h>
|
||||
|
||||
static int StreamReportToHttpClientReport (Stream::Report streamSendError)
|
||||
{
|
||||
switch (streamSendError)
|
||||
{
|
||||
case Stream::Report::TimedOut: return HTTPC_ERROR_READ_TIMEOUT;
|
||||
case Stream::Report::ReadError: return HTTPC_ERROR_NO_STREAM;
|
||||
case Stream::Report::WriteError: return HTTPC_ERROR_STREAM_WRITE;
|
||||
case Stream::Report::ShortOperation: return HTTPC_ERROR_STREAM_WRITE;
|
||||
case Stream::Report::Success: return 0;
|
||||
}
|
||||
return 0; // never reached, keep gcc quiet
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
@ -429,24 +442,9 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s
|
||||
return returnError(HTTPC_ERROR_SEND_HEADER_FAILED);
|
||||
}
|
||||
|
||||
// send Payload if needed
|
||||
if (payload && size > 0) {
|
||||
size_t bytesWritten = 0;
|
||||
const uint8_t *p = payload;
|
||||
size_t originalSize = size;
|
||||
while (bytesWritten < originalSize) {
|
||||
int written;
|
||||
int towrite = std::min((int)size, (int)HTTP_TCP_BUFFER_SIZE);
|
||||
written = _client->write(p + bytesWritten, towrite);
|
||||
if (written < 0) {
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
} else if (written == 0) {
|
||||
return returnError(HTTPC_ERROR_CONNECTION_LOST);
|
||||
}
|
||||
bytesWritten += written;
|
||||
size -= written;
|
||||
}
|
||||
}
|
||||
// transfer all of it, with send-timeout
|
||||
if (size && StreamConstPtr(payload, size).sendAll(_client) != size)
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
|
||||
// handle Server Response (Header)
|
||||
code = handleHeaderResponse();
|
||||
@ -545,111 +543,12 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
|
||||
return returnError(HTTPC_ERROR_SEND_HEADER_FAILED);
|
||||
}
|
||||
|
||||
int buff_size = HTTP_TCP_BUFFER_SIZE;
|
||||
|
||||
int len = size;
|
||||
int bytesWritten = 0;
|
||||
|
||||
if(len == 0) {
|
||||
len = -1;
|
||||
}
|
||||
|
||||
// if possible create smaller buffer then HTTP_TCP_BUFFER_SIZE
|
||||
if((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) {
|
||||
buff_size = len;
|
||||
}
|
||||
|
||||
// create buffer for read
|
||||
uint8_t * buff = (uint8_t *) malloc(buff_size);
|
||||
|
||||
if(buff) {
|
||||
// read all data from stream and send it to server
|
||||
while(connected() && (stream->available() > 0) && (len > 0 || len == -1)) {
|
||||
|
||||
// get available data size
|
||||
int sizeAvailable = stream->available();
|
||||
|
||||
if(sizeAvailable) {
|
||||
|
||||
int readBytes = sizeAvailable;
|
||||
|
||||
// read only the asked bytes
|
||||
if(len > 0 && readBytes > len) {
|
||||
readBytes = len;
|
||||
}
|
||||
|
||||
// not read more the buffer can handle
|
||||
if(readBytes > buff_size) {
|
||||
readBytes = buff_size;
|
||||
}
|
||||
|
||||
// read data
|
||||
int bytesRead = stream->readBytes(buff, readBytes);
|
||||
|
||||
// write it to Stream
|
||||
int bytesWrite = _client->write((const uint8_t *) buff, bytesRead);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
// are all Bytes a writen to stream ?
|
||||
if(bytesWrite != bytesRead) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d retry...\n", bytesRead, bytesWrite);
|
||||
|
||||
// check for write error
|
||||
if(_client->getWriteError()) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError());
|
||||
|
||||
//reset write error for retry
|
||||
_client->clearWriteError();
|
||||
}
|
||||
|
||||
// some time for the stream
|
||||
delay(1);
|
||||
|
||||
int leftBytes = (readBytes - bytesWrite);
|
||||
|
||||
// retry to send the missed bytes
|
||||
bytesWrite = _client->write((const uint8_t *) (buff + bytesWrite), leftBytes);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
if(bytesWrite != leftBytes) {
|
||||
// failed again
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d failed.\n", leftBytes, bytesWrite);
|
||||
free(buff);
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
// check for write error
|
||||
if(_client->getWriteError()) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError());
|
||||
free(buff);
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
|
||||
// count bytes to read left
|
||||
if(len > 0) {
|
||||
len -= readBytes;
|
||||
}
|
||||
|
||||
delay(0);
|
||||
} else {
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
free(buff);
|
||||
|
||||
if(size && (int) size != bytesWritten) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %zd mismatch!.\n", bytesWritten, size);
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!");
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
} else {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload written: %d\n", bytesWritten);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE);
|
||||
return returnError(HTTPC_ERROR_TOO_LESS_RAM);
|
||||
// transfer all of it, with timeout
|
||||
size_t transferred = stream->sendSize(_client, size);
|
||||
if (transferred != size)
|
||||
{
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d failed.\n", size, transferred);
|
||||
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
|
||||
}
|
||||
|
||||
// handle Server Response (Header)
|
||||
@ -725,13 +624,13 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
int ret = 0;
|
||||
|
||||
if(_transferEncoding == HTTPC_TE_IDENTITY) {
|
||||
if(len > 0 || len == -1) {
|
||||
ret = writeToStreamDataBlock(stream, len);
|
||||
// len < 0: transfer all of it, with timeout
|
||||
// len >= 0: max:len, with timeout
|
||||
ret = _client->sendSize(stream, len);
|
||||
|
||||
// have we an error?
|
||||
if(ret < 0) {
|
||||
return returnError(ret);
|
||||
}
|
||||
// do we have an error?
|
||||
if(_client->getLastSendReport() != Stream::Report::Success) {
|
||||
return returnError(StreamReportToHttpClientReport(_client->getLastSendReport()));
|
||||
}
|
||||
} else if(_transferEncoding == HTTPC_TE_CHUNKED) {
|
||||
int size = 0;
|
||||
@ -754,11 +653,11 @@ int HTTPClient::writeToStream(Stream * stream)
|
||||
|
||||
// data left?
|
||||
if(len > 0) {
|
||||
int r = writeToStreamDataBlock(stream, len);
|
||||
if(r < 0) {
|
||||
// error in writeToStreamDataBlock
|
||||
return returnError(r);
|
||||
}
|
||||
// read len bytes with timeout
|
||||
int r = _client->sendSize(stream, len);
|
||||
if (_client->getLastSendReport() != Stream::Report::Success)
|
||||
// not all data transferred
|
||||
return returnError(StreamReportToHttpClientReport(_client->getLastSendReport()));
|
||||
ret += r;
|
||||
} else {
|
||||
|
||||
@ -948,9 +847,7 @@ bool HTTPClient::connect(void)
|
||||
{
|
||||
if(_reuse && _canReuse && connected()) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client] connect: already connected, reusing connection\n");
|
||||
while(_client->available() > 0) {
|
||||
_client->read();
|
||||
}
|
||||
_client->sendAvailable(devnull); // clear _client's output (all of it, no timeout)
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1032,7 +929,8 @@ bool HTTPClient::sendHeader(const char * type)
|
||||
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client] sending request header\n-----\n%s-----\n", header.c_str());
|
||||
|
||||
return (_client->write((const uint8_t *) header.c_str(), header.length()) == header.length());
|
||||
// transfer all of it, with timeout
|
||||
return StreamConstPtr(header).sendAll(_client) == header.length();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1150,116 +1048,6 @@ int HTTPClient::handleHeaderResponse()
|
||||
return HTTPC_ERROR_CONNECTION_LOST;
|
||||
}
|
||||
|
||||
/**
|
||||
* write one Data Block to Stream
|
||||
* @param stream Stream *
|
||||
* @param size int
|
||||
* @return < 0 = error >= 0 = size written
|
||||
*/
|
||||
int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
|
||||
{
|
||||
int buff_size = HTTP_TCP_BUFFER_SIZE;
|
||||
int len = size; // left size to read
|
||||
int bytesWritten = 0;
|
||||
|
||||
// if possible create smaller buffer then HTTP_TCP_BUFFER_SIZE
|
||||
if((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) {
|
||||
buff_size = len;
|
||||
}
|
||||
|
||||
// create buffer for read
|
||||
uint8_t * buff = (uint8_t *) malloc(buff_size);
|
||||
|
||||
if(!buff) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE);
|
||||
return HTTPC_ERROR_TOO_LESS_RAM;
|
||||
}
|
||||
|
||||
// read all data from server
|
||||
while(connected() && (len > 0 || len == -1))
|
||||
{
|
||||
int readBytes = len;
|
||||
|
||||
// not read more the buffer can handle
|
||||
if(readBytes > buff_size) {
|
||||
readBytes = buff_size;
|
||||
}
|
||||
|
||||
// len == -1 or len > what is available, read only what is available
|
||||
int av = _client->available();
|
||||
if (readBytes < 0 || readBytes > av) {
|
||||
readBytes = av;
|
||||
}
|
||||
|
||||
// read data
|
||||
int bytesRead = _client->readBytes(buff, readBytes);
|
||||
if (!bytesRead)
|
||||
{
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] input stream timeout\n");
|
||||
free(buff);
|
||||
return HTTPC_ERROR_READ_TIMEOUT;
|
||||
}
|
||||
|
||||
// write it to Stream
|
||||
int bytesWrite = stream->write(buff, bytesRead);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
// are all Bytes a writen to stream ?
|
||||
if(bytesWrite != bytesRead) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d retry...\n", bytesRead, bytesWrite);
|
||||
|
||||
// check for write error
|
||||
if(stream->getWriteError()) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError());
|
||||
|
||||
//reset write error for retry
|
||||
stream->clearWriteError();
|
||||
}
|
||||
|
||||
// some time for the stream
|
||||
delay(1);
|
||||
|
||||
int leftBytes = (bytesRead - bytesWrite);
|
||||
|
||||
// retry to send the missed bytes
|
||||
bytesWrite = stream->write((buff + bytesWrite), leftBytes);
|
||||
bytesWritten += bytesWrite;
|
||||
|
||||
if(bytesWrite != leftBytes) {
|
||||
// failed again
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d failed.\n", leftBytes, bytesWrite);
|
||||
free(buff);
|
||||
return HTTPC_ERROR_STREAM_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
// check for write error
|
||||
if(stream->getWriteError()) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError());
|
||||
free(buff);
|
||||
return HTTPC_ERROR_STREAM_WRITE;
|
||||
}
|
||||
|
||||
// count bytes to read left
|
||||
if(len > 0) {
|
||||
len -= bytesRead;
|
||||
}
|
||||
|
||||
delay(0);
|
||||
}
|
||||
|
||||
free(buff);
|
||||
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] end of chunk or data (transferred: %d).\n", bytesWritten);
|
||||
|
||||
if((size > 0) && (size != bytesWritten)) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] transferred size %d and request size %d mismatch!.\n", bytesWritten, size);
|
||||
return HTTPC_ERROR_STREAM_WRITE;
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* called to handle error return, may disconnect the connection if still exists
|
||||
* @param error
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <StreamString.h>
|
||||
#include <WiFiClient.h>
|
||||
|
||||
#ifdef DEBUG_ESP_HTTP_CLIENT
|
||||
@ -148,8 +148,6 @@ typedef enum {
|
||||
class TransportTraits;
|
||||
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
|
||||
|
||||
class StreamString;
|
||||
|
||||
class HTTPClient
|
||||
{
|
||||
public:
|
||||
|
Reference in New Issue
Block a user