-
Notifications
You must be signed in to change notification settings - Fork 4
/
iwn_wf.h
302 lines (252 loc) · 13.3 KB
/
iwn_wf.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#pragma once
/// High level HTTP web-framework.
#include "iwnet.h"
#include "iwn_http_server.h"
#include <iowow/iwlog.h>
#include <stdio.h>
IW_EXTERN_C_START;
/// Error codes specific to web-framework module.
typedef enum {
_WF_ERROR_START = (IW_ERROR_START + 205000UL),
WF_ERROR_INVALID_FORM_DATA, ///< Invalid (unparseable) form data (WF_ERROR_INVALID_FORM_DATA)
WF_ERROR_PARENT_ROUTE_FROM_DIFFERENT_CONTEXT,
/**< Parent route from different context
(WF_ERROR_PARENT_ROUTE_FROM_DIFFERENT_CONTEXT). */
WF_ERROR_REGEXP_INVALID, ///< Invalid regular expression (WF_ERROR_REGEXP_INVALID)
/**< Illegal instruction in compiled regular expression (please report this
bug) (WF_ERROR_REGEXP_ENGINE) */
WF_ERROR_UNSUPPORTED_HTTP_METHOD, ///< Unsupported HTTP method (WF_ERROR_UNSUPPORTED_HTTP_METHOD)
WF_ERROR_MAX_NESTED_ROUTES, ///< Exceeds the max number of nested routes: 127 (WF_ERROR_MAX_NESTED_ROUTES)
WF_ERROR_CURL_API, ///< CUrl API call error.
_WF_ERROR_END,
} iwn_wf_ecode_e;
#define IWN_WF_SESSION_ID_LEN 32
#define IWN_WF_SESSION_COOKIE_KEY "sessionid"
/// @defgroup wf_handler_return_value Constants as route handlers return values
/// @see iwn_wf_handler
/// @{
#define IWN_WF_RES_NOT_PROCESSED 0 ///< Request is not handled by route handler.
/// Farther processing of route request handlers (iwn_wf_handler) allowed.
#define IWN_WF_RES_PROCESSED 1 ///< Request is processed by route handler, response was sent.
#define IWN_WF_RES_CONNECTION_CLOSE -1 ///< Abort request processing, close network connection.
#define IWN_WF_RES_SKIP_CHILD_ROUTES -2 ///< Skip processing of child routes.
/// Frequently used status codes as return value from route handler.
#define IWN_WF_RES_FORBIDDEN 403
#define IWN_WF_RES_BAD_REQUEST 400
#define IWN_WF_RES_INTERNAL_ERROR 500
#define IWN_WF_RES_NOT_IMPLEMENTED 501
/// @}
/// @defgroup wf_flags HTTP Methods and request status flags.
/// @see iwn_wf_req::flags
/// @see iwn_wf_route::flags
/// @{
#define IWN_WF_GET 0x01U
#define IWN_WF_PUT 0x02U
#define IWN_WF_POST 0x04U
#define IWN_WF_DELETE 0x08U
#define IWN_WF_HEAD 0x10U
#define IWN_WF_OPTIONS 0x20U
#define IWN_WF_PATCH 0x40U
#define IWN_WF_METHODS_ALL (IWN_WF_GET | IWN_WF_PUT | IWN_WF_POST | IWN_WF_DELETE | IWN_WF_HEAD | IWN_WF_OPTIONS \
| IWN_WF_PATCH)
/// With this flag a route pattern is matched even if it matches only prefix part of request path.
/// This flag is set automatically for routes having child subroutes.
#define IWN_WF_MATCH_PREFIX 0x100U
/// Request specific flags.
/// @see iwn_wf_req::flags
#define IWN_WF_FORM_MULTIPART 0x200U
#define IWN_WF_FORM_URL_ENCODED 0x400U
#define IWN_WF_FORM_ALL (IWN_WF_FORM_MULTIPART | IWN_WF_FORM_URL_ENCODED)
/// @}
struct iwn_wf_ctx;
struct iwn_wf_req;
/// Route request handler.
///
/// A handler function is called when request matched to its route configuration:
/// - iwn_wf_route::pattern
/// - iwn_wf_route::flags
///
/// Handler user data `user_data` is supplied from route configuration iwn_wf_route::user_data.
///
/// - If returned value is greater than `1` it will be interpreted as HTTP status code and
/// appropriate client response will be generated.
/// - ` 0` (IWN_WF_RES_NOT_PROCESSED) Handler doesn't write a response. Next matched routes will be processed.
/// - ` 1` (IWN_WF_RES_PROCESSED) Request was fully processed by handler and HTTP response has been sent to client.
/// - `-1` (IWN_WF_RES_CONNECTION_CLOSE) Request connection should be closed, response should be aborted.
/// - `-2` (IWN_WF_RES_SKIP_CHILD_ROUTES) Skip processing of child routes.
///
/// @warning Double check what handler function will always return `1` (IWN_WF_RES_PROCESSED)
/// When `iwn_http_response_write()` or `iwn_http_response_printf()` was called by handler.
/// Otherwise app will meet undefined memory access behavior.
///
/// @see wf_handler_return_value
typedef int (*iwn_wf_handler)(struct iwn_wf_req*, void *user_data);
/// Route disposition callback.
typedef void (*iwn_wf_handler_dispose)(struct iwn_wf_ctx*, void *user_data);
/// Request path regexp submatch entry.
struct iwn_wf_route_submatch { ///< Route regexp submatch node.
const char *input; ///< Matched input.
const char *sp; ///< Pointer to start of submatch.
const char *ep; ///< Pointer to the end of submatch (exclusive).
const struct iwn_wf_route *route; ///< Matched route.
struct iwn_wf_route_submatch *next; ///< Next submatch in chain.
};
/// Web-framework HTTP request object.
struct iwn_wf_req {
struct iwn_wf_ctx *ctx; ///< Framework context.
struct iwn_http_req *http; ///< Low level HTTP request object.
const char *path; ///< Full request path except query string.
const char *path_unmatched; ///< Rest of path not consumed by previous router matcher.
const char *path_matched; ///< Start position of last match section of the path.
const char *body; ///< Pointer to the `\0` terminated request body.
size_t body_len; ///< Length of request body.
struct iwn_wf_route_submatch *first; ///< First regexp request path submatch.
struct iwn_wf_route_submatch *last; ///< Last regexp request path submatch.
struct iwn_wf_route *route; ///< Current route processed by route handler.
/// Request URL query parameters list.
/// @note key/value buffers are zero terminated strings.
struct iwn_pairs query_params;
/// Request form data parameters list.
/// @note key/value buffers are zero terminated strings.
struct iwn_pairs form_params;
/// Request method, form flags.
/// see IWN_WF_<METHOD>, IWN_WF_FORM_MULTIPART,IWN_WF_FORM_URL_ENCODED
uint32_t flags;
};
/// Web-framework Route configuration.
struct iwn_wf_route {
struct iwn_wf_ctx *ctx; ///< Web-framework context associated with route.
const struct iwn_wf_route *parent; ///< Optional parent route.
/// A route matching pattern.
/// To be consistent in pattern matching follow the these rules:
/// - Non regexp patterns start with: `/` Eg: `/hello/name`
/// - Regular expression patterns start with: `^/` Eg: `^/hello.*`
const char *pattern;
uint32_t flags; ///< Matching flags @ref wf_flags
iwn_wf_handler handler; ///< Optional route handler.
iwn_wf_handler_dispose handler_dispose; ///< Optional handler dispose callback.
#ifdef IW_BLOCKS
int (^handler_block)(struct iwn_wf_req*);
int (^handler_dispose_block)(void);
#endif
void *user_data; ///< Optional route handler user data.
const char *tag; ///< Constant string tag associated with routed, used for debugging.
};
/// Configuration of HTTP session storage backend.
struct iwn_wf_session_store {
/// Gets session value under the specified key.
/// Returned value should be freed by `free()`
char* (*get)(struct iwn_wf_session_store *store, const char *sid, const char *key);
iwrc (*put)(struct iwn_wf_session_store *store, const char *sid, const char *key, const char *val);
void (*del)(struct iwn_wf_session_store *store, const char *sid, const char *key);
void (*clear)(struct iwn_wf_session_store *store, const char *sid);
void (*dispose)(struct iwn_wf_session_store *store);
void *user_data;
};
/// HTTP server configuration.
struct iwn_wf_server_spec {
struct iwn_poller *poller; ///< A poller. Required.
struct iwn_http_server_ssl_spec ssl; ///< TLS server parameters.
struct iwn_wf_session_store session_store; ///< HTTP session store configuration.
iwn_http_server_proxy_handler proxy_handler; ///< HTTP proxy session setup handler.
const char *listen; ///< Server listen hostname. Default: localhost
int port; ///< Default: 8080 http, 8443 https
int socket_queue_size; ///< Default: 64
int request_buf_max_size; ///< Default: 8Mb
int request_buf_size; ///< Default: 1024
int request_file_max_size; ///< -1: To disable chunked requests and files uploading.
/// Default: 50Mb
int request_max_headers_count; ///< Default: 127
int request_timeout_keepalive_sec; ///< -1 Disable timeout, 0 Use default timeout: 120sec
int request_timeout_sec; ///< -1 Disable timeout, 0 Use default timeout: 20sec
int request_token_max_len; ///< Default: 8192
};
/// Web-framework configuration context.
struct iwn_wf_ctx {
/// Context root route configuration.
const struct iwn_wf_route *root;
};
/// Create a web-framework context.
/// Web-framework context must be created with root route configuration.
/// @note Root route handler is called for requests not handled by other route handlers.
/// @param root Optional. Root route configuration. Pattern doesn't makes sense for root router.
/// @param[out] Output context. Should be disposed by `iwn_wf_destroy()`
///
IW_EXPORT WUR iwrc iwn_wf_create(const struct iwn_wf_route *root, struct iwn_wf_ctx **out_ctx);
/// Register new route.
/// @param spec Route configuration.
/// @param[out] Optional placeholder to store resulted route configuration.
/// Used when you build hierarchy of routes.
IW_EXPORT WUR iwrc iwn_wf_route(const struct iwn_wf_route *spec, struct iwn_wf_route **out_route);
#ifdef IW_BLOCKS
IW_EXPORT WUR iwrc iwn_wf_route_block(const struct iwn_wf_route*, struct iwn_wf_route**, int (^)(struct iwn_wf_req*));
#endif
/// Create HTTP server associated with web-framework context.
/// If poller is active @ref iwn_poller_poll() server will process incoming HTTP request according
/// to the routes configuration.
IW_EXPORT WUR iwrc iwn_wf_server(const struct iwn_wf_server_spec *spec, struct iwn_wf_ctx *ctx);
/// Print routes configuration to the given `out` file.
/// @note Use @ref iwn_wf_route::tag routes labeling.
IW_EXPORT void iwn_wf_route_print(const struct iwn_wf_route*, FILE *out);
/// Returns a poller associated with framework context.
IW_EXPORT struct iwn_poller* iwn_wf_poller_get(struct iwn_wf_ctx *ctx);
/// Returns server socket fd.
IW_EXPORT int iwn_wf_server_fd_get(struct iwn_wf_ctx *ctx);
/// Find the first regular expression submatch part for the current route.
IW_EXPORT struct iwn_wf_route_submatch* iwn_wf_request_submatch_first(const struct iwn_wf_req*);
/// Find the last regular expression submatch part for the current route.
IW_EXPORT struct iwn_wf_route_submatch* iwn_wf_request_submatch_last(const struct iwn_wf_req*);
/// Parses a query string (the part after `?`) and fill the provided `pairs` chain
/// where `pool` is used to store new pairs records. Values for pairs points to parts of query buffer which is modified
// in place.
IW_EXPORT iwrc iwn_wf_parse_query_inplace(struct iwpool *pool, struct iwn_pairs *pairs, char *query, size_t query_len);
IW_EXPORT const char* iwn_wf_header_val_part_next(
const char *header_val,
const char *ptr,
const char *header_val_end,
struct iwn_pair *out);
IW_EXPORT struct iwn_pair iwn_wf_header_val_part_find(
const char *header_val,
const char *header_val_end,
const char *part_name);
IW_EXPORT struct iwn_pair iwn_wf_header_part_find(
struct iwn_wf_req*,
const char *header_name,
const char *part_name);
/// Returns a session id associated with request or zero if no session created.
IW_EXPORT const char* iwn_wf_session_id(struct iwn_wf_req*);
/// Associate a request with session id.
/// @note Session id must be zero terminated character string with length `IWN_WF_SESSION_ID_LEN`
IW_EXPORT iwrc iwn_wf_session_id_set(struct iwn_wf_req*, const char *sid);
/// Returns a string data stored in session by given `key`.
IW_EXPORT const char* iwn_wf_session_get(struct iwn_wf_req*, const char *key);
/// Store some zero terminated data buffer in request session under specified `key`.
IW_EXPORT iwrc iwn_wf_session_put(struct iwn_wf_req*, const char *key, const char *data);
/// Store printf formatted data in request session under specified `key`.
IW_EXPORT iwrc iwn_wf_session_printf(struct iwn_wf_req*, const char *key, const char *fmt, ...)
__attribute__((format(__printf__, 3, 4)));
IW_EXPORT iwrc iwn_wf_session_printf_va(struct iwn_wf_req*, const char *key, const char *fmt, va_list va);
/// Remove session data under given `key`.
IW_EXPORT void iwn_wf_session_del(struct iwn_wf_req*, const char *key);
/// Remove all data from session associated with request.
IW_EXPORT void iwn_wf_session_clear(struct iwn_wf_req*);
struct iwn_wf_cookie_opts {
const char *path;
const char *domain;
const char *extra;
int max_age_sec;
bool httponly;
bool secure;
};
/// Set a cookie in HTTP response.
/// `",;/` in `value` must be escaped.
IW_EXPORT iwrc iwn_wf_cookie_add(
struct iwn_wf_req*,
const char *name,
const char *value,
const struct iwn_wf_cookie_opts opts);
/// Destroy web-framework configuration and stops an HTTP server.
IW_EXPORT void iwn_wf_destroy(struct iwn_wf_ctx *ctx);
/// Initialize global web-framework state and register WF error codes.
IW_EXPORT iwrc iwn_wf_init(void);
IW_EXTERN_C_END;