hio/lib/http.c

784 lines
18 KiB
C
Raw Permalink Normal View History

2020-03-03 13:47:51 +00:00
/*
Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
2021-07-22 07:30:20 +00:00
#include <hio-http.h>
#include <hio-chr.h>
#include <hio-utl.h>
#include "hio-prv.h"
2020-05-01 14:00:27 +00:00
#include <time.h>
2020-03-03 13:47:51 +00:00
2021-07-22 07:30:20 +00:00
int hio_comp_http_versions (const hio_http_version_t* v1, const hio_http_version_t* v2)
2020-03-03 13:47:51 +00:00
{
if (v1->major == v2->major) return v1->minor - v2->minor;
return v1->major - v2->major;
}
2021-07-22 07:30:20 +00:00
int hio_comp_http_version_numbers (const hio_http_version_t* v1, int v2_major, int v2_minor)
{
if (v1->major == v2_major) return v1->minor - v2_minor;
return v1->major - v2_major;
}
2021-07-22 07:30:20 +00:00
const hio_bch_t* hio_http_status_to_bcstr (int code)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
const hio_bch_t* msg;
2020-03-03 13:47:51 +00:00
switch (code)
{
case HIO_HTTP_STATUS_CONTINUE: msg = "Continue"; break;
case HIO_HTTP_STATUS_SWITCH_PROTOCOL: msg = "Switching Protocols"; break;
case HIO_HTTP_STATUS_OK: msg = "OK"; break;
case HIO_HTTP_STATUS_CREATED: msg = "Created"; break;
case HIO_HTTP_STATUS_ACCEPTED: msg = "Accepted"; break;
case HIO_HTTP_STATUS_NON_AUTHORITATIVE: msg = "Non-Authoritative Information"; break;
case HIO_HTTP_STATUS_NO_CONTENT: msg = "No Content"; break;
case HIO_HTTP_STATUS_RESET_CONTENT: msg = "Reset Content"; break;
case HIO_HTTP_STATUS_PARTIAL_CONTENT: msg = "Partial Content"; break;
2020-03-03 13:47:51 +00:00
case 300: msg = "Multiple Choices"; break;
case HIO_HTTP_STATUS_MOVED_PERMANENTLY: msg = "Moved Permanently"; break;
case HIO_HTTP_STATUS_MOVED_TEMPORARILY: msg = "Moved Temporarily"; break;
2020-03-03 13:47:51 +00:00
case 303: msg = "See Other"; break;
case HIO_HTTP_STATUS_NOT_MODIFIED: msg = "Not Modified"; break;
2020-03-03 13:47:51 +00:00
case 305: msg = "Use Proxy"; break;
case HIO_HTTP_STATUS_TEMPORARY_REDIRECT: msg = "Temporary Redirect"; break;
case HIO_HTTP_STATUS_PERMANENT_REDIRECT: msg = "Permanent Redirect"; break;
2020-03-03 13:47:51 +00:00
case HIO_HTTP_STATUS_BAD_REQUEST: msg = "Bad Request"; break;
case HIO_HTTP_STATUS_UNAUTHORIZED: msg = "Unauthorized"; break;
case HIO_HTTP_STATUS_PAYMENT_REQUIRED: msg = "Payment Required"; break;
case HIO_HTTP_STATUS_FORBIDDEN: msg = "Forbidden"; break;
case HIO_HTTP_STATUS_NOT_FOUND: msg = "Not Found"; break;
case HIO_HTTP_STATUS_METHOD_NOT_ALLOWED: msg = "Method Not Allowed"; break;
case HIO_HTTP_STATUS_NOT_ACCEPTABLE: msg = "Not Acceptable"; break;
case HIO_HTTP_STATUS_PROXY_AUTH_REQUIRED: msg = "Proxy Authentication Required"; break;
case HIO_HTTP_STATUS_REQUEST_TIMEOUT: msg = "Request Timeout"; break;
2020-03-03 13:47:51 +00:00
case 409: msg = "Conflict"; break;
case 410: msg = "Gone"; break;
case HIO_HTTP_STATUS_LENGTH_REQUIRED: msg = "Length Required"; break;
2020-03-03 13:47:51 +00:00
case 412: msg = "Precondition Failed"; break;
case 413: msg = "Request Entity Too Large"; break;
case 414: msg = "Request-URI Too Long"; break;
case 415: msg = "Unsupported Media Type"; break;
case HIO_HTTP_STATUS_RANGE_NOT_SATISFIABLE: msg = "Requested Range Not Satisfiable"; break;
case HIO_HTTP_STATUS_EXPECTATION_FAILED: msg = "Expectation Failed"; break;
2020-03-03 13:47:51 +00:00
case 426: msg = "Upgrade Required"; break;
case 428: msg = "Precondition Required"; break;
case 429: msg = "Too Many Requests"; break;
case 431: msg = "Request Header Fields Too Large"; break;
case HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR: msg = "Internal Server Error"; break;
case HIO_HTTP_STATUS_NOT_IMPLEMENTED: msg = "Not Implemented"; break;
case HIO_HTTP_STATUS_BAD_GATEWAY: msg = "Bad Gateway"; break;
case HIO_HTTP_STATUS_SERVICE_UNAVAILABLE: msg = "Service Unavailable"; break;
case HIO_HTTP_STATUS_VERSION_NOT_SUPPORTED: msg = "HTTP Version Not Supported"; break;
2020-03-03 13:47:51 +00:00
default: msg = "Unknown"; break;
2020-03-03 13:47:51 +00:00
}
return msg;
}
2021-07-22 07:30:20 +00:00
const hio_bch_t* hio_http_method_to_bcstr (hio_http_method_t type)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
/* keep this table in the same order as hio_httpd_method_t enumerators */
static hio_bch_t* names[] =
2020-03-03 13:47:51 +00:00
{
"OTHER",
"HEAD",
"GET",
"POST",
"PUT",
"DELETE",
2020-06-02 09:43:55 +00:00
"PATCH",
2020-03-03 13:47:51 +00:00
"OPTIONS",
"TRACE",
"CONNECT"
};
2020-03-03 13:47:51 +00:00
2021-07-22 07:30:20 +00:00
return (type < 0 || type >= HIO_COUNTOF(names))? HIO_NULL: names[type];
2020-03-03 13:47:51 +00:00
}
struct mtab_t
{
2021-07-22 07:30:20 +00:00
const hio_bch_t* name;
hio_http_method_t type;
2020-03-03 13:47:51 +00:00
};
static struct mtab_t mtab[] =
{
/* keep this table sorted by name for binary search */
2021-07-22 07:30:20 +00:00
{ "CONNECT", HIO_HTTP_CONNECT },
{ "DELETE", HIO_HTTP_DELETE },
{ "GET", HIO_HTTP_GET },
{ "HEAD", HIO_HTTP_HEAD },
{ "OPTIONS", HIO_HTTP_OPTIONS },
{ "PATCH", HIO_HTTP_PATCH },
{ "POST", HIO_HTTP_POST },
{ "PUT", HIO_HTTP_PUT },
{ "TRACE", HIO_HTTP_TRACE }
2020-03-03 13:47:51 +00:00
};
2021-07-22 07:30:20 +00:00
hio_http_method_t hio_bcstr_to_http_method (const hio_bch_t* name)
2020-03-03 13:47:51 +00:00
{
/* perform binary search */
/* declaring left, right, mid to be of int is ok
* because we know mtab is small enough. */
2021-07-22 07:30:20 +00:00
int left = 0, right = HIO_COUNTOF(mtab) - 1, mid;
2020-03-03 13:47:51 +00:00
while (left <= right)
{
int n;
struct mtab_t* entry;
/*mid = (left + right) / 2;*/
mid = left + (right - left) / 2;
entry = &mtab[mid];
2021-07-22 07:30:20 +00:00
n = hio_comp_bcstr(name, entry->name, 1);
if (n < 0)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
/* if left, right, mid were of hio_oow_t,
* you would need the following line.
2020-03-03 13:47:51 +00:00
if (mid == 0) break;
*/
right = mid - 1;
}
else if (n > 0) left = mid + 1;
else return entry->type;
}
2021-07-22 07:30:20 +00:00
return HIO_HTTP_OTHER;
2020-03-03 13:47:51 +00:00
}
2021-07-22 07:30:20 +00:00
hio_http_method_t hio_bchars_to_http_method (const hio_bch_t* nameptr, hio_oow_t namelen)
2020-03-03 13:47:51 +00:00
{
/* perform binary search */
/* declaring left, right, mid to be of int is ok
* because we know mtab is small enough. */
2021-07-22 07:30:20 +00:00
int left = 0, right = HIO_COUNTOF(mtab) - 1, mid;
2020-03-03 13:47:51 +00:00
while (left <= right)
{
int n;
struct mtab_t* entry;
/*mid = (left + right) / 2;*/
mid = left + (right - left) / 2;
entry = &mtab[mid];
2021-07-22 07:30:20 +00:00
n = hio_comp_bchars_bcstr(nameptr, namelen, entry->name, 1);
if (n < 0)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
/* if left, right, mid were of hio_oow_t,
* you would need the following line.
2020-03-03 13:47:51 +00:00
if (mid == 0) break;
*/
right = mid - 1;
}
else if (n > 0) left = mid + 1;
else return entry->type;
}
2021-07-22 07:30:20 +00:00
return HIO_HTTP_OTHER;
2020-03-03 13:47:51 +00:00
}
2021-07-22 07:30:20 +00:00
int hio_parse_http_range_bcstr (const hio_bch_t* str, hio_http_range_t* range)
2020-03-03 13:47:51 +00:00
{
/* NOTE: this function does not support a range set
2020-03-03 13:47:51 +00:00
* like bytes=1-20,30-50 */
2021-07-22 07:30:20 +00:00
hio_foff_t from, to;
int type = HIO_HTTP_RANGE_PROPER;
2020-03-03 13:47:51 +00:00
if (str[0] != 'b' || str[1] != 'y' || str[2] != 't' || str[3] != 'e' || str[4] != 's' || str[5] != '=') return -1;
2020-03-03 13:47:51 +00:00
str += 6;
2020-07-13 10:01:42 +00:00
from = to = 0;
2021-07-22 07:30:20 +00:00
if (hio_is_bch_digit(*str))
2020-03-03 13:47:51 +00:00
{
do
{
from = from * 10 + (*str - '0');
str++;
}
2021-07-22 07:30:20 +00:00
while (hio_is_bch_digit(*str));
2020-03-03 13:47:51 +00:00
}
2021-07-22 07:30:20 +00:00
else type = HIO_HTTP_RANGE_SUFFIX;
2020-03-03 13:47:51 +00:00
if (*str != '-') return -1;
str++;
2021-07-22 07:30:20 +00:00
if (hio_is_bch_digit(*str))
2020-03-03 13:47:51 +00:00
{
to = 0;
do
{
to = to * 10 + (*str - '0');
str++;
}
2021-07-22 07:30:20 +00:00
while (hio_is_bch_digit(*str));
2020-03-03 13:47:51 +00:00
2020-07-13 10:01:42 +00:00
if (from > to) return -1;
}
2021-07-22 07:30:20 +00:00
else type = HIO_HTTP_RANGE_PREFIX;
2020-03-03 13:47:51 +00:00
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
2020-07-15 10:30:12 +00:00
if (*str != '\0') return -1;
2020-03-03 13:47:51 +00:00
range->type = type;
range->from = from;
range->to = to;
return 0;
}
typedef struct mname_t mname_t;
struct mname_t
{
2021-07-22 07:30:20 +00:00
const hio_bch_t* s;
const hio_bch_t* l;
2020-03-03 13:47:51 +00:00
};
2020-03-03 13:47:51 +00:00
static mname_t wday_name[] =
{
{ "Sun", "Sunday" },
{ "Mon", "Monday" },
{ "Tue", "Tuesday" },
{ "Wed", "Wednesday" },
{ "Thu", "Thursday" },
{ "Fri", "Friday" },
{ "Sat", "Saturday" }
};
static mname_t mon_name[] =
{
{ "Jan", "January" },
{ "Feb", "February" },
{ "Mar", "March" },
{ "Apr", "April" },
{ "May", "May" },
{ "Jun", "June" },
{ "Jul", "July" },
{ "Aug", "August" },
{ "Sep", "September" },
{ "Oct", "October" },
{ "Nov", "November" },
{ "Dec", "December" }
};
2021-07-22 07:30:20 +00:00
int hio_parse_http_time_bcstr (const hio_bch_t* str, hio_ntime_t* nt)
2020-03-03 13:47:51 +00:00
{
2020-05-01 14:00:27 +00:00
struct tm bt;
2021-07-22 07:30:20 +00:00
const hio_bch_t* word;
hio_oow_t wlen, i;
2020-03-03 13:47:51 +00:00
/* TODO: support more formats */
2021-07-22 07:30:20 +00:00
HIO_MEMSET (&bt, 0, HIO_SIZEOF(bt));
2020-03-03 13:47:51 +00:00
/* weekday */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
for (word = str; hio_is_bch_alpha(*str); str++);
2020-03-03 13:47:51 +00:00
wlen = str - word;
2021-07-22 07:30:20 +00:00
for (i = 0; i < HIO_COUNTOF(wday_name); i++)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
if (hio_comp_bchars_bcstr(word, wlen, wday_name[i].s, 1) == 0)
2020-03-03 13:47:51 +00:00
{
2020-05-01 14:00:27 +00:00
bt.tm_wday = i;
2020-03-03 13:47:51 +00:00
break;
}
}
2021-07-22 07:30:20 +00:00
if (i >= HIO_COUNTOF(wday_name)) return -1;
2020-03-03 13:47:51 +00:00
/* comma - i'm just loose as i don't care if it doesn't exist */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
2020-03-03 13:47:51 +00:00
if (*str == ',') str++;
/* day */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
if (!hio_is_bch_digit(*str)) return -1;
do bt.tm_mday = bt.tm_mday * 10 + *str++ - '0'; while (hio_is_bch_digit(*str));
2020-03-03 13:47:51 +00:00
/* month */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
for (word = str; hio_is_bch_alpha(*str); str++);
2020-03-03 13:47:51 +00:00
wlen = str - word;
2021-07-22 07:30:20 +00:00
for (i = 0; i < HIO_COUNTOF(mon_name); i++)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
if (hio_comp_bchars_bcstr(word, wlen, mon_name[i].s, 1) == 0)
2020-03-03 13:47:51 +00:00
{
2020-05-01 14:00:27 +00:00
bt.tm_mon = i;
2020-03-03 13:47:51 +00:00
break;
}
}
2021-07-22 07:30:20 +00:00
if (i >= HIO_COUNTOF(mon_name)) return -1;
2020-03-03 13:47:51 +00:00
/* year */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
if (!hio_is_bch_digit(*str)) return -1;
do bt.tm_year = bt.tm_year * 10 + *str++ - '0'; while (hio_is_bch_digit(*str));
2020-05-01 14:00:27 +00:00
bt.tm_year -= 1900;
2020-03-03 13:47:51 +00:00
/* hour */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
if (!hio_is_bch_digit(*str)) return -1;
do bt.tm_hour = bt.tm_hour * 10 + *str++ - '0'; while (hio_is_bch_digit(*str));
2020-03-03 13:47:51 +00:00
if (*str != ':') return -1;
str++;
/* min */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
if (!hio_is_bch_digit(*str)) return -1;
do bt.tm_min = bt.tm_min * 10 + *str++ - '0'; while (hio_is_bch_digit(*str));
2020-03-03 13:47:51 +00:00
if (*str != ':') return -1;
str++;
/* sec */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
if (!hio_is_bch_digit(*str)) return -1;
do bt.tm_sec = bt.tm_sec * 10 + *str++ - '0'; while (hio_is_bch_digit(*str));
2020-03-03 13:47:51 +00:00
/* GMT */
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
for (word = str; hio_is_bch_alpha(*str); str++);
2020-03-03 13:47:51 +00:00
wlen = str - word;
2021-07-22 07:30:20 +00:00
if (hio_comp_bchars_bcstr(word, wlen, "GMT", 1) != 0) return -1;
2020-03-03 13:47:51 +00:00
2021-07-22 07:30:20 +00:00
while (hio_is_bch_space(*str)) str++;
2020-03-03 13:47:51 +00:00
if (*str != '\0') return -1;
2020-05-01 14:00:27 +00:00
nt->sec = timegm(&bt);
nt->nsec = 0;
return 0;
2020-03-03 13:47:51 +00:00
}
2021-07-22 07:30:20 +00:00
hio_bch_t* hio_fmt_http_time_to_bcstr (const hio_ntime_t* nt, hio_bch_t* buf, hio_oow_t bufsz)
2020-03-03 13:47:51 +00:00
{
2020-05-01 14:00:27 +00:00
time_t t;
struct tm bt;
t = nt->sec;
2021-07-22 07:30:20 +00:00
gmtime_r (&t, &bt); /* TODO: create hio_sys_gmtime() and make it system dependent */
2020-05-01 14:00:27 +00:00
hio_fmttobcstr (HIO_NULL, buf, bufsz,
2020-05-01 14:00:27 +00:00
"%hs, %d %hs %d %02d:%02d:%02d GMT",
wday_name[bt.tm_wday].s,
bt.tm_mday,
mon_name[bt.tm_mon].s,
bt.tm_year + 1900,
bt.tm_hour, bt.tm_min, bt.tm_sec
2020-03-03 13:47:51 +00:00
);
return buf;
}
2021-07-22 07:30:20 +00:00
int hio_is_perenced_http_bcstr (const hio_bch_t* str)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
const hio_bch_t* p = str;
2020-03-03 13:47:51 +00:00
while (*p != '\0')
{
if (*p == '%' && *(p + 1) != '\0' && *(p + 2) != '\0')
{
2021-07-22 07:30:20 +00:00
int q = HIO_XDIGIT_TO_NUM(*(p + 1));
2020-03-03 13:47:51 +00:00
if (q >= 0)
{
/* return true if the first valid percent-encoded sequence is found */
2021-07-22 07:30:20 +00:00
int w = HIO_XDIGIT_TO_NUM(*(p + 2));
if (w >= 0) return 1;
2020-03-03 13:47:51 +00:00
}
}
p++;
}
return 1;
}
2021-07-22 07:30:20 +00:00
hio_oow_t hio_perdec_http_bcstr (const hio_bch_t* str, hio_bch_t* buf, hio_oow_t* ndecs)
2020-03-03 13:47:51 +00:00
{
2021-07-22 07:30:20 +00:00
const hio_bch_t* p = str;
hio_bch_t* out = buf;
hio_oow_t dec_count = 0;
2020-03-03 13:47:51 +00:00
while (*p != '\0')
{
if (*p == '%' && *(p + 1) != '\0' && *(p + 2) != '\0')
{
2021-07-22 07:30:20 +00:00
int q = HIO_XDIGIT_TO_NUM(*(p + 1));
2020-03-03 13:47:51 +00:00
if (q >= 0)
{
2021-07-22 07:30:20 +00:00
int w = HIO_XDIGIT_TO_NUM(*(p + 2));
2020-03-03 13:47:51 +00:00
if (w >= 0)
{
/* we don't care if it contains a null character */
*out++ = ((q << 4) + w);
p += 3;
dec_count++;
continue;
}
}
}
*out++ = *p++;
}
*out = '\0';
if (ndecs) *ndecs = dec_count;
return out - buf;
}
2021-07-22 07:30:20 +00:00
hio_oow_t hio_perdec_http_bcs (const hio_bcs_t* str, hio_bch_t* buf, hio_oow_t* ndecs)
2020-05-27 02:32:51 +00:00
{
2021-07-22 07:30:20 +00:00
const hio_bch_t* p = str->ptr;
const hio_bch_t* end = str->ptr + str->len;
hio_bch_t* out = buf;
hio_oow_t dec_count = 0;
2020-05-27 02:32:51 +00:00
while (p < end)
{
if (*p == '%' && (p + 2) < end)
{
2021-07-22 07:30:20 +00:00
int q = HIO_XDIGIT_TO_NUM(*(p + 1));
2020-05-27 02:32:51 +00:00
if (q >= 0)
{
2021-07-22 07:30:20 +00:00
int w = HIO_XDIGIT_TO_NUM(*(p + 2));
2020-05-27 02:32:51 +00:00
if (w >= 0)
{
/* we don't care if it contains a null character */
*out++ = ((q << 4) + w);
p += 3;
dec_count++;
continue;
}
}
}
*out++ = *p++;
}
/* [NOTE] this function deesn't insert '\0' at the end */
if (ndecs) *ndecs = dec_count;
return out - buf;
}
2020-03-03 13:47:51 +00:00
#define IS_UNRESERVED(c) \
(((c) >= 'A' && (c) <= 'Z') || \
((c) >= 'a' && (c) <= 'z') || \
((c) >= '0' && (c) <= '9') || \
(c) == '-' || (c) == '_' || \
(c) == '.' || (c) == '~')
#define TO_HEX(v) ("0123456789ABCDEF"[(v) & 15])
hio_oow_t hio_perenc_http_bchars (const hio_bch_t* str, hio_oow_t sln, hio_bch_t* buf, hio_oow_t len, int opt)
2020-03-03 13:47:51 +00:00
{
const hio_bch_t* ptr, * end = str + sln;
2021-07-22 07:30:20 +00:00
hio_bch_t* out = buf;
hio_bch_t slash;
hio_oow_t reqlen = 0;
2020-03-03 13:47:51 +00:00
slash = (opt & HIO_PERENC_HTTP_KEEP_SLASH)? '/': '\0';
2020-03-03 13:47:51 +00:00
for (ptr = str; ptr < end; ptr++)
2020-03-03 13:47:51 +00:00
{
reqlen += (IS_UNRESERVED(*ptr) || *ptr == slash)? 1: 3;
2020-03-03 13:47:51 +00:00
}
if (len >= reqlen)
2020-03-03 13:47:51 +00:00
{
ptr = str;
while (ptr < end)
2020-03-03 13:47:51 +00:00
{
if (IS_UNRESERVED(*ptr) || *ptr == slash) *out++ = *ptr;
2020-03-03 13:47:51 +00:00
else
{
*out++ = '%';
*out++ = TO_HEX(*ptr >> 4);
*out++ = TO_HEX(*ptr & 15);
2020-03-03 13:47:51 +00:00
}
ptr++;
2020-03-03 13:47:51 +00:00
}
}
return reqlen;
2020-03-03 13:47:51 +00:00
}
hio_oow_t hio_perenc_http_bcstr (const hio_bch_t* str, hio_bch_t* buf, hio_oow_t len, int opt)
2020-03-03 13:47:51 +00:00
{
const hio_bch_t* ptr = str;
hio_bch_t* out = buf;
hio_bch_t slash;
hio_oow_t reqlen = 0;
slash = (opt & HIO_PERENC_HTTP_KEEP_SLASH)? '/': '\0';
for (ptr = str; *ptr != '\0'; ptr++)
2020-03-03 13:47:51 +00:00
{
reqlen += (IS_UNRESERVED(*ptr) || *ptr == slash)? 1: 3;
2020-03-03 13:47:51 +00:00
}
if (len > reqlen)
2020-03-03 13:47:51 +00:00
{
ptr = str;
while (*ptr != '\0')
2020-03-03 13:47:51 +00:00
{
if (IS_UNRESERVED(*ptr) || *ptr == slash) *out++ = *ptr;
else
{
*out++ = '%';
*out++ = TO_HEX(*ptr >> 4);
*out++ = TO_HEX(*ptr & 15);
}
ptr++;
2020-03-03 13:47:51 +00:00
}
*out = '\0';
}
2023-01-02 16:00:38 +00:00
else reqlen++; /* to cater for the terminating null in the return value */
2020-03-03 13:47:51 +00:00
return reqlen;
2020-03-03 13:47:51 +00:00
}
2020-05-27 02:32:51 +00:00
2021-07-22 07:30:20 +00:00
int hio_scan_http_qparam (hio_bch_t* qparam, int (*qparamcb) (hio_bcs_t* key, hio_bcs_t* val, void* ctx), void* ctx)
{
2021-07-22 07:30:20 +00:00
hio_bcs_t key, val;
hio_bch_t* p, * end;
2020-05-27 02:32:51 +00:00
p = qparam;
2021-07-22 07:30:20 +00:00
end = p + hio_count_bcstr(qparam);
2020-05-27 02:32:51 +00:00
key.ptr = p; key.len = 0;
2021-07-22 07:30:20 +00:00
val.ptr = HIO_NULL; val.len = 0;
do
{
if (p >= end || *p == '&' || *p == ';')
{
2020-05-27 02:32:51 +00:00
if (val.ptr)
{
2020-05-27 02:32:51 +00:00
val.len = p - val.ptr;
}
else
{
key.len = p - key.ptr;
if (key.len == 0) goto next_octet; /* both key and value are empty. we don't need to do anything */
}
/* set request parameter string callback before scanning */
2020-05-27 02:32:51 +00:00
if (qparamcb(&key, &val, ctx) <= -1) return -1;
next_octet:
if (p >= end) break;
p++;
2020-05-27 02:32:51 +00:00
key.ptr = p; key.len = 0;
2021-07-22 07:30:20 +00:00
val.ptr = HIO_NULL; val.len = 0;
}
else if (*p == '=')
{
2020-05-27 02:32:51 +00:00
if (!val.ptr)
{
key.len = p - key.ptr;
val.ptr = ++p;
/*val.len = 0; */
}
else
{
p++;
}
}
else
{
2020-05-27 02:32:51 +00:00
p++;
}
}
while (1);
return 0;
}
hio_oow_t hio_escape_html_bchars (const hio_bch_t* str, hio_oow_t sln, hio_bch_t* buf, hio_oow_t len)
{
hio_bch_t* ptr;
const hio_bch_t* end = str + sln;
hio_oow_t reqlen = 0;
for (ptr = (hio_bch_t*)str; ptr < end; ptr++)
{
switch (*ptr)
{
case '<':
case '>':
reqlen += 4;
break;
case '&':
reqlen += 5;
break;
default:
reqlen++;
break;
}
}
if (len >= reqlen)
{
/* the buffer is large enough */
ptr = buf;
while (str < end)
{
switch (*str)
{
case '<':
*ptr++ = '&'; *ptr++ = 'l'; *ptr++ = 't'; *ptr++ = ';';
break;
case '>':
*ptr++ = '&'; *ptr++ = 'g'; *ptr++ = 't'; *ptr++ = ';';
break;
case '&':
*ptr++ = '&'; *ptr++ = 'a'; *ptr++ = 'm'; *ptr++ = 'p'; *ptr++ = ';';
break;
default:
*ptr++ = *str;
break;
}
str++;
}
}
/* NOTE no null termination */
return reqlen;
}
hio_oow_t hio_escape_html_bcstr (const hio_bch_t* str, hio_bch_t* buf, hio_oow_t len)
{
hio_bch_t* ptr;
hio_oow_t reqlen = 0;
for (ptr = (hio_bch_t*)str; *ptr != '\0'; ptr++)
{
switch (*ptr)
{
case '<':
case '>':
reqlen += 4;
break;
case '&':
reqlen += 5;
break;
default:
reqlen++;
break;
}
}
if (len > reqlen)
{
/* the buffer is large enough */
ptr = buf;
while (*str != '\0')
{
switch (*str)
{
case '<':
*ptr++ = '&'; *ptr++ = 'l'; *ptr++ = 't'; *ptr++ = ';';
break;
case '>':
*ptr++ = '&'; *ptr++ = 'g'; *ptr++ = 't'; *ptr++ = ';';
break;
case '&':
*ptr++ = '&'; *ptr++ = 'a'; *ptr++ = 'm'; *ptr++ = 'p'; *ptr++ = ';';
break;
default:
*ptr++ = *str;
break;
}
str++;
}
*ptr = '\0';
}
2023-01-02 16:00:38 +00:00
else reqlen++; /* to cater for the terminating null in the return value */
return reqlen;
}
int hio_parse_http_status_header_value (const hio_bch_t* status_value, int* status_code, const hio_bch_t** status_desc)
{
const hio_bch_t* begptr, * endptr;
int v_is_sober = 1;
hio_intmax_t v = 0;
hio_oow_t code_len;
hio_oow_t desc_len;
endptr = status_value;
while (hio_is_bch_space(*endptr)) endptr++;
begptr = endptr;
while (hio_is_bch_digit(*endptr))
{
v = v * 10 + (*endptr - '0');
if (v > HIO_TYPE_MAX(int)) v_is_sober = 0;
endptr++;
}
code_len = endptr - begptr;
while (hio_is_bch_space(*endptr)) endptr++;
begptr = endptr;
while (*endptr != '\0') endptr++;
desc_len = endptr - begptr;
if (v_is_sober)
{
*status_code = v;
*status_desc = HIO_NULL;
if (code_len > 0 && desc_len > 0)
{
/* the status line could be simply "Status: 302" or more verbose like "Status: 302 Moved"
* the value may contain more than numbers */
*status_desc = begptr;
}
return 0;
}
/* no sober - do not update *status_code and *status_desc */
return -1; /* not sober */
}