Prusa MINI Firmware overview
lwsapi_app.hpp
Go to the documentation of this file.
1 //! LwIP WSAPI C/C++ implementation.
2 /** This is C/C++ implementation of Lua's WSAPI or prospal related to WSGI 2.0,
3  for LwIP stack, which is base on tcp callbacks. The interface is like
4  Lua's WSAPI, but extend to context pointers, while C don't have coroutines,
5  and LwIP could work on more systems, so context switching could not be
6  good idea.
7 
8  +--------+ +----------------------------------+ |
9  |Thread X| | LwIP Thread | |
10  | | | +-------------------------------+| |
11  | | | |LwIP || |
12  | | | |+-------------------+ +-----+ || | +-------------+
13  | | | || WSAPI HTTP Server <--> Eth <--------> HTTP Client |
14  | | | |+----------^--------+ +-----+ || | +-------------+
15  | | | +-----------|-------------------+| |
16  |+------+| | +-----------v------------+ | |
17  || Data <--------> WSAPI HTTP Application | | |
18  |+------+| | +------------------------+ | |
19  +--------+ +----------------------------------+ |
20 */
21 
22 #pragma once
23 
24 #include <memory>
25 #include <cstring>
26 
27 #include "lwip/pbuf.h"
28 #include "lwip/mem.h"
29 
30 #include "dbg.h"
31 
32 //! maximum of HTTP request without payload
33 #define MAX_HTTP_REQUEST 1024
34 
35 //! maximum method length (PROPPATCH) from WebDAV + \0
36 #define METHOD_LENGTH 10
37 
38 //! maximum length of request uri
39 #define URI_LENGTH 64
40 
41 #define lwsapi_dbg _dbg
42 #define lwsapi_error _dbg
43 
44 #define lwsapi_free(arg) \
45  if (arg != nullptr) { \
46  mem_free(arg); \
47  arg = nullptr; \
48  }
49 
50 /*!
51  All classes, which could be dynamical initialized in LwIP thread, must
52  do internal memory pool, or another thread safe allocation. This class
53  have own new/delete operators, which create object instance in LwIP memory
54  pool.
55 */
56 class LwIPClass {
57 public:
58  static void *operator new(size_t size) {
59  return mem_malloc(size);
60  }
61 
62  static void operator delete(void *ptr) {
63  return mem_free(ptr);
64  }
65 
66  virtual ~LwIPClass() {}
67 };
68 
69 //! Headers list. Creator is responsible to clean the values.
70 class IHeader : public LwIPClass {
71 public:
72  const char *key; /**< key must be const char defined in code */
74 
75  IHeader(const char *const key, IHeader *next = nullptr)
76  : key(key)
77  , next(next) {}
78 
79  virtual size_t length() const = 0;
80  virtual void snprintf(char *buff) const = 0;
81  virtual void dbg() const = 0;
82 
83 protected:
84  static const size_t format_chars = 5; // ': \r\n\0'
85 };
86 
87 //! NumberHeader is for number headers like Content-Length
88 class NumberHeader : public IHeader {
89 public:
90  size_t value;
91 
92  NumberHeader(const char *const key, size_t value, IHeader *next = nullptr)
93  : IHeader(key, next)
94  , value(value) {}
95 
96  virtual size_t length() const override {
97  size_t count = 1;
98  size_t tmp = value;
99  while (tmp > 9) {
100  count++;
101  tmp = tmp / 10;
102  }
103 
104  return count + strlen(key) + format_chars;
105  }
106  virtual void snprintf(char *buff) const override {
107  ::snprintf(buff, length(), "%s: %zu\r\n", key, value);
108  }
109 
110  virtual void dbg() const override {
111  lwsapi_dbg("Header: [%s]:'%zu'", key, value);
112  }
113 };
114 
115 //! ConstHeader only point to const chars defined in code
116 class ConstHeader : public IHeader {
117 public:
118  const char *value;
119 
120  ConstHeader(const char *const key, const char *const value,
121  IHeader *next = nullptr)
122  : IHeader(key, next)
123  , value(value) {}
124 
125  virtual size_t length() const override {
126  return strlen(key) + strlen(value) + format_chars;
127  }
128 
129  virtual void snprintf(char *buff) const override {
130  ::snprintf(buff, length(), "%s: %s\r\n", key, value);
131  }
132 
133  virtual void dbg() const override {
134  lwsapi_dbg("Header: [%s]:'%s'", key, value);
135  }
136 };
137 
138 //! DynamicsHeader store it's value to LwIP memory pool
139 class DynamicsHeader : public IHeader {
140 public:
141  char *value;
142 
143  DynamicsHeader(const char *const key, const char *value,
144  IHeader *next = nullptr)
145  : IHeader(key, next) {
146  this->value = reinterpret_cast<char *>(
147  mem_calloc(sizeof(char), strlen(value) + 1));
148  if (this->value != nullptr) {
149  memcpy(this->value, value, strlen(value));
150  }
151  }
152 
153  DynamicsHeader(const char *const key, const char *value, size_t length,
154  IHeader *next = nullptr)
155  : IHeader(key, next) {
156  this->value = reinterpret_cast<char *>(
157  mem_calloc(sizeof(char), length + 1));
158  if (this->value != nullptr) {
159  memcpy(this->value, value, length);
160  }
161  }
162 
165  }
166 
167  virtual size_t length() const override {
168  return strlen(key) + strlen(value) + format_chars;
169  }
170 
171  virtual void snprintf(char *buff) const override {
172  ::snprintf(buff, length(), "%s: %s\r\n", key, value);
173  }
174 
175  virtual void dbg() const override {
176  lwsapi_dbg("Header: [%s]:'%s'", key, value);
177  }
178 };
179 
180 //! Environment struct like as WSGI environment as possible could be.
181 class Environment {
182 public:
183  char method[METHOD_LENGTH] = { '\0' }; /**< HTTP METHOD (GET|POST|etc..) */
184  char request_uri[URI_LENGTH] = { '\0' }; /**< Full HTTP request uri */
185 
187  : headers(nullptr)
188  , last(nullptr) {}
189 
191  auto it = headers;
192  IHeader *next = nullptr;
193  while (it != nullptr) {
194  next = it->next;
195  delete it;
196  it = next;
197  }
198  }
199 
200  void add_header(IHeader *header);
201  const IHeader *get_headers() const {
202  return headers;
203  }
204 
205 private:
206  IHeader *headers; /**< List of request headers */
207  IHeader *last;
208 };
209 
210 //! Message which must be returned from coroutine generator.
211 struct Message_t {
212  const char *response; /**< 200 OK etc.*/
213  const IHeader *headers; /**< response header */
214  const uint8_t *payload;
215  int length; /**< payload length */
216 };
217 
218 class IResponse : public LwIPClass {
219 public:
220  IResponse(const IResponse &) = delete;
222 
223  //! WSAPI generator (called more time from WSAPI http server).
224  /*!
225  This is generator method must iterative return response content.
226 
227  @param input is LwIP input buffer chain.
228 
229  @return Message contains response, headers, payload and length. If
230  length is EOF, all data was sent. Response must be set in first
231  time, headers second time and payload could be send moretimes. When
232  response or headers in message exists, that will be send. All data
233  in message must exists to next generator call!
234  */
235  virtual Message_t generator(const struct pbuf *input = nullptr) = 0;
236 
237  typedef std::unique_ptr<IResponse> unique_ptr_t;
238 };
239 
240 //! application_fn typedef, which is called in tcp_recv callback.
241 /*!
242  This is "WSGI" application handler, which gets reference to Environment
243  structure. This must return IResponse::unique_ptr_t, which must implements
244  generator method to return response content iterative if is needed.
245 */
247 
248 //! Define of application functions
250 
251 typedef IHeader *(*header_factory_fn)(const char *key,
252  const char *value, size_t value_length);
253 
254 //! Return new ConstHeader
255 IHeader *const_header_factory(const char *key,
256  const char *value, size_t value_length);
257 
258 //! Return new DynamicsHeader
259 IHeader *dynamics_header_factory(const char *key,
260  const char *value, size_t value_length);
261 
262 //! Response new NumberHeader
263 IHeader *number_header_factory(const char *key,
264  const char *value, size_t value_length);
265 
266 //! This factory is used for parsing input headers
267 /*!
268  request_header_fn is called when each header was detected in request.
269  Application part of this http server must implement this function to
270  decide which header is needed, and which not. All other headers will be
271  ignored.
272 
273  @return Right header object or nullptr when header could be ignore
274 */
275 typedef IHeader *(request_header_fn)(const char *key, size_t key_length,
276  const char *value, size_t value_length);
277 
278 IHeader *request_header(const char *key, size_t key_length,
279  const char *value, size_t value_length);
ConstHeader
ConstHeader only point to const chars defined in code.
Definition: lwsapi_app.hpp:116
NumberHeader
NumberHeader is for number headers like Content-Length.
Definition: lwsapi_app.hpp:88
Message_t::response
const char * response
Definition: lwsapi_app.hpp:212
DynamicsHeader::dbg
virtual void dbg() const override
Definition: lwsapi_app.hpp:175
NumberHeader::snprintf
virtual void snprintf(char *buff) const override
Definition: lwsapi_app.hpp:106
LwIPClass::~LwIPClass
virtual ~LwIPClass()
Definition: lwsapi_app.hpp:66
IResponse::generator
virtual Message_t generator(const struct pbuf *input=nullptr)=0
WSAPI generator (called more time from WSAPI http server).
pbuf.h
ConstHeader::value
const char * value
Definition: lwsapi_app.hpp:118
DynamicsHeader::value
char * value
Definition: lwsapi_app.hpp:141
DynamicsHeader::length
virtual size_t length() const override
Definition: lwsapi_app.hpp:167
IHeader
Headers list. Creator is responsible to clean the values.
Definition: lwsapi_app.hpp:70
IHeader::key
const char * key
Definition: lwsapi_app.hpp:72
Environment::request_uri
char request_uri[URI_LENGTH]
Definition: lwsapi_app.hpp:184
ConstHeader::ConstHeader
ConstHeader(const char *const key, const char *const value, IHeader *next=nullptr)
Definition: lwsapi_app.hpp:120
METHOD_LENGTH
#define METHOD_LENGTH
maximum method length (PROPPATCH) from WebDAV + \0
Definition: lwsapi_app.hpp:36
request_header
IHeader * request_header(const char *key, size_t key_length, const char *value, size_t value_length)
request_header_fn callbacke, which is call from LwIP WSAPI http server
Definition: connect.cpp:289
ConstHeader::length
virtual size_t length() const override
Definition: lwsapi_app.hpp:125
dbg.h
Environment::Environment
Environment()
Definition: lwsapi_app.hpp:186
mem_calloc
void * mem_calloc(mem_size_t count, mem_size_t size)
Definition: mem.c:765
const_header_factory
IHeader * const_header_factory(const char *key, const char *value, size_t value_length)
Return new ConstHeader.
IResponse::IResponse
IResponse()
Definition: lwsapi_app.hpp:221
NumberHeader::value
size_t value
Definition: lwsapi_app.hpp:90
IHeader::snprintf
virtual void snprintf(char *buff) const =0
IHeader::dbg
virtual void dbg() const =0
application_fn
IResponse::unique_ptr_t() application_fn(Environment &env)
application_fn typedef, which is called in tcp_recv callback.
Definition: lwsapi_app.hpp:246
DynamicsHeader::snprintf
virtual void snprintf(char *buff) const override
Definition: lwsapi_app.hpp:171
DynamicsHeader::~DynamicsHeader
~DynamicsHeader()
Definition: lwsapi_app.hpp:163
lwsapi_dbg
#define lwsapi_dbg
Definition: lwsapi_app.hpp:41
Environment::get_headers
const IHeader * get_headers() const
Definition: lwsapi_app.hpp:201
IResponse
Definition: lwsapi_app.hpp:218
Message_t::length
int length
Definition: lwsapi_app.hpp:215
NumberHeader::length
virtual size_t length() const override
Definition: lwsapi_app.hpp:96
dynamics_header_factory
IHeader * dynamics_header_factory(const char *key, const char *value, size_t value_length)
Return new DynamicsHeader.
Definition: lwsapi.cpp:440
LwIPClass
Definition: lwsapi_app.hpp:56
create_custom_upload_command_CDC.env
env
Definition: create_custom_upload_command_CDC.py:23
Environment
Environment struct like as WSGI environment as possible could be.
Definition: lwsapi_app.hpp:181
request_header_fn
IHeader *() request_header_fn(const char *key, size_t key_length, const char *value, size_t value_length)
This factory is used for parsing input headers.
Definition: lwsapi_app.hpp:275
uint8_t
const uint8_t[]
Definition: 404_html.c:3
IHeader::next
IHeader * next
Definition: lwsapi_app.hpp:73
IResponse::unique_ptr_t
std::unique_ptr< IResponse > unique_ptr_t
Definition: lwsapi_app.hpp:237
Message_t
Message which must be returned from coroutine generator.
Definition: lwsapi_app.hpp:211
IHeader::length
virtual size_t length() const =0
application
IResponse::unique_ptr_t application(Environment &env)
Define of application functions.
Definition: connect.cpp:306
lwsapi_free
#define lwsapi_free(arg)
Definition: lwsapi_app.hpp:44
URI_LENGTH
#define URI_LENGTH
maximum length of request uri
Definition: lwsapi_app.hpp:39
IHeader::IHeader
IHeader(const char *const key, IHeader *next=nullptr)
Definition: lwsapi_app.hpp:75
Message_t::payload
const uint8_t * payload
Definition: lwsapi_app.hpp:214
mem_malloc
void * mem_malloc(mem_size_t size)
Definition: mem.c:603
ConstHeader::dbg
virtual void dbg() const override
Definition: lwsapi_app.hpp:133
ConstHeader::snprintf
virtual void snprintf(char *buff) const override
Definition: lwsapi_app.hpp:129
DynamicsHeader::DynamicsHeader
DynamicsHeader(const char *const key, const char *value, IHeader *next=nullptr)
Definition: lwsapi_app.hpp:143
DynamicsHeader
DynamicsHeader store it's value to LwIP memory pool.
Definition: lwsapi_app.hpp:139
NumberHeader::NumberHeader
NumberHeader(const char *const key, size_t value, IHeader *next=nullptr)
Definition: lwsapi_app.hpp:92
Environment::add_header
void add_header(IHeader *header)
Definition: lwsapi.cpp:28
mem.h
mem_free
void mem_free(void *rmem)
Definition: mem.c:419
number_header_factory
IHeader * number_header_factory(const char *key, const char *value, size_t value_length)
Response new NumberHeader.
Definition: lwsapi.cpp:445
IHeader::format_chars
static const size_t format_chars
Definition: lwsapi_app.hpp:84
Environment::~Environment
~Environment()
Definition: lwsapi_app.hpp:190
pbuf
Definition: pbuf.h:142
Environment::method
char method[METHOD_LENGTH]
Definition: lwsapi_app.hpp:183
Message_t::headers
const IHeader * headers
Definition: lwsapi_app.hpp:213
size
static png_bytep size_t size
Definition: pngwrite.c:2170
DynamicsHeader::DynamicsHeader
DynamicsHeader(const char *const key, const char *value, size_t length, IHeader *next=nullptr)
Definition: lwsapi_app.hpp:153
NumberHeader::dbg
virtual void dbg() const override
Definition: lwsapi_app.hpp:110