mirror of
https://github.com/zigzap/zap.git
synced 2025-10-21 15:44:10 +00:00
235 lines
7.8 KiB
C
235 lines
7.8 KiB
C
/*
|
|
Copyright: Boaz Segev, 2016-2019
|
|
License: MIT
|
|
|
|
Feel free to copy, use and enjoy according to the license provided.
|
|
*/
|
|
#ifndef H_HTTP_INTERNAL_H
|
|
#define H_HTTP_INTERNAL_H
|
|
|
|
#include <fio.h>
|
|
/* subscription lists have a long lifetime */
|
|
#define FIO_FORCE_MALLOC_TMP 1
|
|
#define FIO_INCLUDE_LINKED_LIST
|
|
#include <fio.h>
|
|
|
|
#include <http.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
|
|
/* *****************************************************************************
|
|
Types
|
|
***************************************************************************** */
|
|
|
|
typedef struct http_fio_protocol_s http_fio_protocol_s;
|
|
typedef struct http_vtable_s http_vtable_s;
|
|
|
|
struct http_vtable_s {
|
|
/** Should send existing headers and data */
|
|
int (*const http_send_body)(http_s *h, void *data, uintptr_t length);
|
|
/** Should send existing headers and file */
|
|
int (*const http_sendfile)(http_s *h, int fd, uintptr_t length,
|
|
uintptr_t offset);
|
|
/** Should send existing headers and data and prepare for streaming */
|
|
int (*const http_stream)(http_s *h, void *data, uintptr_t length);
|
|
/** Should send existing headers or complete streaming */
|
|
void (*const http_finish)(http_s *h);
|
|
/** Push for data. */
|
|
int (*const http_push_data)(http_s *h, void *data, uintptr_t length,
|
|
FIOBJ mime_type);
|
|
/** Upgrades a connection to Websockets. */
|
|
int (*const http2websocket)(http_s *h, websocket_settings_s *arg);
|
|
/** Push for files. */
|
|
int (*const http_push_file)(http_s *h, FIOBJ filename, FIOBJ mime_type);
|
|
/** Pauses the request / response handling. */
|
|
void (*http_on_pause)(http_s *, http_fio_protocol_s *);
|
|
|
|
/** Resumes a request / response handling. */
|
|
void (*http_on_resume)(http_s *, http_fio_protocol_s *);
|
|
/** hijacks the socket aaway from the protocol. */
|
|
intptr_t (*http_hijack)(http_s *h, fio_str_info_s *leftover);
|
|
|
|
/** Upgrades an HTTP connection to an EventSource (SSE) connection. */
|
|
int (*http_upgrade2sse)(http_s *h, http_sse_s *sse);
|
|
/** Writes data to an EventSource (SSE) connection. MUST free the FIOBJ. */
|
|
int (*http_sse_write)(http_sse_s *sse, FIOBJ str);
|
|
/** Closes an EventSource (SSE) connection. */
|
|
int (*http_sse_close)(http_sse_s *sse);
|
|
};
|
|
|
|
struct http_fio_protocol_s {
|
|
fio_protocol_s protocol; /* facil.io protocol */
|
|
intptr_t uuid; /* socket uuid */
|
|
http_settings_s *settings; /* pointer to HTTP settings */
|
|
};
|
|
|
|
#define http2protocol(h) ((http_fio_protocol_s *)h->private_data.flag)
|
|
|
|
/* *****************************************************************************
|
|
Constants that shouldn't be accessed by the users (`fiobj_dup` required).
|
|
***************************************************************************** */
|
|
|
|
extern FIOBJ HTTP_HEADER_ACCEPT_RANGES;
|
|
extern FIOBJ HTTP_HEADER_WS_SEC_CLIENT_KEY;
|
|
extern FIOBJ HTTP_HEADER_WS_SEC_KEY;
|
|
extern FIOBJ HTTP_HVALUE_BYTES;
|
|
extern FIOBJ HTTP_HVALUE_CLOSE;
|
|
extern FIOBJ HTTP_HVALUE_CONTENT_TYPE_DEFAULT;
|
|
extern FIOBJ HTTP_HVALUE_GZIP;
|
|
extern FIOBJ HTTP_HVALUE_KEEP_ALIVE;
|
|
extern FIOBJ HTTP_HVALUE_MAX_AGE;
|
|
extern FIOBJ HTTP_HVALUE_NO_CACHE;
|
|
extern FIOBJ HTTP_HVALUE_SSE_MIME;
|
|
extern FIOBJ HTTP_HVALUE_WEBSOCKET;
|
|
extern FIOBJ HTTP_HVALUE_WS_SEC_VERSION;
|
|
extern FIOBJ HTTP_HVALUE_WS_UPGRADE;
|
|
extern FIOBJ HTTP_HVALUE_WS_VERSION;
|
|
|
|
/* *****************************************************************************
|
|
HTTP request/response object management
|
|
***************************************************************************** */
|
|
|
|
static inline void http_s_new(http_s *h, http_fio_protocol_s *owner,
|
|
http_vtable_s *vtbl) {
|
|
*h = (http_s){
|
|
.private_data =
|
|
{
|
|
.vtbl = vtbl,
|
|
.flag = (uintptr_t)owner,
|
|
.out_headers = fiobj_hash_new(),
|
|
},
|
|
.headers = fiobj_hash_new(),
|
|
.received_at = fio_last_tick(),
|
|
.status = 200,
|
|
};
|
|
}
|
|
|
|
static inline void http_s_destroy(http_s *h, uint8_t log) {
|
|
if (log && h->status && !h->status_str) {
|
|
http_write_log(h);
|
|
}
|
|
fiobj_free(h->method);
|
|
fiobj_free(h->status_str);
|
|
fiobj_free(h->private_data.out_headers);
|
|
fiobj_free(h->headers);
|
|
fiobj_free(h->version);
|
|
fiobj_free(h->query);
|
|
fiobj_free(h->path);
|
|
fiobj_free(h->cookies);
|
|
fiobj_free(h->body);
|
|
fiobj_free(h->params);
|
|
|
|
*h = (http_s){
|
|
.private_data.vtbl = h->private_data.vtbl,
|
|
.private_data.flag = h->private_data.flag,
|
|
};
|
|
}
|
|
|
|
static inline void http_s_clear(http_s *h, uint8_t log) {
|
|
http_s_destroy(h, log);
|
|
http_s_new(h, (http_fio_protocol_s *)h->private_data.flag,
|
|
h->private_data.vtbl);
|
|
}
|
|
|
|
/** tests handle validity */
|
|
#define HTTP_INVALID_HANDLE(h) \
|
|
(!(h) || (!(h)->method && !(h)->status_str && (h)->status))
|
|
|
|
/* *****************************************************************************
|
|
Request / Response Handlers
|
|
***************************************************************************** */
|
|
|
|
/** Use this function to handle HTTP requests.*/
|
|
void http_on_request_handler______internal(http_s *h,
|
|
http_settings_s *settings);
|
|
|
|
void http_on_response_handler______internal(http_s *h,
|
|
http_settings_s *settings);
|
|
int http_send_error2(size_t error, intptr_t uuid, http_settings_s *settings);
|
|
|
|
/* *****************************************************************************
|
|
EventSource Support (SSE)
|
|
***************************************************************************** */
|
|
|
|
typedef struct http_sse_internal_s {
|
|
http_sse_s sse; /* the user SSE settings */
|
|
intptr_t uuid; /* the socket's uuid */
|
|
http_vtable_s *vtable; /* the protocol's vtable */
|
|
uintptr_t id; /* the SSE identifier */
|
|
fio_ls_s subscriptions; /* Subscription List */
|
|
fio_lock_i lock; /* Subscription List lock */
|
|
size_t ref; /* reference count */
|
|
} http_sse_internal_s;
|
|
|
|
static inline void http_sse_init(http_sse_internal_s *sse, intptr_t uuid,
|
|
http_vtable_s *vtbl, http_sse_s *args) {
|
|
*sse = (http_sse_internal_s){
|
|
.sse = *args,
|
|
.uuid = uuid,
|
|
.subscriptions = FIO_LS_INIT(sse->subscriptions),
|
|
.vtable = vtbl,
|
|
.ref = 1,
|
|
};
|
|
}
|
|
|
|
static inline void http_sse_try_free(http_sse_internal_s *sse) {
|
|
if (fio_atomic_sub(&sse->ref, 1))
|
|
return;
|
|
fio_free(sse);
|
|
}
|
|
|
|
static inline void http_sse_destroy(http_sse_internal_s *sse) {
|
|
while (fio_ls_any(&sse->subscriptions)) {
|
|
void *sub = fio_ls_pop(&sse->subscriptions);
|
|
fio_unsubscribe(sub);
|
|
}
|
|
if (sse->sse.on_close)
|
|
sse->sse.on_close(&sse->sse);
|
|
sse->uuid = -1;
|
|
http_sse_try_free(sse);
|
|
}
|
|
|
|
/* *****************************************************************************
|
|
Helpers
|
|
***************************************************************************** */
|
|
|
|
/** sets an outgoing header only if it doesn't exist */
|
|
static inline void set_header_if_missing(FIOBJ hash, FIOBJ name, FIOBJ value) {
|
|
FIOBJ old = fiobj_hash_replace(hash, name, value);
|
|
if (!old)
|
|
return;
|
|
fiobj_hash_replace(hash, name, old);
|
|
fiobj_free(value);
|
|
}
|
|
|
|
/** sets an outgoing header, collecting duplicates in an Array (i.e. cookies)
|
|
*/
|
|
static inline void set_header_add(FIOBJ hash, FIOBJ name, FIOBJ value) {
|
|
FIOBJ old = fiobj_hash_replace(hash, name, value);
|
|
if (!old)
|
|
return;
|
|
if (!value) {
|
|
fiobj_free(old);
|
|
return;
|
|
}
|
|
if (!FIOBJ_TYPE_IS(old, FIOBJ_T_ARRAY)) {
|
|
FIOBJ tmp = fiobj_ary_new();
|
|
fiobj_ary_push(tmp, old);
|
|
old = tmp;
|
|
}
|
|
if (FIOBJ_TYPE_IS(value, FIOBJ_T_ARRAY)) {
|
|
for (size_t i = 0; i < fiobj_ary_count(value); ++i) {
|
|
fiobj_ary_push(old, fiobj_dup(fiobj_ary_index(value, i)));
|
|
}
|
|
/* frees `value` */
|
|
fiobj_hash_set(hash, name, old);
|
|
return;
|
|
}
|
|
/* value will be owned by both hash and array */
|
|
fiobj_ary_push(old, value);
|
|
/* don't free `value` (leave in array) */
|
|
fiobj_hash_replace(hash, name, old);
|
|
}
|
|
|
|
#endif /* H_HTTP_INTERNAL_H */
|