diff --git a/qse/include/qse/net/htrd.h b/qse/include/qse/net/htrd.h index 1458013f..53477794 100644 --- a/qse/include/qse/net/htrd.h +++ b/qse/include/qse/net/htrd.h @@ -165,9 +165,9 @@ void qse_htrd_setrecbs ( * callback function if it has processed a proper htrd request. */ int qse_htrd_feed ( - qse_htrd_t* htrd, /**< htrd */ - const qse_htoc_t* req, /**< request octets */ - qse_size_t len /**< number of octets */ + qse_htrd_t* htrd, /**< htrd */ + const qse_mchar_t* req, /**< request octets */ + qse_size_t len /**< number of octets */ ); int qse_htrd_read ( diff --git a/qse/include/qse/net/http.h b/qse/include/qse/net/http.h index 51c39a03..a89d080a 100644 --- a/qse/include/qse/net/http.h +++ b/qse/include/qse/net/http.h @@ -31,8 +31,6 @@ #include #include -typedef qse_mchar_t qse_htoc_t; - /* octet buffer */ typedef qse_mbs_t qse_htob_t; @@ -80,11 +78,20 @@ typedef qse_foff_t qse_http_range_int_t; typedef qse_ulong_t qse_http_range_int_t; #endif +enum qse_http_range_type_t +{ + QSE_HTTP_RANGE_NONE, + QSE_HTTP_RANGE_PROPER, + QSE_HTTP_RANGE_SUFFIX +}; +typedef enum qse_http_range_type_t qse_http_range_type_t; /** * The qse_http_range_t type defines a structure that can represent * a value for the @b Range: http header. * - * If suffix is non-zero, 'from' is meaningleass and 'to' indicates + * If type is #QSE_HTTP_RANGE_NONE, this range is not valid. + * + * If type is #QSE_HTTP_RANGE_SUFFIX, 'from' is meaningleass and 'to' indicates * the number of bytes from the back. * - -500 => last 500 bytes * @@ -95,16 +102,17 @@ typedef qse_ulong_t qse_http_range_int_t; * range.to = range.to + range.from - 1; * @endcode * - * If suffix is zero, 'from' and 'to' represents a proper range where - * the value of 0 indicates the first byte. This doesn't require any adjustment. + * If type is #QSE_HTTP_RANGE_PROPER, 'from' and 'to' represents a proper range + * where the value of 0 indicates the first byte. This doesn't require any + * adjustment. * - 0-999 => first 1000 bytes * - 99- => from the 100th bytes to the end. */ struct qse_http_range_t { - int suffix; /**< suffix indicator */ - qse_http_range_int_t from; /**< starting offset */ - qse_http_range_int_t to; /**< ending offset */ + qse_http_range_type_t type; /**< type indicator */ + qse_http_range_int_t from; /**< starting offset */ + qse_http_range_int_t to; /**< ending offset */ }; typedef struct qse_http_range_t qse_http_range_t; @@ -112,12 +120,12 @@ typedef struct qse_http_range_t qse_http_range_t; extern "C" { #endif -const qse_htoc_t* qse_gethttpmethodname ( +const qse_mchar_t* qse_gethttpmethodname ( qse_http_method_t type ); int qse_gethttpmethodtype ( - const qse_htoc_t* name, + const qse_mchar_t* name, qse_http_method_t* method ); diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h index 1beb6732..c356670c 100644 --- a/qse/include/qse/net/httpd.h +++ b/qse/include/qse/net/httpd.h @@ -46,16 +46,38 @@ typedef enum qse_httpd_errnum_t qse_httpd_errnum_t; typedef struct qse_httpd_cbs_t qse_httpd_cbs_t; struct qse_httpd_cbs_t { - int (*handle_request) (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); - int (*handle_expect_continue) (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); + int (*handle_request) ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); + int (*handle_expect_continue) ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); }; typedef struct qse_httpd_task_t qse_httpd_task_t; + +typedef int (*qse_httpd_task_init_t) ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + qse_httpd_task_t* task +); + +typedef void (*qse_httpd_task_fini_t) ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + qse_httpd_task_t* task +); + +typedef int (*qse_httpd_task_main_t) ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + qse_httpd_task_t* task +); + struct qse_httpd_task_t { - int (*init) (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task); - void (*fini) (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task); - int (*main) (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task); + /* you must not call another entask functions from within initailizer */ + qse_httpd_task_init_t init; + qse_httpd_task_fini_t fini; + qse_httpd_task_main_t main; void* ctx; }; @@ -106,6 +128,12 @@ int qse_httpd_addlisteners ( const qse_char_t* uri ); + +void qse_httpd_markclientbad ( + qse_httpd_t* httpd, + qse_httpd_client_t* client +); + #define qse_httpd_gettaskxtn(httpd,task) ((void*)(task+1)) int qse_httpd_entask ( @@ -115,27 +143,35 @@ int qse_httpd_entask ( qse_size_t xtnsize ); -int qse_httpd_entasksendtext ( +int qse_httpd_entasktext ( qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_mchar_t* text ); -int qse_httpd_entasksendfmt ( +int qse_httpd_entaskformat ( qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_mchar_t* fmt, ... ); -int qse_httpd_entasksendfile ( +int qse_httpd_entaskfile ( qse_httpd_t* httpd, qse_httpd_client_t* client, - int fd, + qse_ubi_t handle, qse_foff_t offset, qse_foff_t size ); +int qse_httpd_entaskpath ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + const qse_mchar_t* name, + const qse_http_range_t* range, + const qse_http_version_t* version +); + int qse_httpd_entaskdisconnect ( qse_httpd_t* httpd, qse_httpd_client_t* client diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index d8083c1e..b3f2e6da 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -24,58 +24,58 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (htrd) -static const qse_htoc_t NUL = '\0'; +static const qse_mchar_t NUL = QSE_MT('\0'); -static QSE_INLINE int is_whspace_octet (qse_htoc_t c) +static QSE_INLINE int is_whspace_octet (qse_mchar_t c) { - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; + return c == QSE_MT(' ') || c == QSE_MT('\t') || c == QSE_MT('\r') || c == QSE_MT('\n'); } -static QSE_INLINE int is_space_octet (qse_htoc_t c) +static QSE_INLINE int is_space_octet (qse_mchar_t c) { return c == ' ' || c == '\t' || c == '\r'; } -static QSE_INLINE int is_purespace_octet (qse_htoc_t c) +static QSE_INLINE int is_purespace_octet (qse_mchar_t c) { return c == ' ' || c == '\t'; } -static QSE_INLINE int is_upalpha_octet (qse_htoc_t c) +static QSE_INLINE int is_upalpha_octet (qse_mchar_t c) { return c >= 'A' && c <= 'Z'; } -static QSE_INLINE int is_loalpha_octet (qse_htoc_t c) +static QSE_INLINE int is_loalpha_octet (qse_mchar_t c) { return c >= 'a' && c <= 'z'; } -static QSE_INLINE int is_alpha_octet (qse_htoc_t c) +static QSE_INLINE int is_alpha_octet (qse_mchar_t c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } -static QSE_INLINE int is_digit_octet (qse_htoc_t c) +static QSE_INLINE int is_digit_octet (qse_mchar_t c) { return c >= '0' && c <= '9'; } -static QSE_INLINE int is_xdigit_octet (qse_htoc_t c) +static QSE_INLINE int is_xdigit_octet (qse_mchar_t c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } -static QSE_INLINE int digit_to_num (qse_htoc_t c) +static QSE_INLINE int digit_to_num (qse_mchar_t c) { if (c >= '0' && c <= '9') return c - '0'; return -1; } -static QSE_INLINE int xdigit_to_num (qse_htoc_t c) +static QSE_INLINE int xdigit_to_num (qse_mchar_t c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'Z') return c - 'A' + 10; @@ -85,7 +85,7 @@ static QSE_INLINE int xdigit_to_num (qse_htoc_t c) static QSE_INLINE int push_to_buffer ( qse_htrd_t* htrd, qse_htob_t* octb, - const qse_htoc_t* ptr, qse_size_t len) + const qse_mchar_t* ptr, qse_size_t len) { if (qse_mbs_ncat (octb, ptr, len) == (qse_size_t)-1) { @@ -198,10 +198,10 @@ void qse_htrd_fini (qse_htrd_t* htrd) qse_mbs_fini (&htrd->tmp.qparam); } -static qse_htoc_t* parse_initial_line ( - qse_htrd_t* htrd, qse_htoc_t* line) +static qse_mchar_t* parse_initial_line ( + qse_htrd_t* htrd, qse_mchar_t* line) { - qse_htoc_t* p = line; + qse_mchar_t* p = line; qse_mcstr_t tmp; qse_http_method_t mtype; @@ -225,7 +225,7 @@ static qse_htoc_t* parse_initial_line ( qse_htre_setqmethod (&htrd->re, mtype); } else if ((htrd->option & QSE_HTRD_RESPONSE) && - qse_mbsxcmp (tmp.ptr, tmp.len, "HTTP") == 0) + qse_mbsxcmp (tmp.ptr, tmp.len, QSE_MT("HTTP")) == 0) { /* it begins with HTTP. it may be a response */ htrd->retype = QSE_HTRD_RETYPE_S; @@ -290,7 +290,7 @@ static qse_htoc_t* parse_initial_line ( } else { - qse_htoc_t* out; + qse_mchar_t* out; qse_mcstr_t param; /* method name must be followed by space */ @@ -437,12 +437,12 @@ void qse_htrd_setrecbs (qse_htrd_t* htrd, const qse_htrd_recbs_t* recbs) #define octet_toupper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~0x20) : (c)) static QSE_INLINE int compare_octets ( - const qse_htoc_t* s1, qse_size_t len1, - const qse_htoc_t* s2, qse_size_t len2) + const qse_mchar_t* s1, qse_size_t len1, + const qse_mchar_t* s2, qse_size_t len2) { qse_char_t c1, c2; - const qse_htoc_t* end1 = s1 + len1; - const qse_htoc_t* end2 = s2 + len2; + const qse_mchar_t* end1 = s1 + len1; + const qse_mchar_t* end2 = s2 + len2; while (s1 < end1) { @@ -487,7 +487,7 @@ static QSE_INLINE int capture_content_length ( qse_htrd_t* htrd, qse_htb_pair_t* pair) { qse_size_t len = 0, off = 0, tmp; - const qse_htoc_t* ptr = QSE_HTB_VPTR(pair); + const qse_mchar_t* ptr = QSE_HTB_VPTR(pair); while (off < QSE_HTB_VLEN(pair)) { @@ -577,7 +577,7 @@ static QSE_INLINE int capture_key_header ( { static struct { - const qse_htoc_t* ptr; + const qse_mchar_t* ptr; qse_size_t len; int (*handler) (qse_htrd_t*, qse_htb_pair_t*); } hdrtab[] = @@ -653,7 +653,7 @@ static qse_htb_pair_t* hdr_cbserter ( /* the key exists. let's combine values, each separated * by a comma */ struct hdr_cmb_t* cmb; - qse_htoc_t* ptr; + qse_mchar_t* ptr; qse_size_t len; /* TODO: reduce waste in case the same key appears again. @@ -671,7 +671,7 @@ static qse_htb_pair_t* hdr_cbserter ( cmb = (struct hdr_cmb_t*) QSE_MMGR_ALLOC ( tx->htrd->mmgr, QSE_SIZEOF(*cmb) + - QSE_SIZEOF(qse_htoc_t) * (QSE_HTB_VLEN(pair) + 1 + tx->vlen + 1) + QSE_SIZEOF(qse_mchar_t) * (QSE_HTB_VLEN(pair) + 1 + tx->vlen + 1) ); if (cmb == QSE_NULL) { @@ -680,7 +680,7 @@ static qse_htb_pair_t* hdr_cbserter ( } /* let 'ptr' point to the actual space for the combined value */ - ptr = (qse_htoc_t*)(cmb + 1); + ptr = (qse_mchar_t*)(cmb + 1); len = 0; /* fill the space with the value */ @@ -724,12 +724,12 @@ Change it to doubly linked for this? } } -qse_htoc_t* parse_header_fields (qse_htrd_t* htrd, qse_htoc_t* line) +qse_mchar_t* parse_header_fields (qse_htrd_t* htrd, qse_mchar_t* line) { - qse_htoc_t* p = line, * last; + qse_mchar_t* p = line, * last; struct { - qse_htoc_t* ptr; + qse_mchar_t* ptr; qse_size_t len; } name, value; @@ -767,7 +767,7 @@ qse_htoc_t* parse_header_fields (qse_htrd_t* htrd, qse_htoc_t* line) * the continuation */ if (is_purespace_octet (*++p)) { - qse_htoc_t* cpydst; + qse_mchar_t* cpydst; cpydst = p - 1; if (*(cpydst-1) == '\r') cpydst--; @@ -817,9 +817,9 @@ badhdr: } static QSE_INLINE int parse_initial_line_and_headers ( - qse_htrd_t* htrd, const qse_htoc_t* req, qse_size_t rlen) + qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t rlen) { - qse_htoc_t* p; + qse_mchar_t* p; /* add the actual request */ if (push_to_buffer (htrd, &htrd->fed.b.raw, req, rlen) <= -1) return -1; @@ -864,9 +864,9 @@ static QSE_INLINE int parse_initial_line_and_headers ( #define GET_CHUNK_CRLF 3 #define GET_CHUNK_TRAILERS 4 -static const qse_htoc_t* getchunklen (qse_htrd_t* htrd, const qse_htoc_t* ptr, qse_size_t len) +static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr, qse_size_t len) { - const qse_htoc_t* end = ptr + len; + const qse_mchar_t* end = ptr + len; /* this function must be called in the GET_CHUNK_LEN context */ QSE_ASSERT (htrd->fed.s.chunk.phase == GET_CHUNK_LEN); @@ -932,14 +932,14 @@ static const qse_htoc_t* getchunklen (qse_htrd_t* htrd, const qse_htoc_t* ptr, q return ptr; } -static const qse_htoc_t* get_trailing_headers ( - qse_htrd_t* htrd, const qse_htoc_t* req, const qse_htoc_t* end) +static const qse_mchar_t* get_trailing_headers ( + qse_htrd_t* htrd, const qse_mchar_t* req, const qse_mchar_t* end) { - const qse_htoc_t* ptr = req; + const qse_mchar_t* ptr = req; while (ptr < end) { - register qse_htoc_t b = *ptr++; + register qse_mchar_t b = *ptr++; switch (b) { @@ -957,7 +957,7 @@ static const qse_htoc_t* get_trailing_headers ( } else { - qse_htoc_t* p; + qse_mchar_t* p; QSE_ASSERT (htrd->fed.s.crlf <= 3); htrd->fed.s.crlf = 0; @@ -1008,10 +1008,10 @@ done: } /* feed the percent encoded string */ -int qse_htrd_feed (qse_htrd_t* htrd, const qse_htoc_t* req, qse_size_t len) +int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) { - const qse_htoc_t* end = req + len; - const qse_htoc_t* ptr = req; + const qse_mchar_t* end = req + len; + const qse_mchar_t* ptr = req; /* does this goto drop code maintainability? */ if (htrd->fed.s.need > 0) @@ -1041,7 +1041,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_htoc_t* req, qse_size_t len) while (ptr < end) { - register qse_htoc_t b = *ptr++; + register qse_mchar_t b = *ptr++; if (htrd->option & QSE_HTRD_LEADINGEMPTYLINES && htrd->fed.s.plen <= 0 && is_whspace_octet(b)) @@ -1310,8 +1310,8 @@ feedme_more: int qse_htrd_scanqparam (qse_htrd_t* htrd, const qse_mcstr_t* cstr) { qse_mcstr_t key, val; - const qse_htoc_t* p, * end; - qse_htoc_t* out; + const qse_mchar_t* p, * end; + qse_mchar_t* out; if (cstr == QSE_NULL) cstr = qse_htre_getqparamcstr(&htrd->re); diff --git a/qse/lib/net/htre.c b/qse/lib/net/htre.c index 6203f3eb..318cb13e 100644 --- a/qse/lib/net/htre.c +++ b/qse/lib/net/htre.c @@ -77,7 +77,7 @@ const qse_mchar_t* qse_htre_gethdrval ( { qse_htb_pair_t* pair; pair = qse_htb_search (&re->hdrtab, name, qse_mbslen(name)); - if (pair == QSE_NULL) return pair; + if (pair == QSE_NULL) return QSE_NULL; return QSE_HTB_VPTR(pair); } diff --git a/qse/lib/net/http.c b/qse/lib/net/http.c index e7b45764..d1605557 100644 --- a/qse/lib/net/http.c +++ b/qse/lib/net/http.c @@ -142,7 +142,7 @@ int qse_parsehttprange (const qse_mchar_t* str, qse_http_range_t* range) * like bytes=1-20,30-50 */ qse_http_range_int_t from, to; - int suffix = 0; + int type = QSE_HTTP_RANGE_PROPER; if (str[0] != QSE_MT('b') || str[1] != QSE_MT('y') || @@ -163,7 +163,7 @@ int qse_parsehttprange (const qse_mchar_t* str, qse_http_range_t* range) } while (QSE_ISDIGIT(*str)); } - else suffix = 1; + else type = QSE_HTTP_RANGE_SUFFIX; if (*str != QSE_MT('-')) return -1; str++; @@ -182,7 +182,7 @@ int qse_parsehttprange (const qse_mchar_t* str, qse_http_range_t* range) if (from > to) return -1; - range->suffix = suffix; + range->type = type; range->from = from; range->to = to; return 0; diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index dcfd26d9..097d12fc 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -778,7 +778,7 @@ static int make_fd_set_from_client_array (qse_httpd_t* httpd, fd_set* r, fd_set* { if (ca->data[fd].htrd) { - if (r) + if (r && !ca->data[fd].bad) { FD_SET (ca->data[fd].handle.i, r); if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i; @@ -860,7 +860,10 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %S\n"), strerro /* break; */ continue; } - if (n == 0) continue; + if (n == 0) + { + continue; + } for (fd = 0; fd < httpd->client.array.capa; fd++) { @@ -873,9 +876,15 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %S\n"), strerro if (client->bad) { /*send (client->handle, i, "INTERNAL SERVER ERROR..", ...);*/ - shutdown (client->handle.i, 0); + /*shutdown (client->handle.i, 0);*/ + pthread_mutex_lock (&httpd->client.mutex); + delete_from_client_array (httpd, fd); + pthread_mutex_unlock (&httpd->client.mutex); + } + else if (client->task.queue.count > 0) + { + perform_task (httpd, client); } - else if (client->task.queue.count > 0) perform_task (httpd, client); } } @@ -887,7 +896,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %S\n"), strerro static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client) { - qse_htoc_t buf[1024]; + qse_mchar_t buf[1024]; qse_ssize_t m; reread: @@ -974,7 +983,10 @@ int qse_httpd_loop (qse_httpd_t* httpd) tv.tv_sec = 1; tv.tv_usec = 0; + pthread_mutex_lock (&httpd->client.mutex); max = make_fd_set_from_client_array (httpd, &r, QSE_NULL); + pthread_mutex_unlock (&httpd->client.mutex); + n = select (max + 1, &r, NULL, NULL, &tv); if (n <= -1) { @@ -997,14 +1009,18 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n")); if (!client->htrd) continue; - if (FD_ISSET(client->handle.i, &r)) + if (FD_ISSET(client->handle.i, &r)) { /* got input */ if (read_from_client (httpd, client) <= -1) { + qse_httpd_markclientbad (httpd, client); + shutdown (client->handle.i, 0); + #if 0 pthread_mutex_lock (&httpd->client.mutex); delete_from_client_array (httpd, fd); pthread_mutex_unlock (&httpd->client.mutex); + #endif } } } diff --git a/qse/lib/net/httpd_task.c b/qse/lib/net/httpd_task.c index 61a2bf22..4d7a9313 100644 --- a/qse/lib/net/httpd_task.c +++ b/qse/lib/net/httpd_task.c @@ -56,17 +56,17 @@ int qse_httpd_entaskdisconnect (qse_httpd_t* httpd, qse_httpd_client_t* client) /*------------------------------------------------------------------------*/ -typedef struct task_sendtext_t task_sendtext_t; -struct task_sendtext_t +typedef struct task_text_t task_text_t; +struct task_text_t { const qse_mchar_t* ptr; qse_size_t left; }; -static int httpd_init_sendtext ( +static int task_init_text ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { - task_sendtext_t* xtn = qse_httpd_gettaskxtn (httpd, task); + task_text_t* xtn = qse_httpd_gettaskxtn (httpd, task); QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn)); QSE_MEMCPY (xtn + 1, xtn->ptr, xtn->left); @@ -76,12 +76,12 @@ static int httpd_init_sendtext ( return 0; } -static int httpd_main_sendtext ( +static int task_main_text ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { ssize_t n; size_t count; - task_sendtext_t* ctx = (task_sendtext_t*)task->ctx; + task_text_t* ctx = (task_text_t*)task->ctx; count = MAX_SENDFILE_SIZE; if (count >= ctx->left) count = ctx->left; @@ -102,19 +102,19 @@ static int httpd_main_sendtext ( return 1; /* more work to do */ } -int qse_httpd_entasksendtext ( +int qse_httpd_entasktext ( qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_mchar_t* text) { qse_httpd_task_t task; - task_sendtext_t data; + task_text_t data; QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); data.ptr = text; data.left = qse_mbslen(text); QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); - task.init = httpd_init_sendtext; - task.main = httpd_main_sendtext; + task.init = task_init_text; + task.main = task_main_text; task.ctx = &data; return qse_httpd_entask ( @@ -127,37 +127,37 @@ int qse_httpd_entasksendtext ( /*------------------------------------------------------------------------*/ -typedef struct task_sendfmt_t task_sendfmt_t; -struct task_sendfmt_t +typedef struct task_format_t task_format_t; +struct task_format_t { qse_mchar_t* org; const qse_mchar_t* ptr; qse_size_t left; }; -static int httpd_init_sendfmt ( +static int task_init_format ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { - task_sendfmt_t* xtn = qse_httpd_gettaskxtn (httpd, task); + task_format_t* xtn = qse_httpd_gettaskxtn (httpd, task); QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn)); task->ctx = xtn; return 0; } -static void httpd_fini_sendfmt ( +static void task_fini_format ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { - task_sendfmt_t* ctx = (task_sendfmt_t*)task->ctx; + task_format_t* ctx = (task_format_t*)task->ctx; qse_httpd_freemem (httpd, ctx->org); } -static int httpd_main_sendfmt ( +static int task_main_format ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { ssize_t n; size_t count; - task_sendfmt_t* ctx = (task_sendfmt_t*)task->ctx; + task_format_t* ctx = (task_format_t*)task->ctx; count = MAX_SENDFILE_SIZE; if (count >= ctx->left) count = ctx->left; @@ -178,11 +178,11 @@ static int httpd_main_sendfmt ( return 1; /* more work to do */ } -int qse_httpd_entasksendfmt ( +int qse_httpd_entaskformat ( qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_mchar_t* fmt, ...) { qse_httpd_task_t task; - task_sendfmt_t data; + task_format_t data; va_list ap; qse_mchar_t n[2]; @@ -259,9 +259,9 @@ int qse_httpd_entasksendfmt ( data.left = l; QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); - task.init = httpd_init_sendfmt; - task.fini = httpd_fini_sendfmt; - task.main = httpd_main_sendfmt; + task.init = task_init_format; + task.fini = task_fini_format; + task.main = task_main_format; task.ctx = &data; return qse_httpd_entask ( @@ -272,53 +272,58 @@ int qse_httpd_entasksendfmt ( /*------------------------------------------------------------------------*/ -typedef struct httpd_task_sendfile_t httpd_task_sendfile_t; -struct httpd_task_sendfile_t +typedef struct task_file_t task_file_t; +struct task_file_t { - int fd; + qse_ubi_t handle; qse_foff_t left; qse_foff_t offset; }; -static int httpd_init_sendfile ( +static int task_init_file ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { - httpd_task_sendfile_t* xtn = qse_httpd_gettaskxtn (httpd, task); + task_file_t* xtn = qse_httpd_gettaskxtn (httpd, task); QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn)); task->ctx = xtn; return 0; } -static void httpd_fini_sendfile ( +static void task_fini_file ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { - httpd_task_sendfile_t* ctx = (httpd_task_sendfile_t*)task->ctx; - close (ctx->fd); + task_file_t* ctx = (task_file_t*)task->ctx; + close (ctx->handle.i); } -static int httpd_main_sendfile ( +static int task_main_file ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { ssize_t n; size_t count; - httpd_task_sendfile_t* ctx = (httpd_task_sendfile_t*)task->ctx; + task_file_t* ctx = (task_file_t*)task->ctx; count = MAX_SENDFILE_SIZE; if (count >= ctx->left) count = ctx->left; +/* TODO: more adjustment needed for OS with different sendfile semantics... */ n = sendfile ( client->handle.i, - ctx->fd, + ctx->handle.i, &ctx->offset, count ); - if (n <= -1) return -1; + if (n <= -1) return -1; /* TODO: any logging */ if (n == 0 && count > 0) { -/* TODO: .... */ - /* anything to do in this case? can this happen if the file has been truncated during transfer.... */ + /* The file could be truncated when this condition is set. + * The content-length sent in the header can't be fulfilled. + * So let's return an error here so that the main loop abort + * the connection. */ +/* TODO: any logging....??? */ + return -1; } ctx->left -= n; @@ -327,22 +332,22 @@ static int httpd_main_sendfile ( return 1; /* more work to do */ } -int qse_httpd_entasksendfile ( +int qse_httpd_entaskfile ( qse_httpd_t* httpd, qse_httpd_client_t* client, - int fd, qse_foff_t offset, qse_foff_t size) + qse_ubi_t handle, qse_foff_t offset, qse_foff_t size) { qse_httpd_task_t task; - httpd_task_sendfile_t data; + task_file_t data; QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); - data.fd = fd; + data.handle = handle; data.offset = offset; data.left = size; QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); - task.init = httpd_init_sendfile; - task.main = httpd_main_sendfile; - task.fini = httpd_fini_sendfile; + task.init = task_init_file; + task.main = task_main_file; + task.fini = task_fini_file; task.ctx = &data; return qse_httpd_entask (httpd, client, &task, QSE_SIZEOF(data)); @@ -350,6 +355,156 @@ int qse_httpd_entasksendfile ( /*------------------------------------------------------------------------*/ +#include +#include +#include + +typedef struct task_path_t task_path_t; +struct task_path_t +{ + const qse_mchar_t* name; + qse_http_range_t range; + qse_http_version_t version; +}; + +static int task_init_path ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_path_t* xtn = qse_httpd_gettaskxtn (httpd, task); + QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn)); + qse_mbscpy ((qse_mchar_t*)(xtn + 1), xtn->name); + xtn->name = (qse_mchar_t*)(xtn + 1); + task->ctx = xtn; + return 0; +} + +static int task_main_path ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_path_t* data = (task_path_t*)task->ctx; + qse_ubi_t handle; + struct stat st; + int x; + + handle.i = open (data->name, O_RDONLY); + if (handle.i <= -1) + { + const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); + x = qse_httpd_entaskformat (httpd, client, + QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + data->version.major, data->version.minor, + (int)qse_mbslen(msg) + 4, msg + ); + if (x <= -1) return -1; + } + + if (fstat (handle.i, &st) <= -1) + { + const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); + + x = qse_httpd_entaskformat (httpd, client, + QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + data->version.major, data->version.minor, + (int)qse_mbslen(msg) + 4, msg + ); + if (x <= -1) goto oops; + } + + if (st.st_size < 0) st.st_size = 0; /* can this happen? */ + + if (data->range.type != QSE_HTTP_RANGE_NONE) + { + if (data->range.type == QSE_HTTP_RANGE_SUFFIX) + { + if (data->range.to > st.st_size) data->range.to = st.st_size; + data->range.from = st.st_size - data->range.to; + data->range.to = data->range.to + data->range.from; + if (st.st_size > 0) data->range.to--; + } + + if (data->range.from >= st.st_size) + { + const qse_mchar_t* msg; + + msg = QSE_MT("Requested range not satisfiableREQUESTED RANGE NOT SATISFIABLE"); + x = qse_httpd_entaskformat (httpd, client, + QSE_MT("HTTP/%d.%d 416 Requested range not satisfiable\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + data->version.major, data->version.minor, + (int)qse_mbslen(msg) + 4, msg + ); + if (x <= -1) goto oops; + } + + if (data->range.to >= st.st_size) data->range.to = st.st_size - 1; + + x = qse_httpd_entaskformat (httpd, client, + QSE_MT("HTTP/%d.%d 206 Partial content\r\nContent-Length: %llu\r\nContent-Location: %s\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"), + data->version.major, + data->version.minor, + (unsigned long long)(data->range.to - data->range.from + 1), + data->name, + (unsigned long long)data->range.from, + (unsigned long long)data->range.to, + st.st_size + ); + if (x <= -1) goto oops; + + x = qse_httpd_entaskfile ( + httpd, client, handle, + data->range.from, + (data->range.to - data->range.from + 1) + ); + if (x <= -1) goto oops; + } + else + { +/* TODO: int64 format.... don't hard code it llu */ + + x = qse_httpd_entaskformat (httpd, client, + QSE_MT("HTTP/%d.%d 200 OK\r\nContent-Length: %llu\r\nContent-Location: %s\r\n\r\n"), + data->version.major, + data->version.minor, + (unsigned long long)st.st_size, + data->name + ); + if (x <= -1) goto oops; + + x = qse_httpd_entaskfile (httpd, client, handle, 0, st.st_size); + if (x <= -1) goto oops; + } + + return 0; + +oops: + close (handle.i); + return -1; +} + +int qse_httpd_entaskpath ( + qse_httpd_t* httpd, qse_httpd_client_t* client, + const qse_mchar_t* name, const qse_http_range_t* range, + const qse_http_version_t* verison) +{ + qse_httpd_task_t task; + task_path_t data; + + QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); + data.name = name; + if (range) data.range = *range; + else data.range.type = QSE_HTTP_RANGE_NONE; + data.version = *verison; + + QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); + task.init = task_init_path; + task.main = task_main_path; + task.ctx = &data; + + return qse_httpd_entask (httpd, client, &task, + QSE_SIZEOF(task_path_t) + qse_mbslen(name) + 1); +} + +/*------------------------------------------------------------------------*/ + #if 0 typedef struct httpd_task_cgi_t httpd_task_cgi_t; struct httpd_task_cgi_t diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 48919505..4039434e 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -23,18 +22,6 @@ qse_printf (QSE_T("HEADER OK %d[%S] %d[%S]\n"), (int)QSE_HTB_KLEN(pair), QSE_HT return QSE_HTB_WALK_FORWARD; } -static int range_not_satisfiable (qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_htre_t* req) -{ - const qse_mchar_t* msg; - - msg = QSE_MT("Requested range not satisfiableREQUESTED RANGE NOT SATISFIABLE"); - return qse_httpd_entasksendfmt (httpd, client, - QSE_MT("HTTP/%d.%d 416 Requested range not satisfiable\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), - req->version.major, req->version.minor, - (int)qse_mbslen(msg) + 4, msg - ); -} - static int handle_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req) { @@ -69,112 +56,35 @@ qse_printf (QSE_T("content = [%.*S]\n"), if (method == QSE_HTTP_GET || method == QSE_HTTP_POST) { - int fd; + const qse_mchar_t* rangestr; + qse_http_range_t range; + int x; - fd = open (qse_htre_getqpathptr(req), O_RDONLY); - if (fd <= -1) + rangestr = qse_htre_gethdrval (req, "Range"); + if (rangestr && qse_parsehttprange (rangestr, &range) <= -1) { - const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); - if (qse_httpd_entasksendfmt (httpd, client, - QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), + const qse_mchar_t* msg; + msg = QSE_MT("Requested range not satisfiableREQUESTED RANGE NOT SATISFIABLE"); + x = qse_httpd_entaskformat (httpd, client, + QSE_MT("HTTP/%d.%d 416 Requested range not satisfiable\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), req->version.major, req->version.minor, - (int)qse_mbslen(msg) + 4, msg) <= -1) goto oops; + (int)qse_mbslen(msg) + 4, msg + ); + if (x <= -1) goto oops; } - else - { - struct stat st; - if (fstat (fd, &st) <= -1) - { - const qse_mchar_t* msg = QSE_MT("Not foundREQUESTED FILE NOT FOUND"); - close (fd); - if (qse_httpd_entasksendfmt (httpd, client, - QSE_MT("HTTP/%d.%d 404 Not found\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), - req->version.major, req->version.minor, - (int)qse_mbslen(msg) + 4, msg) <= -1) goto oops; - } - else - { - const qse_mchar_t* rangestr; - qse_http_range_t range; - if (st.st_size <= 0) st.st_size = 0; - - rangestr = qse_htre_gethdrval (req, "Range"); - if (rangestr) - { - if (qse_parsehttprange (rangestr, &range) <= -1) - { - if (range_not_satisfiable (httpd, client, req) <= -1) goto oops; - } - else - { - if (range.suffix) - { - if (range.to > st.st_size) range.to = st.st_size; - range.from = st.st_size - range.to; - range.to = range.to + range.from; - if (st.st_size > 0) range.to--; - } - - if (range.from >= st.st_size) - { - if (range_not_satisfiable (httpd, client, req) <= -1) goto oops; - } - else - { - if (range.to >= st.st_size) range.to = st.st_size - 1; - - if (qse_httpd_entasksendfmt (httpd, client, - QSE_MT("HTTP/%d.%d 206 Partial content\r\nContent-Length: %llu\r\nContent-Location: %s\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"), - qse_htre_getmajorversion(req), - qse_htre_getminorversion(req), - (unsigned long long)(range.to - range.from + 1), - qse_htre_getqpathptr(req), - (unsigned long long)range.from, - (unsigned long long)range.to, - st.st_size) <= -1) - { - close (fd); - goto oops; - } - - if (qse_httpd_entasksendfile (httpd, client, fd, range.from, (range.to - range.from + 1)) <= -1) - { - close (fd); - goto oops; - } - } - } - } - else - { -/* TODO: int64 format.... don't hard code it llu */ - if (qse_httpd_entasksendfmt (httpd, client, - QSE_MT("HTTP/%d.%d 200 OK\r\nContent-Length: %llu\r\nContent-Location: %s\r\n\r\n"), - qse_htre_getmajorversion(req), - qse_htre_getminorversion(req), - (unsigned long long)st.st_size, - qse_htre_getqpathptr(req)) <= -1) - { - close (fd); - goto oops; - } - - if (qse_httpd_entasksendfile (httpd, client, fd, 0, st.st_size) <= -1) - { - close (fd); - goto oops; - } - } - - } - - } + x = qse_httpd_entaskpath ( + httpd, client, + qse_htre_getqpathptr(req), + (rangestr? &range: QSE_NULL), + qse_htre_getversion(req) + ); + if (x <= -1) goto oops; } else { const qse_mchar_t* msg = QSE_MT("Method not allowedREQUEST METHOD NOT ALLOWED"); - if (qse_httpd_entasksendfmt (httpd, client, + if (qse_httpd_entaskformat (httpd, client, QSE_MT("HTTP/%d.%d 405 Method not allowed\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), req->version.major, req->version.minor, (int)qse_mbslen(msg) + 4, msg) <= -1) goto oops;