Skip to content

Commit

Permalink
Merge pull request #95 from lasselukkari/buffered-reads
Browse files Browse the repository at this point in the history
Buffered reads
  • Loading branch information
lasselukkari authored Jan 23, 2021
2 parents 376d8f3 + dc08185 commit db94041
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 11 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ Arduino web server library.
* [Response](https://awot.net/en/2x/api.html#res)
* [Router](https://awot.net/en/2x/api.html#router)

## Compatibility

The aWOT web server library has been designed to work with all Arduino compatible development boards and networking options. This also means that switching the board or changing from WiFi to Ethernet will require minimal changes. The examples directory shows you how to use the library with the most popular Ethernet and WiFi libraries

However there are few subtle differences that need to be taken into account. Also unfortunately some of the WiFi and Ethernet libraries have bugs that prevent the library from working properly.

### ESP32 and ESP8266 WiFi

In both of the ESP Arduino cores the WiFiClient closes the connection automatically in the class destructor. This means that the client.stop(), does not need to be explicitly called but you will need to take extra steps if you want to keep the connection alive.

### ESP32 + Wiznet W5500
The current version of the ESP32 Arduino core uses a non standard version of the Server class. Until the ESP32 core is fixed you need to manually modify the begin function in the Server.h if you want to use the Ethernet library that is shipped with the core.

Bug report: https://github.com/espressif/arduino-esp32/issues/2704

### WifiNina
The WifiNina firmware currently has a bug that causes large writes with the client to fail randomly. The fix is already implemented but it has not yet been merged to the master or released.

Bug report: https://github.com/arduino/nina-fw/issues/61

### Teensy 4.1 + Ethernet
The Teensy 4.1 Ethernet library currently has a bug that causes the connection to stall and reset when connections to the server are opened in fast phase. The bug has been verified but not fixed yet.

Bug report: https://github.com/vjmuzik/NativeEthernet/issues/7

### Arduino UNO
Because of the limited RAM and ROM Arduino UNO is on the edge of being usable for anything more complicated. If you want to use this library together with the SD card or any JSON parsing library, pay attention that you do not run out of memory.

## Examples
### Hello World
```cpp
Expand Down Expand Up @@ -52,7 +80,7 @@ void loop() {

if (client.connected()) {
app.process(&client);
// If you are using the EthernetClient remember to call client.stop() here.
client.stop();
}
}
```
Expand Down
1 change: 1 addition & 0 deletions examples/CaptivePortal/CaptivePortal.ino
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void loop() {

if (client.connected()) {
app.process(&client);
client.stop();
}

dnsServer.processNextRequest();
Expand Down
1 change: 1 addition & 0 deletions examples/CardFiles/CardFiles.ino
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,6 @@ void loop() {

if (client.connected()) {
app.process(&client);
client.stop();
}
}
1 change: 1 addition & 0 deletions examples/CustomNotFound/CustomNotFound.ino
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ void loop() {

if (client.connected()) {
app.process(&client);
client.stop();
}
}
1 change: 1 addition & 0 deletions examples/FileServer/FileServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@ void loop() {

if (client.connected()) {
app.process(&client);
client.stop();
}
}
1 change: 1 addition & 0 deletions examples/OTAUpdate/OTAUpdate.ino
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ void loop() {

if (client.connected()) {
app.process(&client);
client.stop();
}

if (shouldRestart) {
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=aWOT
version=3.1.3
version=3.2.0
author=Lasse Lukkari <lasse.lukkari@gmail.com>
maintainer=Lasse Lukkari <lasse.lukkari@gmail.com>
sentence=Arduino web server library.
Expand Down
42 changes: 38 additions & 4 deletions src/aWOT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ void Response::writeP(const unsigned char *data, size_t length) {
}
}

void Response::m_init(Stream *client) {
void Response::m_init(Client *client) {
m_stream = client;
m_contentLenghtSet = false;
m_contentTypeSet = false;
Expand Down Expand Up @@ -882,6 +882,30 @@ int Request::read() {
return ch;
}

int Request::read(uint8_t* buf, size_t size) {
int ret = 0;

while (m_pushbackDepth > 0) {
*buf++ = m_pushback[--m_pushbackDepth];
size--;
}

int read = m_stream->read(buf, (size < m_left ? size : m_left));
if (read == -1) {
if (ret > 0) {
return ret;
}

return -1;
}

ret += read;
m_bytesRead += read;
m_left -= read;

return ret;
}

bool Request::route(const char *name, char *buffer, int bufferLength) {
int part = 0;
int i = 1;
Expand Down Expand Up @@ -940,7 +964,7 @@ size_t Request::write(uint8_t* buffer, size_t bufferLength) {
return m_response->write(buffer, bufferLength);
}

void Request::m_init(Stream *client, Response *response, HeaderNode *headerTail, char *buffer,
void Request::m_init(Client *client, Response *response, HeaderNode *headerTail, char *buffer,
int bufferLength, unsigned long timeout) {
m_stream = client;
m_response = response;
Expand Down Expand Up @@ -1446,12 +1470,12 @@ void Application::put(const char *path, Router::Middleware *middleware) {
m_defaultRouter.m_addMiddleware(Request::PUT, path, middleware);
}

void Application::process(Stream *stream) {
void Application::process(Client *stream) {
char request[SERVER_URL_BUFFER_SIZE];
process(stream, request, SERVER_URL_BUFFER_SIZE);
}

void Application::process(Stream *stream, char *buffer, int bufferLength) {
void Application::process(Client *stream, char *buffer, int bufferLength) {
if (stream == NULL) {
return;
}
Expand All @@ -1465,6 +1489,16 @@ void Application::process(Stream *stream, char *buffer, int bufferLength) {
m_response.m_reset();
}

void Application::process(Stream *stream) {
StreamClient client(stream);
process(&client);
}

void Application::process(Stream *stream, char *buffer, int bufferLength) {
StreamClient client(stream);
process(&client, buffer, bufferLength);
}

void Application::use(const char *path, Router::Middleware *middleware) {
m_defaultRouter.m_addMiddleware(Request::USE, path, middleware);
}
Expand Down
47 changes: 42 additions & 5 deletions src/aWOT.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include <stdlib.h>
#include <string.h>

#include "Stream.h"
#include "Client.h"

#define CRLF "\r\n"

Expand Down Expand Up @@ -71,6 +71,40 @@
#define P(name) static const unsigned char name[] PROGMEM
#define SIZE(array) (sizeof(array) / sizeof(*array))

class StreamClient : public Client {
private:
Stream* s;

public:
StreamClient(Stream* stream) : s(stream){};
int connect(IPAddress ip, uint16_t port){return 1;};
int connect(const char* host, uint16_t port){return 1;};
size_t write(uint8_t byte){return s->write(byte);};
size_t write(const uint8_t* buffer, size_t length){return s->write(buffer, length);};
int available(){return s->available();};
int read() {return s->read();};
int read(uint8_t* buffer, size_t length) {
size_t count = 0;

while (count < length) {
int c = read();
if (c < 0) {
break;
}

*buffer++ = (uint8_t)c;
count++;
}

return count;
}
int peek(){return s->peek();};
void flush(){return s->flush();};
void stop(){};
uint8_t connected(){return 1;};
operator bool(){return true;};
};

class Response : public Print {
friend class Application;
friend class Router;
Expand Down Expand Up @@ -99,15 +133,15 @@ class Response : public Print {
private:
Response();

void m_init(Stream* client);
void m_init(Client* client);
void m_printStatus(int code);
bool m_shouldPrintHeaders();
void m_printHeaders();
void m_printCRLF();
void m_flushBuf();
void m_reset();

Stream* m_stream;
Client* m_stream;
struct Headers {
const char* name;
const char* value;
Expand Down Expand Up @@ -149,6 +183,7 @@ class Request : public Stream {
char* query();
bool query(const char* name, char* buffer, int bufferLength);
int read();
int read(uint8_t* buf, size_t size);
bool route(const char* name, char* buffer, int bufferLength);
bool route(int number, char* buffer, int bufferLength);
int minorVersion();
Expand All @@ -164,7 +199,7 @@ class Request : public Stream {
};

Request();
void m_init(Stream* client, Response* m_response, HeaderNode *headerTail, char* buffer, int bufferLength, unsigned long timeout);
void m_init(Client* client, Response* m_response, HeaderNode *headerTail, char* buffer, int bufferLength, unsigned long timeout);
bool m_processMethod();
bool m_readURL();
bool m_readVersion();
Expand All @@ -181,7 +216,7 @@ class Request : public Stream {
int m_timedRead();
bool m_timedout();

Stream* m_stream;
Client* m_stream;
Response* m_response;
MethodType m_method;
int m_minorVersion;
Expand Down Expand Up @@ -256,6 +291,8 @@ class Application {
void patch(const char* path, Router::Middleware* middleware);
void post(const char* path, Router::Middleware* middleware);
void put(const char* path, Router::Middleware* middleware);
void process(Client* client);
void process(Client* client, char* buffer, int bufferLength);
void process(Stream* client);
void process(Stream* client, char* buffer, int bufferLength);
void setTimeout(unsigned long timeoutMillis);
Expand Down
36 changes: 36 additions & 0 deletions test/buffered-read.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <ArduinoUnitTests.h>
#include "../src/aWOT.h"
#include "./mocks/MockStream.h"

void handler(Request & req, Response & res) {
uint8_t buffer[100] = {};

int read = req.read(buffer , 100);

res.write(buffer, read);
}

unittest(buffered_read) {
const char *request =
"POST / HTTP/1.0" CRLF
"Content-Length: 4" CRLF
CRLF
"body";

const char *expected =
"HTTP/1.1 200 OK" CRLF
"Content-Type: text/plain" CRLF
"Connection: close" CRLF
CRLF
"body";

MockStream stream(request);
Application app;

app.post("/", &handler);
app.process(&stream);

assertEqual(expected, stream.response());
}

unittest_main()

0 comments on commit db94041

Please sign in to comment.