Application

The Application class is used for mounting Middleware to Routers and to process the incoming HTTP requests.

Example
#include <WiFi.h>
#include <aWOT.h>

WiFiServer server(80);
Application app;

void index(Request &req, Response &res) {
  res.print("Hello Wolrd!");
}

void setup() {
  Serial.begin(115200);

  WiFi.begin("ssid", "password");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(WiFi.localIP());

  app.get("/", &index);
  server.begin();
}

void loop() {
  WiFiClient client = server.available();

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

Methods

void all(const char* path, Router::Middleware* middleware);

This method is like any standard app.METHOD() methods, except it matches all HTTP verbs.

The app.all() method is useful for mapping “global” logic for specific path paths.

For more information, see the routing guide.

Example
void noDelete(Request &req, Response &res) {
  if (req.method() == Request::DELETE) {
    res.sendStatus(405);
  }
}

app.all("/nodelete", &noDelete);

void del(const char* path, Router::Middleware* middleware);

Routes HTTP DELETE requests to the specified path with the specified Middleware function.

For more information, see the routing guide.

Example
void deleteResource(Request &req, Response &res) {
  res.sendStatus(204);
}

app.del("/delete", &deleteResource);

void get(const char* path, Router::Middleware* middleware);

Routes HTTP GET requests to the specified path with the specified Middleware function.

For more information, see the routing guide.

Example
void index(Request &req, Response &res) {
  res.print("Hello World!");
}

app.get("/index.html", &noDelete);

void header(const char* name, char* buffer, int bufferLength);

When request gets to the first middleware all headers have been already processed. To make header lookup available inside a Middleware with the Request::get method we need to specify the headers we are interested in advance.

The header name matching is case-insensitive and the buffer needs to have space for null termination. If the header value does not fit the buffer the server will automatically send a 431 Request Header Fields Too Large response.

Example
char contentType[100];

void headers(Request &req, Response &res) {
  char * type = req.get("Content-Type");
}

app.header("Content-Type", contentType, 100);
app.use(&headers);

void options(const char* path, Router::Middleware* middleware);

Routes HTTP OPTIONS requests to the specified path with the specified Middleware function.

For more information, see the routing guide.

Example
void cors(Request &req, Response &res) {
  res.set("Access-Control-Allow-Origin", "*");
  res.set("Access-Control-Allow-Methods", "GET, HEAD");
  res.sendStatus(204);
}

app.options("/js/app.js", &cors);

void patch(const char* path, Router::Middleware* middleware);

Routes HTTP PATCH requests to the specified path with the specified Middleware function.

For more information, see the routing guide.

void post(const char* path, Router::Middleware* middleware);

Routes HTTP POST requests to the specified path with the specified Middleware function.

For more information, see the routing guide.

Example
void form(Request &req, Response &res) {
  char name[10];
  char value[10];

  while (req.left()) {
    if (!req.form(name, 10, value, 10)) {
      return res.sendStatus(400);
    }

    res.print(name);
    res.print(":");
    res.println(value);
  }
}

app.post("/form", &form);

void process(Client *client);
void process(Client *client, char* buffer, int bufferLength);

The method processes a connected client. Call this method in yout main loop when ever you have connected client available.

The Client class is an Arduino defined interface so we can process requests from multiple different Client implementations such as EthernetClient or WifiClient. For debugging purposes it is event possible to wrap the serial port to a Client class.

The basic of version of process uses the internal buffer for storing the url of the request. The max length is defined in SERVER_URL_BUFFER_SIZE. The second overloaded version works exactly the same way but the buffer for storing the url of the HTTP request is given as a argument.

Example
void loop() {
  WiFiClient client = server.available();

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

void put(const char* path, Router::Middleware* middleware);

Routes HTTP PUT requests to the specified path with the specified Middleware function.

For more information, see the routing guide.

void route(Router * router);

Mounts a Router instance to the application.

Example
Application app;
Router cats("/cats");

void looooong(Request &req, Response &res) {
  res.print("looooong cat is long!");
}

void ceiling(Request &req, Response &res) {
  res.print("ceiling cat is watching you debug!");
}

void nyannyan(Request &req, Response &res) {
  for (int i = 0; i < 100; i++) {
      res.print("nyan ");
  }
}

cats.get("/long", &looooong);
cats.get("/ceiling", &ceiling);
cats.get("/nyan", &nyannyan);

app.route(&cats);

void use(Router::Middleware* middleware);

This method is like any standard app.METHOD() methods, except it matches all HTTP verbs and routes.

Request

The Request class represents the HTTP request and has methods for the request query string, parameters, body, HTTP headers, and so on.

Types

enum MethodType {GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS, ALL, USE}

The incoming HTTP request method type. HEAD requests are converted to GET requests but only the headers are sent to the client. The method types ALL and USE are used when defining middleware handlers to a Router.

Methods

int available();

Returns the number of bytes available for reading. That is, the amount of data that has been written to the client by the server it is connected to. It is not the expected length of the request body defined in the Content-Length header. The function inherits from the Stream utility class.

bool body(uint8_t *buffer, int bufferLength);

Reads the request body to a buffer. The buffer must have space for null termination. The function returns true if was able to read the the amount of data specified in the Content-Length headers. If the client timed out or the body did not fit the buffer completely it returns false.

Example
byte buffer[100];

if(!req.body(buffer, 100)){
  return res.sendStatus(400);
}

int bytesRead();

Returns the number of bytes read so far from the incoming request. This includes the full HTTP request with header. Not just the bytes read from the body.

Client * client();

Returns a pointer to the raw tcp client. You should not normally need to use this. You need to know what you are doing. Reading from the client will not add to the the result of bytesRead() and you need to take care of request timeouts.

bool form(char *name, int nameLength, char *value, int valueLength);

Reads the next key value pair from the request body. The function expects the request content type to be as application/x-www-form-urlencoded.

Returns true if the there was a value in the body. Returns false if there is no name value pair to read or the content were too long for the buffers.

Example
char name[10];
char value[100];

if(!req.form(name, 10, value, 100)){
  return res.sendStatus(400);
}

char * get(const char *name);

Returns a header value by name. Only header values marked for reading with Application::header() will be available. The matching is case insensitive.

Example
char contentType[100];
void headers(Request &req, Response &res) {
  char * type = req.get("Content-Type");
  // => "text/plain"
}

app.header("Content-Type", contentType, 100);
app.use(&headers);

int left();

Returns the number bytes left to read in the request body. The initial value is taken from the Content-Length header.

Example
while(req.left() && !req.timeout()){
  res.write(req.read());
}

MethodType method();

Returns the MethodType type of the incoming http request.

Example
void noDelete(Request &req, Response &res) {
  if (req.method() == Request::DELETE) {
    res.sendStatus(405);
  }
}

app.all("/nodelete", &noDelete);

char * path();

Returns a null terminated string contains the path part of the request URL.

Example
// example.com/users?sort=desc
req.path()
// => "/users"

When called from a middleware, the mount point is included in req.path.

int peek();

Returns the next byte of incoming data without removing it from the internal buffer. That is, successive calls to peek() will return the same character, as will the next call to read(). peek() inherits from the Stream class.

void push(uint8_t ch);

Puts a byte back to the read buffer. Undo of read(). If the internal buffer gets full the last byte is replaced over and over again. The internal buffer size is defined in the SERVER_PUSHBACK_BUFFER_SIZE value.

char * query();
bool query(const char *name, char *buffer, int bufferLength);

Without any parameters the method returns a null terminated string containing the query part of the request URL. The overloaded version reads the request parameter defined by the name to the buffer. The buffer must have space for null termination. Returns true if the parameter was found and the buffer had enough space for the value. Returns false if the parameter is not found or the value was too long for the buffer.

Example
/cats?type=lolcat
req.query();
// => "type=lolcat"

char type[64];
req.query("type", type, 64);
// => "lolcat"

int read();

Reads a byte from the incoming stream. Returns -1 if the there is no data available during the the time specified in the SERVER_READ_TIMEOUT_MS setting.

Example
// HTTP GET /cats/lolcat
void routeParams(Request &req, Response &res) {
  char catId[64];
  req.route("catId", catId, 64);

  res.success("text/plain");
  res.print(catId);
}

void setup() {
  // other setup

  app.get("/cats/:catId", &routeParams);
}

bool route(const char *name, char *buffer, int bufferLength);
bool route(int number, char *buffer, int bufferLength);

The firt version of the method reads the route parameter defined by name to the buffer. The buffer must have space for null termination. Returns true if the parameter was found and the buffer had enough space for the value. Returns false if the parameter is not found or the value was too long for the buffer.

The second overload works exaclty the same way but it uses an index number for the look up.

Example
// HTTP GET /cats/lolcat
void routeParams(Request &req, Response &res) {
  char catId[64];
  req.route("catId", catId, 64);
  // => "lolcat"
}

void setup() {
  app.get("/cats/:catId", &routeParams);
}

bool timeout();

Returns true if the previous call to read() was timed out. The time to wait for incoming data is defined in SERVER_READ_TIMEOUT_MS value.

Example
while(req.left() && !req.timeout()){
  res.write(req.read());
}

Response

The Response class represents the HTTP response that an Express app sends when it gets an HTTP request.

Methods

int bytesSent();

Returns the number of bytes written so far to the outgoing response. This includes the full HTTP request with headers. Not just the bytes written to the body.

void end();

Ends the response process. All further write operations will be discarded. This will also prevent any pending Middleware functions in the chain from being executed.

Example
void auth(Request &req, Response &res) {
  char * authHeader = req.get("Authorization");

  if (strcmp(authHeader, "Basic c3VwZXI6YWRtaW4=") != 0) { // super:admin in base64
    res.set("WWW-Authenticate", "Basic realm=\"Secret Area\"");
    res.sendStatus(401);
    res.end();
  }
}

bool ended();

Returns true if request has been ended.

void flush();

Flushes outgoing buffer and waits until all outgoing data in buffer have been sent. This is called internally before the client is disconnected at the end of the request.

const char * get(const char *name);

Returns the HTTP response header specified by name if it has been previously set. The match is case-insensitive.

Example
res.get('Content-Type');
// => "text/plain"

bool headersSent();

Returns true if the response headers have been already sent. After headers have been sent all writes will go to the request body. Calling any write method will cause headers to be sent automatically.

void printP(const char *string); void printP(const unsigned char *string);

Output a string stored in the program memory, usually defined with the P macro. Using this will save you from wasting your precious RAM for static strings.

Ther overloaded version is for handling signed char strings

void sendStatus(int code);

Send a HTTP full http response matching to the code.

Example
res.sendStatus(404);
 // is equivalent to 
res.status(404);
res.print(“Not Found”);

void set(const char* name, const char* value);

Sets the response’s HTTP header field to the value. Existing value will be overridden by the new value if the name matches. The match is case-insensitive. The max number of headers is defined in SERVER_MAX_HEADERS.

Example
res.set("Content-Type", "text/plain");

void status(int code);

Sends the initial status line of the HTTP the response. After calling the function new headers can be still set to the response before first write method is called.

Example
res.status(404);
res.print("Pagina no encontrada");

bool statusSent();

Return true if the status line of the HTTP response has been already send.

size_t write(uint8_t ch);
size_t write(uint8_t *buffer, size_t bufferLength);

The first version writes a single byte to the response. The function inherits from the Stream utility class. The other version writes a buffer to the response.

Returns the number of bytes written. The function inherits from the Stream utility class.

void writeP(const unsigned char *data, size_t length);

Output a buffer stored in the program memory.

Router

A Router is an isolated instance of middleware and routes. You can think of it as a “mini-application,” capable only of performing middleware and routing functions. Every aWOT application has a built-in default router.

Routers can’t be nested. Use it as an argument to app.route() method.

Example
Application app;
Router cats("/cats");

void looooong(Request &req, Response &res) {
  res.print("looooong cat is long!");
}

void ceiling(Request &req, Response &res) {
  res.print("ceiling cat is watching you debug!");
}

void nyannyan(Request &req, Response &res) {
  for (int i = 0; i < 100; i++) {
      res.print("nyan ");
  }
}

void setup() {
  // other setup

  cats.get("/long", &looooong);
  cats.get("/ceiling", &ceiling);
  cats.get("/nyan", &nyannyan);

  app.use(&cats);
}

Types

typedef void Middleware(Request& request, Response& response);

A type definition for the middleware handler functions.

Methods

Router(const char * urlPrefix = "");

Router constructor takes a parameter the prefix that will be used for routering requests. For example e router defined as Router router(“/api”) will try to find matching middleware when the request url begins with “/api”. Te prefix can be left empty to make the router middleware handlers mounted to the root path.

void all(const char* urlPattern, Middleware* middleware);

Routes a request to a Middleware when the request url matches url pattern regardless of the HTTP type.

For more information, see the routing guide.

void del(const char* urlPattern, Middleware* middleware);

Routes a DELETE request to a Middleware when the request url matches the url pattern.

For more information, see the routing guide.

void get(const char* urlPattern, Middleware* middleware);

Routes a GET request to a Middleware when the request url matches the url pattern.

For more information, see the routing guide.

void options(const char* urlPattern, Middleware* middleware);

Routes a OPTIONS request to a Middleware when the request url matches the url pattern.

For more information, see the routing guide.

void patch(const char* urlPattern, Middleware* middleware);

Routes a PATCH request to a Middleware when the request url matches the url pattern.

For more information, see the routing guide.

void post(const char* urlPattern, Middleware* middleware);

Routes a POST request to a Middleware when the request url matches the url pattern.

void put(const char* urlPattern, Middleware* middleware);

Routes a PUT request to a Middleware when the request url matches the url pattern.

For more information, see the routing guide.

void use(Middleware* middleware);

Makes the Middleware executed when the request URL beginning matches the prefix of the router.