mirror of
https://github.com/eddyem/snippets_library.git
synced 2025-12-06 02:35:20 +03:00
started v.0.3.3 - more functionality for sockets
This commit is contained in:
parent
df1f2193ee
commit
a445aec8da
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.9)
|
cmake_minimum_required(VERSION 4.0)
|
||||||
set(PROJ usefull_macros)
|
set(PROJ usefull_macros)
|
||||||
set(MINOR_VERSION "2")
|
set(MINOR_VERSION "3")
|
||||||
set(MID_VERSION "3")
|
set(MID_VERSION "3")
|
||||||
set(MAJOR_VERSION "0")
|
set(MAJOR_VERSION "0")
|
||||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||||
|
|||||||
16
Changelog
16
Changelog
@ -1,10 +1,4 @@
|
|||||||
Wed Dec 23 17:22:39 MSK 2020
|
Dec 16 2024
|
||||||
|
|
||||||
VERSION 0.1.1:
|
|
||||||
add tty_timeout()
|
|
||||||
fix read_tty() for disconnect
|
|
||||||
add logging
|
|
||||||
add sl_libversion()
|
|
||||||
|
|
||||||
VERSION 0.2.1:
|
VERSION 0.2.1:
|
||||||
RENAME:
|
RENAME:
|
||||||
@ -54,3 +48,11 @@ Remove "bool" type (change to int)
|
|||||||
FIXED bug with several only long options with same shortopt mark (like 0)
|
FIXED bug with several only long options with same shortopt mark (like 0)
|
||||||
Add simple config files, so you can move some of your parameters settings to it
|
Add simple config files, so you can move some of your parameters settings to it
|
||||||
|
|
||||||
|
|
||||||
|
Wed Dec 23 17:22:39 MSK 2020
|
||||||
|
|
||||||
|
VERSION 0.1.1:
|
||||||
|
add tty_timeout()
|
||||||
|
fix read_tty() for disconnect
|
||||||
|
add logging
|
||||||
|
add sl_libversion()
|
||||||
|
|||||||
37
Readme.rus
Normal file
37
Readme.rus
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<a href=https://github.com/eddyem/snippets_library>Репозиторий с кодом</a>.
|
||||||
|
Сначала избавился от нескольких багов. Потом решил добавить сетевой функционал. Ну, а коль сеть — то и кольцевой буфер нужен. В общем, теперь там — не просто макросы и функционал, который в 99% случаев нужен (тот же разбор параметров командной строки), но и дополнительные облегчалки.
|
||||||
|
<lj-cut text="Подробней">
|
||||||
|
В релизе 0.2.1 код подвергся рефакторингу, многие функции переименованы, чтобы было единообразие: почти все функции и т.п. имеют префикс <tt>sl_</tt>; типы-перечисления имеют суффикс <tt>_e</tt>, типы данных — суффикс <tt>_t</tt>. Без префикса я оставил лишь функции <tt>red</tt> и <tt>green</tt> для цветного вывода сообщений. И многие макросы лень было переименовывать.
|
||||||
|
|
||||||
|
<p><b>"Старое":</b></p>
|
||||||
|
Для использования функционала в самом начале <tt>main()</tt> нужно выполнить функцию <tt>sl_init</tt>. Для работы с терминалом ввода в режиме "без эха" — <tt>sl_setup_con()</tt> (и не забыть в конце и в функции <tt>signals()</tt> вызвать <tt>sl_restore_con()</tt>, иначе по завершению придется ручками вводить <tt>reset</tt>). Функции <tt>sl_read_con()</tt> и <tt>sl_getchar()</tt> дают, соответственно, неблокирующее и блокирующее чтение символа, введенного пользователем.
|
||||||
|
Осталось и упрощение работы с <tt>mmap</tt>: <tt>sl_mmap()</tt> и <tt>sl_munmap()</tt>.
|
||||||
|
<tt>sl_random_seed()</tt> позволяет при помощи <tt>/dev/random</tt> сгенерировать случайную затравку для использования в ГСЧ. <tt>sl_libversion()</tt> позволяет получить строку с текущей версией библиотеки.
|
||||||
|
Для работы с gettext теперь нужно определить макрос <tt>GETTEXT</tt>, в этом случае макрос <tt>_()</tt> будет вызывать <tt>gettext()</tt>.
|
||||||
|
Функции работы с блочными устройствами не просто переименовались: по умолчанию я перешел на termios2. Если нужно собрать библиотеку с поддержкой "классической" termios со всеми ее убогими косяками, нужно определить макрос <tt>SL_USE_OLD_TTY</tt> (в этом случае еще и будет доступна функция <tt>sl_tty_convspd()</tt> для преобразования скорости интерфейса из числа в макрос). А остальные функции — как и были: <tt>sl_tty_new()</tt> для выделения памяти и инициализации структур; <tt>sl_tty_open()</tt> — открыть порт; <tt>sl_tty_read()</tt> и <tt>sl_tty_write()</tt> для чтения/записи; <tt>sl_tty_tmout()</tt> позволяет задать таймаут для <tt>select()</tt> в микросекундах. <tt>sl_tty_close()</tt> — закрыть устройство и очистить структуры данных.
|
||||||
|
Логгирование имеет "умолчальный" глобальный лог (<tt>sl_globlog</tt>), для которого нарисованы макросы вроде <tt>LOGERR()</tt>, <tt>LOGERRADD()</tt> и т.п. (все с суффиксом <tt>ADD</tt> добавляют текст без записи timestamp и уровня сообщения — если нужно вывести несколько строк текста; но лучше ими не пользоваться часто, особенно в многопоточных/многопроцессных приложениях). Чтобы логи писались вменяемо в многопоточных/многопроцессных, пришлось лог-файл каждый раз для записи открывать, блокировать, потом писать, закрывать и разблокировать. Чтобы избежать дедлоков или слишком долгих ожиданий, установлен "гвоздями прибитый" таймаут в 0.1с: если за это время заблокировать файл не получится, то отменяем запись и возвращаем пользователю 0; иначе возвращаем количество записанных символов. "Глобальный" лог-файл активируется при помощи макроса <tt>OPENLOG(nm, lvl, prefix)</tt> (имя, уровень, флаг добавления "префикса": строчки с расшифровкой уровня сообщения, например, [WARN] или [ERR]). Функции <tt>sl_createlog()</tt>, <tt>sl_deletelog()</tt> и <tt>sl_putlogt()</tt> позволяют активировать структуру-лог (и создать файл, если надо), удалить структуру (сам файл не удаляется) и что-нибудь туда записать/дописать.
|
||||||
|
В работе с аргументами командной строки я исправил один баг, связанный с выводом справки по "только длинным" аргументам, у которых в качестве "короткой альтернативы" проставлены одинаковые числа. Ну и была проблема с опцией "-?", которая криво обрабатывалась (т.к. я пользуюсь <tt>getopt_long</tt>). В остальном все осталось, как было: поддерживаются целочисленные аргументы (причем, arg_none означает, что к заданной переменной будет добавляться 1 каждый раз, как встречается этот флаг — удобно для задания уровня отображения как, например, <tt>-vvvv</tt>), long long, double, float, string и "аргумент-функция" (т.е. вместо присвоения значения будет выполняться заданная функция — например, чтобы парсить свои вложенные аргументы). Все также для каждого флага можно указать, что он не имеет аргументов, имеет обязательный, необязательный или множественный (в этом случае переменной-целью должен быть указатель соответствующего типа: при разборе получится NULL-terminated массив со всеми введенными пользователем данными (удобно, например, когда тебе нужно указать несколько файлов для считывания из них однообразной информации и т.п.). Поддерживаются "подопции" (когда за флагом следует строка вида "so1=par1;so2=par2..."). <tt>sl_showhelp()</tt> позволяет вызвать справку по всем опциям или только одной. Она же вызывается автоматом, если пользователь что-то не то введет. Чтобы поменять форматную строку справки, используется <tt>sl_helpstring()</tt> (там должен быть один-единственный "%s", куда вставится имя запускаемого файла). <tt>sl_parseargs()</tt> нужно запускать сразу после инициализации, и дальше уже пользоваться своей структурой с параметрами.
|
||||||
|
Для проверки, не запущен ли второй экземпляр, есть функция <tt>sl_check4running()</tt>. Если найден процесс, запускается слабая функция <tt>sl_iffound_deflt()</tt>, которую под свои нужды можно изменить. <tt>sl_getPSname()</tt> парсит <tt>/proc</tt> и выдает имя процесса с заданным PID.
|
||||||
|
FIFO/LIFO также остались без изменений: <tt>sl_list_push()</tt> и <tt>sl_list_push_tail()</tt> позволяют воткнуть запись в начало или хвост списка, а <tt>sl_list_pop()</tt> — извлечь последнюю запись.
|
||||||
|
|
||||||
|
<p><b>"Новое":</b></p>
|
||||||
|
<tt>sl_mem_avail()</tt> позволяет определить доступный объем ОЗУ. <tt>sl_omitspaces()</tt> и <tt>sl_omitspacesr()</tt> — удалить начальные и конечные пробелы. Помимо <tt>sl_str2d()</tt> ("безопасное" преобразование строки в double) я добавил <tt>sl_str2ll()</tt> и <tt>sl_str2i()</tt> — long long и int, соответственно.
|
||||||
|
Для проверки, можно ли в данный дескриптор писать или читать из него без блокировки, добавил функции <tt>sl_canread()</tt> и <tt>sl_canwrite()</tt>.
|
||||||
|
Поддержку конфигурационных файлов тоже добавил в библиотеку. В файле все данные должны быть в формате "параметр = значение" или просто "параметр". Допускается наличие пустых строк и комментариев (все, что за символом "#" в строке). "Гвоздями" прибил максимальную длину параметра (<tt>SL_KEY_LEN</tt>, 32 байта) и значения (<tt>SL_VAL_LEN</tt>, 128 байт). Работа с флагами похожа на работу с аргументами командной строки. <tt>sl_get_keyval()</tt> чистит лишние пробелы и комментарии, выделяя непосредственно строчку "параметр [= значение]" и помещая ее в отдельные аргументы (возвращает 0 если ничего в строке не найдено, 1 — если только параметр, 2 — если и параметр, и значение); эта функция используется и в обработке запросов по сети. <tt>sl_print_opts()</tt> печатает изменяемые параметры (или все, если задан флаг) с комментариями. <tt>sl_conf_readopts()</tt> читает конфигурационный файл. Опции задаются полностью аналогично опциям командной строки, что позволяет печатать сопровождающий текст помимо флага и его значения.
|
||||||
|
Для работы с кольцевым буфером есть следующие функции: <tt>sl_RB_new()</tt> и <tt>sl_RB_delete()</tt> — инициализация и удаление данных структуры кольцевого буфера. <tt>sl_RB_hasbyte()</tt> позволяет определить, есть ли в буфере нужный символ (например, конец строки), а <tt>sl_RB_readto()</tt> — считать до него (включительно). <tt>sl_RB_putbyte()</tt>, <tt>sl_RB_write()</tt> и <tt>sl_RB_writestr()</tt> позволяют записать данные в буфер (возвращают количество записанных символов, чтобы юзер мог понять, что что-то не то, если место кончилось). <tt>sl_RB_datalen()</tt> и <tt>sl_RB_freesize()</tt> позволяют узнать, сколько места в буфере занимают данные, и сколько там свободно. Нужно помнить, что размер данных будет на 1 байт меньше длины буфера (по понятным причинам). <tt>sl_RB_clearbuf()</tt> сбрасывает содержимое буфера, как будто бы там ничего нет.
|
||||||
|
|
||||||
|
Ну и последняя, самая жирная по количеству функций и структур — сетевая часть.
|
||||||
|
Для активации соединения есть две функции: <tt>sl_sock_run_client()</tt> и <tt>sl_sock_run_server()</tt> — для клиента и сервера, соответственно. Они инициализируют структуры, открывают соединение и подключаются для клиента или вызывают bind для сервера. Клиенту заодно запускается поток, читающий приходящие данные и складывающий в кольцевой буфер. У сервера, если указать не-NULL аргумент handlers, тоже запускается отдельный поток: он обслуживает присоединенных клиентов, запускает accept для новых, закрывает отключившиеся; полученные от клиентов данные парсятся и, если среди переданной структуры handlers находится подобный ключ, то запускается соответствующий обработчик + есть три стандартных обработчика: <tt>sl_sock_inthandler()</tt>, <tt>sl_sock_dblhandler()</tt> и <tt>sl_sock_strhandler()</tt> — для int64_t, double и char* (для того, чтобы знать, когда было произведено изменение значения, я дополнительные типы данных ввел, где помимо данных есть timestamp). Предельное количество клиентов можно задать функцией <tt>sl_sock_changemaxclients()</tt>, а узнать текущее — <tt>sl_sock_getmaxclients()</tt>. <tt>sl_sock_delete()</tt> закрывает соединение и удаляет структуру.
|
||||||
|
Помимо этого, есть еще обработчики событий: подключение клиента, отключение и подключение клиента сверх предельного количества. По умолчанию там NULL (т.е. ничего не делается), но можно сменить при помощи, соответственно, функций <tt>sl_sock_connhandler()</tt>, <tt>sl_sock_dischandler()</tt> и <tt>sl_sock_maxclhandler()</tt>. В них можно, например, в логи писать, мол клиент fd=xx ip=yyy подключился/отключился. А в последнем — писать клиенту "подожди, все соединения заняты".
|
||||||
|
Отправлять данные можно непосредственно при помощи write/send (не забывая сначала заблокировать, а потом разблокировать соответствующий мьютекс), либо же при помощи оберток: <tt>sl_sock_sendbyte()</tt>, <tt>sl_sock_sendbinmessage()</tt> и <tt>sl_sock_sendstrmessage()</tt>. Читать тоже можно или при помощи соответствующих функций работы с кольцевым буфером, или же текстовые строковые сообщения можно считывать посредством <tt>sl_sock_readline()</tt>.
|
||||||
|
У сервера бывают ситуации, когда одно и то же сообщение нужно отправить сразу всем клиентам. Для этого есть функция <tt>sl_sock_sendall()</tt> (она тупо перебирает все открытые и подключенные дескрипторы, да отправляет туда данные).
|
||||||
|
|
||||||
|
<p><b>Примеры</b></p>
|
||||||
|
<i>clientserver</i> — пример организации простейшего сервера и клиента. 226 строк всего: аргументы командной строки, примеры обработчиков, пример неблокирующего чтения с клавиатуры одновременно с чтением из сокета. Здесь я не парился насчет типа "локальный сетевой": можно открыть либо UNIX-сокет, либо INET. Заодно логи можно писать.
|
||||||
|
<i>conffile</i> — 101 строка. Дает пример ввода конфигурационных параметров как из командной строки, так и из конфигурационного файла. Также парсит строковый параметр вида "параметр=значение". Отображает состояние флагов после обработки аргументов командной строки, а затем — после считывания из конфигурационного файла. К сожалению, чтобы считать путь к конфигурационному файлу, надо сначала обработать аргументы командной строки, поэтому, если не постараться, а просто на тот же самый массив структур вызывать чтение конф-файла, то приоритет будет за ним. Хотя, логика подсказывает, что приоритет всегда должен быть за командной строкой. Поэтому в случае, когда необходимо именно иметь приоритет командной строки, нужно будет завести 2 раздельных структуры, инициализировать их сначала каким-то дефолтом (с данными, которые пользователь точно не соможет ввести), а после сравнить и все неинициализированное из командной строки инициализировать записями из конф-файла.
|
||||||
|
<i>fifo</i> — 61 строка. Пример работы с FIFO/LIFO. Показывает реальный размер свободной ОЗУ и затем все параметры командной строки помещает в списки: LIFO и FIFO. А затем из каждого списка по одному извлекает, демонстрируя, как это работает.
|
||||||
|
<i>helloworld</i> — 35 строк (из которых только 10 приходится на собственно код). Демонстрирует цветной вывод и ввод без эха.
|
||||||
|
<i>options</i> — 134 строки. Демонстрирует работу с разным типом опций (кроме функции: я и сам уже забыл, зачем оно мне нужно было), в т.ч. массивами. Ну и по умолчанию демонстрирует простейший терминал для работы с символьными устройствами (если пользователь введет путь).
|
||||||
|
<i>ringbuffer</i> — 80 строк, демонстрирующих работу кольцевого буфера. По умолчанию он имеет размер 1024 байт (т.е. вмещает 1023 символа). Дальше пользователю предлагается вводить построчно текст, который будет помещаться в буфер. Ввод заканчивается на переполнении буфера или же при нажатии ctrl+D. После чего весь буфер вычитывается и выводится на консоль.
|
||||||
|
</lj-cut>
|
||||||
|
Теперь как минимум все, что пользуется этой библиотекой, надо обновить. Ну, а сетевые демоны роботелескопов вообще радикально так переделать: чтобы "шапкой" с метеоданными не демон монтировки занимался (что вообще нелогично), а демон погоды; чтобы демоны купола, монтировки и телескопа "слушались" демона погоды. Скажем, если стоит prohibited=1, то прекращать наблюдения, когда они идут, или не давать наблюдателю открыться. Вот на БТА такую функцию АСУшник выполняет (правда, некоторые из рук вон плохо работают, позволяя наблюдателям-вредителям открывать телескоп при влажности свыше 90%, облачности или даже тумане). А на Ц-1000 почему-то до сих пор нет такого "супердемона", который бы "давал по шапке" наблюдателям-вредителям. Иначе угробят телескоп, а за это даже никакого наказания не понесут (я бы отстранял наблюдателей минимум на полгода от наблюдений, если они открывают телескоп тогда, когда этого делать ни в коем случае нельзя).
|
||||||
@ -126,7 +126,7 @@ static sl_sock_string_t sflag = {0};
|
|||||||
static sl_sock_hresult_e dtimeh(sl_sock_t _U_ *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
static sl_sock_hresult_e dtimeh(sl_sock_t _U_ *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||||
char buf[32];
|
char buf[32];
|
||||||
snprintf(buf, 31, "UNIXT=%.2f\n", sl_dtime());
|
snprintf(buf, 31, "UNIXT=%.2f\n", sl_dtime());
|
||||||
sl_sock_sendall((uint8_t*)buf, strlen(buf));
|
sl_sock_sendall(s, (uint8_t*)buf, strlen(buf));
|
||||||
return RESULT_SILENCE;
|
return RESULT_SILENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,16 +158,24 @@ static void toomuch(int fd){
|
|||||||
DBG("Disc after %gs", sl_dtime() - t0);
|
DBG("Disc after %gs", sl_dtime() - t0);
|
||||||
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
||||||
}
|
}
|
||||||
// new connections handler
|
// new connections handler (return FALSE to reject client)
|
||||||
static void connected(sl_sock_t *c){
|
static int connected(sl_sock_t *c){
|
||||||
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
|
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
|
||||||
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
// disconnected handler
|
// disconnected handler
|
||||||
static void disconnected(sl_sock_t *c){
|
static void disconnected(sl_sock_t *c){
|
||||||
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
|
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
|
||||||
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
||||||
}
|
}
|
||||||
|
static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
|
||||||
|
if(!s || !str) return RESULT_FAIL;
|
||||||
|
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
|
||||||
|
sl_sock_sendstrmessage(s, str);
|
||||||
|
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
|
||||||
static sl_sock_hitem_t handlers[] = {
|
static sl_sock_hitem_t handlers[] = {
|
||||||
{sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
|
{sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
|
||||||
@ -185,16 +193,19 @@ int main(int argc, char **argv){
|
|||||||
if(!G.node) ERRX("Point node");
|
if(!G.node) ERRX("Point node");
|
||||||
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
|
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
|
||||||
if(G.isserver){
|
if(G.isserver){
|
||||||
sl_sock_changemaxclients(G.maxclients);
|
|
||||||
sl_sock_maxclhandler(toomuch);
|
|
||||||
sl_sock_connhandler(connected);
|
|
||||||
sl_sock_dischandler(disconnected);
|
|
||||||
s = sl_sock_run_server(type, G.node, -1, handlers);
|
s = sl_sock_run_server(type, G.node, -1, handlers);
|
||||||
} else {
|
} else {
|
||||||
sl_setup_con();
|
sl_setup_con();
|
||||||
s = sl_sock_run_client(type, G.node, -1);
|
s = sl_sock_run_client(type, G.node, -1);
|
||||||
}
|
}
|
||||||
if(!s) ERRX("Can't create socket and/or run threads");
|
if(!s) ERRX("Can't create socket and/or run threads");
|
||||||
|
if(G.isserver){
|
||||||
|
sl_sock_changemaxclients(s, G.maxclients);
|
||||||
|
sl_sock_maxclhandler(s, toomuch);
|
||||||
|
sl_sock_connhandler(s, connected);
|
||||||
|
sl_sock_dischandler(s, disconnected);
|
||||||
|
sl_sock_defmsghandler(s, defhandler);
|
||||||
|
}
|
||||||
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
|
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
|
||||||
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
||||||
if(G.logfile) OPENLOG(G.logfile, lvl, 1);
|
if(G.logfile) OPENLOG(G.logfile, lvl, 1);
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
int main(int argc, char *argv[argc]) {
|
int main(int argc, char *argv[argc]) {
|
||||||
sl_list_t *f = NULL;
|
sl_list_t *f = NULL;
|
||||||
printf("Available memory: %luMB\n", sl_mem_avail()/1024/1024);
|
printf("Available memory: %luMB\n", sl_mem_avail()/1024/1024);
|
||||||
//initial_setup(); - there's no need for this function if you don't use locale & don't want to have
|
sl_init();
|
||||||
// specific output in non-tty
|
// specific output in non-tty
|
||||||
if(argc == 1){
|
if(argc == 1){
|
||||||
green("Usage:\n\t%s args - fill fifo with arguments\n", __progname);
|
green("Usage:\n\t%s args - fill fifo with arguments\n", __progname);
|
||||||
@ -55,7 +55,6 @@ int main(int argc, char *argv[argc]) {
|
|||||||
d = sl_list_pop(&f);
|
d = sl_list_pop(&f);
|
||||||
green("pull: ");
|
green("pull: ");
|
||||||
printf("%s\n", d);
|
printf("%s\n", d);
|
||||||
// after last usage we should FREE data, but here it is parameter of main()
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
/**
|
/**
|
||||||
* @brief sl_list_push_tail - push data into the tail of a stack (like FIFO)
|
* @brief sl_list_push_tail - push data into the tail of a stack (like FIFO)
|
||||||
* @param lst (io) - list
|
* @param lst (io) - list
|
||||||
* @param v (i) - data to push
|
* @param v (i) - data to push (DON'T FREE it after this function as it would be just a link to original!)
|
||||||
* @return pointer to just pused node or NULL in case of error
|
* @return pointer to just pused node or NULL in case of error
|
||||||
*/
|
*/
|
||||||
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v){
|
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v){
|
||||||
|
|||||||
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-12-16 11:25+0300\n"
|
"POT-Creation-Date: 2025-06-27 11:51+0300\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -105,38 +105,39 @@ msgstr ""
|
|||||||
msgid "Wrong argument \"%s\" of parameter \"%s\""
|
msgid "Wrong argument \"%s\" of parameter \"%s\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:148
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147
|
||||||
msgid "Server disconnected"
|
msgid "Server disconnected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:223
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390
|
||||||
msgid "Can't start server handlers thread"
|
msgid "Can't start server handlers thread"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:285
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459
|
||||||
msgid "Limit of connections reached"
|
msgid "Limit of connections reached"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. check for RB overflow
|
#. check for RB overflow
|
||||||
#. -1 - buffer empty (can't be), -2 - buffer overflow
|
#. -1 - buffer empty (can't be), -2 - buffer overflow
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:325
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: ring buffer overflow for fd=%d"
|
msgid "Server thread: ring buffer overflow for fd=%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:339
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
|
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. buffer overflow
|
#. buffer overflow
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:351
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Server thread: buffer overflow from fd=%d"
|
msgid "Server thread: buffer overflow from fd=%d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. never reached
|
#. never reached
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:416
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unsupported socket type %d"
|
msgid "Unsupported socket type %d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr "Project-Id-Version: PACKAGE VERSION\n"
|
msgstr "Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-12-13 09:10+0300\n"
|
"POT-Creation-Date: 2025-06-27 11:49+0300\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -85,7 +85,7 @@ msgstr "
|
|||||||
msgid "Can't setup console"
|
msgid "Can't setup console"
|
||||||
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
|
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:223
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390
|
||||||
msgid "Can't start server handlers thread"
|
msgid "Can't start server handlers thread"
|
||||||
msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
|
msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ msgstr "
|
|||||||
msgid "Key '%s' need value"
|
msgid "Key '%s' need value"
|
||||||
msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ"
|
msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:285
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459
|
||||||
msgid "Limit of connections reached"
|
msgid "Limit of connections reached"
|
||||||
msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ"
|
msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ"
|
||||||
|
|
||||||
@ -146,20 +146,27 @@ msgstr "
|
|||||||
msgid "Port name is missing"
|
msgid "Port name is missing"
|
||||||
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
|
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:148
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147
|
||||||
msgid "Server disconnected"
|
msgid "Server disconnected"
|
||||||
msgstr "óÅÒ×ÅÒ ÏÔËÌÀÞÅÎ"
|
msgstr "óÅÒ×ÅÒ ÏÔËÌÀÞÅÎ"
|
||||||
|
|
||||||
#. buffer overflow
|
#. buffer overflow
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:351
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530
|
||||||
|
#, c-format
|
||||||
msgid "Server thread: buffer overflow from fd=%d"
|
msgid "Server thread: buffer overflow from fd=%d"
|
||||||
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:339
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518
|
||||||
msgid "Server thread: can't write data to ringbuffer, overflow from fd=%d"
|
#, fuzzy, c-format
|
||||||
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
|
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
|
||||||
|
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, "
|
||||||
|
"ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
|
||||||
|
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:325
|
#. check for RB overflow
|
||||||
|
#. -1 - buffer empty (can't be), -2 - buffer overflow
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503
|
||||||
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504
|
||||||
|
#, c-format
|
||||||
msgid "Server thread: ring buffer overflow for fd=%d"
|
msgid "Server thread: ring buffer overflow for fd=%d"
|
||||||
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
|
||||||
|
|
||||||
@ -168,7 +175,7 @@ msgid "Unsupported option type"
|
|||||||
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÏÐÃÉÉ"
|
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÏÐÃÉÉ"
|
||||||
|
|
||||||
#. never reached
|
#. never reached
|
||||||
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:416
|
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unsupported socket type %d"
|
msgid "Unsupported socket type %d"
|
||||||
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÓÏËÅÔÁ %d"
|
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÓÏËÅÔÁ %d"
|
||||||
|
|||||||
10
ringbuffer.c
10
ringbuffer.c
@ -82,6 +82,9 @@ size_t sl_RB_freesize(sl_ringbuffer_t *b){
|
|||||||
static ssize_t hasbyte(sl_ringbuffer_t *b, uint8_t byte){
|
static ssize_t hasbyte(sl_ringbuffer_t *b, uint8_t byte){
|
||||||
if(b->head == b->tail) return -1; // no data in buffer
|
if(b->head == b->tail) return -1; // no data in buffer
|
||||||
size_t startidx = b->head;
|
size_t startidx = b->head;
|
||||||
|
/*DBG("head: %zd, tail: %zd (%c %c %c %c), search %02x",
|
||||||
|
b->head, b->tail, b->data[startidx], b->data[startidx+1],
|
||||||
|
b->data[startidx+2], b->data[startidx+3], byte);*/
|
||||||
if(b->head > b->tail){
|
if(b->head > b->tail){
|
||||||
for(size_t found = b->head; found < b->length; ++found)
|
for(size_t found = b->head; found < b->length; ++found)
|
||||||
if(b->data[found] == byte) return found;
|
if(b->data[found] == byte) return found;
|
||||||
@ -222,6 +225,13 @@ size_t sl_RB_write(sl_ringbuffer_t *b, const uint8_t *str, size_t len){
|
|||||||
DBG("rest: %zd, need: %zd", r, len);
|
DBG("rest: %zd, need: %zd", r, len);
|
||||||
if(len > r) len = r;
|
if(len > r) len = r;
|
||||||
if(!len) goto ret;
|
if(!len) goto ret;
|
||||||
|
/*green("buf:\n");
|
||||||
|
for(size_t i = 0; i < len; ++i){
|
||||||
|
char c = (char)str[i];
|
||||||
|
if(c > 31) printf("%c", c);
|
||||||
|
else printf("\\x%02x", c);
|
||||||
|
}
|
||||||
|
green("(end)\n");*/
|
||||||
size_t _1st = b->length - b->tail;
|
size_t _1st = b->length - b->tail;
|
||||||
if(_1st > len) _1st = len;
|
if(_1st > len) _1st = len;
|
||||||
memcpy(b->data + b->tail, str, _1st);
|
memcpy(b->data + b->tail, str, _1st);
|
||||||
|
|||||||
326
socket.c
326
socket.c
@ -16,6 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
@ -29,37 +30,35 @@
|
|||||||
|
|
||||||
#include "usefull_macros.h"
|
#include "usefull_macros.h"
|
||||||
|
|
||||||
// max clients amount
|
|
||||||
static int maxclients = 32;
|
|
||||||
// too much clients handler; it is running for client connected with number>maxclients (before closing its fd)
|
|
||||||
static sl_sock_maxclh_t toomuch_handler = NULL;
|
|
||||||
// new client connected handler; it will be run each new connection
|
|
||||||
static sl_sock_connh_t newconnect_handler = NULL;
|
|
||||||
// client disconnected handler
|
|
||||||
static sl_sock_disch_t disconnect_handler = NULL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief sl_sock_changemaxclients - change amount of max simultaneously connected clients
|
* @brief sl_sock_changemaxclients - change amount of max simultaneously connected clients
|
||||||
* SHOULD BE run BEFORE running of server
|
* SHOULD BE run BEFORE running of server
|
||||||
* @param val - maximal clients number
|
* @param val - maximal clients number
|
||||||
*/
|
*/
|
||||||
void sl_sock_changemaxclients(int val){
|
void sl_sock_changemaxclients(sl_sock_t *sock, int val){
|
||||||
maxclients = val;
|
if(sock) sock->maxclients = val;
|
||||||
|
}
|
||||||
|
int sl_sock_getmaxclients(sl_sock_t *sock){
|
||||||
|
if(sock) return sock->maxclients;
|
||||||
|
else return -1;
|
||||||
}
|
}
|
||||||
int sl_sock_getmaxclients(){ return maxclients; }
|
|
||||||
|
|
||||||
// in each next function changed default handlers you can set h to NULL to remove your handler
|
// in each next function changed default handlers you can set h to NULL to remove your handler
|
||||||
// setter of "too much clients" handler
|
// setter of "too much clients" handler
|
||||||
void sl_sock_maxclhandler(sl_sock_maxclh_t h){
|
void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int)){
|
||||||
toomuch_handler = h;
|
if(sock) sock->toomuch_handler = h;
|
||||||
}
|
}
|
||||||
// setter of "new client connected" handler
|
// setter of "new client connected" handler
|
||||||
void sl_sock_connhandler(sl_sock_connh_t h){
|
void sl_sock_connhandler(sl_sock_t *sock, int(*h)(struct sl_sock*)){
|
||||||
newconnect_handler = h;
|
if(sock) sock->newconnect_handler = h;
|
||||||
}
|
}
|
||||||
// setter of "client disconnected" handler
|
// setter of "client disconnected" handler
|
||||||
void sl_sock_dischandler(sl_sock_disch_t h){
|
void sl_sock_dischandler(sl_sock_t *sock, void(*h)(struct sl_sock*)){
|
||||||
disconnect_handler = h;
|
if(sock) sock->disconnect_handler = h;
|
||||||
|
}
|
||||||
|
// setter of default client message handler
|
||||||
|
void sl_sock_defmsghandler(struct sl_sock *sock, sl_sock_hresult_e(*h)(struct sl_sock *s, const char *str)){
|
||||||
|
if(sock) sock->defmsg_handler = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// text messages for `hresult`
|
// text messages for `hresult`
|
||||||
@ -163,33 +162,174 @@ errex:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// common for server thread and `sendall`
|
|
||||||
static sl_sock_t **clients = NULL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief sl_sock_sendall - send data to all clients connected (works only for server)
|
* @brief sl_sock_sendall - send data to all clients connected (works only for server)
|
||||||
* @param data - message
|
* @param data - message
|
||||||
* @param len - its length
|
* @param len - its length
|
||||||
* @return N of sends or -1 if no server process running
|
* @return N of sends or -1 if no server process running
|
||||||
*/
|
*/
|
||||||
int sl_sock_sendall(uint8_t *data, size_t len){
|
int sl_sock_sendall(sl_sock_t *sock, uint8_t *data, size_t len){
|
||||||
if(!clients) return -1;
|
FNAME();
|
||||||
|
if(!sock || !sock->clients) return -1;
|
||||||
int nsent = 0;
|
int nsent = 0;
|
||||||
for(int i = maxclients; i > 0; --i){
|
for(int i = sock->maxclients; i > 0; --i){
|
||||||
if(!clients[i]) continue;
|
if(!sock->clients[i]) continue;
|
||||||
if(clients[i]->fd < 0 || !clients[i]->connected) continue;
|
if(sock->clients[i]->fd < 0 || !sock->clients[i]->connected) continue;
|
||||||
if((ssize_t)len == sl_sock_sendbinmessage(clients[i], data, len)) ++nsent;
|
if((ssize_t)len == sl_sock_sendbinmessage(sock->clients[i], data, len)) ++nsent;
|
||||||
}
|
}
|
||||||
return nsent;
|
return nsent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str);
|
||||||
|
|
||||||
|
// return TRUE if this is header without data (also modify c->sockmethod)
|
||||||
|
static int iswebheader(sl_sock_t *client, char *str){
|
||||||
|
DBG("check header: _%s_", str);
|
||||||
|
const char *methods[] = {
|
||||||
|
[SOCKM_GET] = "GET",
|
||||||
|
[SOCKM_PUT] = "PUT",
|
||||||
|
[SOCKM_POST] = "POST",
|
||||||
|
[SOCKM_PATCH] = "PATCH",
|
||||||
|
[SOCKM_DELETE] = "DELETE"
|
||||||
|
};
|
||||||
|
const int metlengths[] = {
|
||||||
|
[SOCKM_GET] = 3,
|
||||||
|
[SOCKM_PUT] = 3,
|
||||||
|
[SOCKM_POST] = 4,
|
||||||
|
[SOCKM_PATCH] = 5,
|
||||||
|
[SOCKM_DELETE] = 6
|
||||||
|
};
|
||||||
|
for(sl_sockmethod_e m = SOCKM_GET; m < SOCKM_AMOUNT; ++m){
|
||||||
|
if(0 == strncmp(str, methods[m], metlengths[m])){
|
||||||
|
client->sockmethod = m;
|
||||||
|
DBG("SOCK method: %s", methods[m]);
|
||||||
|
if(m == SOCKM_GET){ // modify `str` by GET
|
||||||
|
char *slash = strchr(str, '/');
|
||||||
|
if(slash){
|
||||||
|
char *eol = strstr(slash, "HTTP");
|
||||||
|
if(eol){ // move to `str` head
|
||||||
|
size_t l = eol - slash - 2;
|
||||||
|
memmove(str, slash+1, l);
|
||||||
|
str[l] = 0;
|
||||||
|
DBG("User asks GET '%s' -> run parser", str);
|
||||||
|
//parse_post_data(client, str);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *stringscan(char *str, char *needle){
|
||||||
|
char *a, *end = str + strlen(str);
|
||||||
|
a = strstr(str, needle);
|
||||||
|
if(!a) return NULL;
|
||||||
|
a += strlen(needle);
|
||||||
|
while (a < end && isspace(*a)) a++;
|
||||||
|
if(a >= end) return NULL;
|
||||||
|
DBG("Found a needle \"%s\"", needle);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just check for header words - if so, return TRUE
|
||||||
|
static int chkwebreq(sl_sock_t *client, char *str){
|
||||||
|
if(client->contlen == 0){
|
||||||
|
char *cl = stringscan(str, "Content-Length:");
|
||||||
|
if(cl){
|
||||||
|
DBG("Contlen: _%s_", cl);
|
||||||
|
sl_str2i(&client->contlen, cl);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG("retval: %d", !client->gotemptyline);
|
||||||
|
return !client->gotemptyline;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In-place URL parser
|
||||||
|
void url_decode(char *str) {
|
||||||
|
if (!str) return;
|
||||||
|
char *src = str;
|
||||||
|
char *dst = str;
|
||||||
|
char hex[3] = {0};
|
||||||
|
DBG("STR ori: _%s_", str);
|
||||||
|
while(*src){
|
||||||
|
if(*src == '+'){
|
||||||
|
*dst++ = ' ';
|
||||||
|
src++;
|
||||||
|
}else if(*src == '%' && isxdigit((unsigned char)src[1]) && isxdigit((unsigned char)src[2])){
|
||||||
|
hex[0] = src[1];
|
||||||
|
hex[1] = src[2];
|
||||||
|
*dst++ = (char)strtol(hex, NULL, 16);
|
||||||
|
src += 3;
|
||||||
|
}else{
|
||||||
|
*dst++ = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*dst = 0;
|
||||||
|
DBG("STR DECODED to _%s_", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str);
|
||||||
|
|
||||||
|
// parser of web-encoded data by POST/GET:
|
||||||
|
static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str){
|
||||||
|
if (!c || !str) return RESULT_BADKEY;
|
||||||
|
if(0 == strcmp("favicon.ico", str)){
|
||||||
|
DBG("icon -> omit");
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
char *start = str;
|
||||||
|
char *current = str;
|
||||||
|
DBG("\n\n\nSTART parser");
|
||||||
|
while(*current){
|
||||||
|
if(*current == '&'){
|
||||||
|
*current = '\0';
|
||||||
|
DBG("\n\n`start` = _%s_", start);
|
||||||
|
url_decode(start);
|
||||||
|
sl_sock_hresult_e r = msgparser(c, start);
|
||||||
|
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
|
||||||
|
start = current + 1;
|
||||||
|
}
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
if(*start){
|
||||||
|
url_decode(start);
|
||||||
|
sl_sock_hresult_e r = msgparser(c, start);
|
||||||
|
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
|
||||||
|
}
|
||||||
|
DBG("\n\n\nEND parser");
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
|
||||||
// parser of client's message
|
// parser of client's message
|
||||||
|
// "only-server's" fields of `client` are copies of server's
|
||||||
static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
|
static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
|
||||||
char key[SL_KEY_LEN], val[SL_VAL_LEN], *valptr;
|
char key[SL_KEY_LEN], val[SL_VAL_LEN], *valptr;
|
||||||
if(!str || !*str) return RESULT_BADKEY;
|
if(!str || !*str) return RESULT_BADKEY;
|
||||||
|
// check web headers and fields
|
||||||
|
DBG("\n\nLINENO: %lu", client->lineno);
|
||||||
|
if(client->lineno == 0){ // first data line from client -> check if it's a header
|
||||||
|
if(iswebheader(client, str)){
|
||||||
|
if(client->sockmethod == SOCKM_GET)
|
||||||
|
return parse_post_data(client, str);
|
||||||
|
return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
}else if(client->sockmethod != SOCKM_RAW){
|
||||||
|
if(chkwebreq(client, str)) return RESULT_SILENCE;
|
||||||
|
}
|
||||||
|
if(!client->handlers){ // have only default handler
|
||||||
|
if(!client->defmsg_handler) return RESULT_BADKEY;
|
||||||
|
return client->defmsg_handler(client, str);
|
||||||
|
}
|
||||||
int N = sl_get_keyval(str, key, val);
|
int N = sl_get_keyval(str, key, val);
|
||||||
DBG("getval=%d, key=%s, val=%s", N, key, val);
|
DBG("getval=%d, key=%s, val=%s", N, key, val);
|
||||||
if(N == 0) return RESULT_BADKEY;
|
if(N == 0){
|
||||||
|
if(client->defmsg_handler) return client->defmsg_handler(client, str);
|
||||||
|
return RESULT_BADKEY;
|
||||||
|
}
|
||||||
if(N == 1) valptr = NULL;
|
if(N == 1) valptr = NULL;
|
||||||
else valptr = val;
|
else valptr = val;
|
||||||
if(0 == strcmp(key, "help")){
|
if(0 == strcmp(key, "help")){
|
||||||
@ -209,9 +349,31 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
|
|||||||
if(strcmp(h->key, key)) continue;
|
if(strcmp(h->key, key)) continue;
|
||||||
return h->handler(client, h, valptr);
|
return h->handler(client, h, valptr);
|
||||||
}
|
}
|
||||||
|
if(client->defmsg_handler) return client->defmsg_handler(client, str);
|
||||||
return RESULT_BADKEY;
|
return RESULT_BADKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send http responce with data in c->outbuffer
|
||||||
|
static void send_http_response(sl_sock_t* c){
|
||||||
|
if(c->sockmethod == SOCKM_RAW) return;
|
||||||
|
DBG("Send to client HTTP response");
|
||||||
|
c->sockmethod = SOCKM_RAW; // dirty hack to send data by sl_sock_sendbinmessage
|
||||||
|
char tbuf[BUFSIZ];
|
||||||
|
ssize_t L = snprintf(tbuf, BUFSIZ-1,
|
||||||
|
"HTTP/2.0 200 OK\r\n"
|
||||||
|
"Access-Control-Allow-Origin: *\r\n"
|
||||||
|
"Access-Control-Allow-Methods: GET, POST\r\n"
|
||||||
|
"Access-Control-Allow-Credentials: true\r\n"
|
||||||
|
"Content-type: text/plain\r\nContent-Length: %zd\r\n\r\n", c->outplen);
|
||||||
|
if(L < 0){
|
||||||
|
WARN("sprintf()");
|
||||||
|
LOGWARN("sprintf()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(L != sl_sock_sendbinmessage(c, (uint8_t*)tbuf, L)) return;
|
||||||
|
if(c->outplen != (size_t)sl_sock_sendbinmessage(c, (uint8_t*)c->outbuffer, c->outplen)) return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief serverrbthread - thread for standard server procedure (when user give non-NULL `handlers`)
|
* @brief serverrbthread - thread for standard server procedure (when user give non-NULL `handlers`)
|
||||||
* @param d - socket descriptor
|
* @param d - socket descriptor
|
||||||
@ -219,43 +381,50 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
|
|||||||
*/
|
*/
|
||||||
static void *serverthread(void _U_ *d){
|
static void *serverthread(void _U_ *d){
|
||||||
sl_sock_t *s = (sl_sock_t*) d;
|
sl_sock_t *s = (sl_sock_t*) d;
|
||||||
if(!s || !s->handlers){
|
if(!s || (!s->handlers && !s->defmsg_handler)){
|
||||||
WARNX(_("Can't start server handlers thread"));
|
WARNX(_("Can't start server handlers thread"));
|
||||||
goto errex;
|
goto errex;
|
||||||
}
|
}
|
||||||
int sockfd = s->fd;
|
int sockfd = s->fd;
|
||||||
if(listen(sockfd, maxclients) == -1){
|
if(listen(sockfd, s->maxclients) == -1){
|
||||||
WARN("listen");
|
WARN("listen");
|
||||||
goto errex;
|
goto errex;
|
||||||
}
|
}
|
||||||
DBG("Start server handlers thread");
|
DBG("Start server handlers thread");
|
||||||
int nfd = 1; // only one socket @start
|
int nfd = 1; // only one socket @start
|
||||||
struct pollfd *poll_set = MALLOC(struct pollfd, maxclients+1);
|
struct pollfd *poll_set = MALLOC(struct pollfd, s->maxclients+1);
|
||||||
clients = MALLOC(sl_sock_t*, maxclients+1);
|
sl_sock_t **clients = MALLOC(sl_sock_t*, s->maxclients+1);
|
||||||
|
s->clients = clients;
|
||||||
// init default clients records
|
// init default clients records
|
||||||
for(int i = maxclients; i > 0; --i){
|
for(int i = s->maxclients; i > 0; --i){
|
||||||
DBG("fill %dth client info", i);
|
DBG("fill %dth client info", i);
|
||||||
clients[i] = MALLOC(sl_sock_t, 1);
|
clients[i] = MALLOC(sl_sock_t, 1);
|
||||||
sl_sock_t *c = clients[i];
|
sl_sock_t *c = clients[i];
|
||||||
c->type = s->type;
|
c->type = s->type;
|
||||||
if(s->node) c->node = strdup(s->node);
|
if(s->node) c->node = strdup(s->node);
|
||||||
if(s->service) c->service = strdup(s->service);
|
if(s->service) c->service = strdup(s->service);
|
||||||
c->handlers = s->handlers;
|
|
||||||
// fill addrinfo
|
// fill addrinfo
|
||||||
c->addrinfo = MALLOC(struct addrinfo, 1);
|
c->addrinfo = MALLOC(struct addrinfo, 1);
|
||||||
c->addrinfo->ai_addr = MALLOC(struct sockaddr, 1);
|
c->addrinfo->ai_addr = MALLOC(struct sockaddr, 1);
|
||||||
|
// copy server data: we have no `self`, so use so
|
||||||
|
c->handlers = s->handlers;
|
||||||
|
c->defmsg_handler = s->defmsg_handler;
|
||||||
}
|
}
|
||||||
// ZERO - listening server socket
|
// ZERO - listening server socket
|
||||||
poll_set[0].fd = sockfd;
|
poll_set[0].fd = sockfd;
|
||||||
poll_set[0].events = POLLIN;
|
poll_set[0].events = POLLIN;
|
||||||
// disconnect client
|
// disconnect client
|
||||||
void disconnect_(sl_sock_t *c, int N){
|
void disconnect_(sl_sock_t *c, int N){
|
||||||
DBG("client \"%s\" (fd=%d) disconnected", c->IP, c->fd);
|
DBG("Disconnect client \"%s\" (fd=%d)", c->IP, c->fd);
|
||||||
if(disconnect_handler) disconnect_handler(c);
|
if(s->disconnect_handler) s->disconnect_handler(c);
|
||||||
|
if(c->sockmethod != SOCKM_RAW) send_http_response(c); // we are closing HTTP request - send headers and answer
|
||||||
pthread_mutex_lock(&c->mutex);
|
pthread_mutex_lock(&c->mutex);
|
||||||
DBG("close fd %d", c->fd);
|
DBG("close fd %d", c->fd);
|
||||||
c->connected = 0;
|
c->connected = 0;
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
|
c->outplen = 0;
|
||||||
|
c->lineno = 0;
|
||||||
|
c->gotemptyline = 0;
|
||||||
sl_RB_clearbuf(c->buffer);
|
sl_RB_clearbuf(c->buffer);
|
||||||
// now move all data of last client to disconnected
|
// now move all data of last client to disconnected
|
||||||
if(nfd > 2 && N != nfd - 1){ // don't move the only or the last
|
if(nfd > 2 && N != nfd - 1){ // don't move the only or the last
|
||||||
@ -281,9 +450,9 @@ static void *serverthread(void _U_ *d){
|
|||||||
socklen_t len = sizeof(struct sockaddr);
|
socklen_t len = sizeof(struct sockaddr);
|
||||||
int client = accept(sockfd, &a, &len);
|
int client = accept(sockfd, &a, &len);
|
||||||
DBG("New connection, nfd=%d, len=%d", nfd, len);
|
DBG("New connection, nfd=%d, len=%d", nfd, len);
|
||||||
if(nfd == maxclients + 1){
|
if(nfd == s->maxclients + 1){
|
||||||
WARNX(_("Limit of connections reached"));
|
WARNX(_("Limit of connections reached"));
|
||||||
if(toomuch_handler) toomuch_handler(client);
|
if(s->toomuch_handler) s->toomuch_handler(client);
|
||||||
close(client);
|
close(client);
|
||||||
}else{
|
}else{
|
||||||
memset(&poll_set[nfd], 0, sizeof(struct pollfd));
|
memset(&poll_set[nfd], 0, sizeof(struct pollfd));
|
||||||
@ -302,12 +471,16 @@ static void *serverthread(void _U_ *d){
|
|||||||
*c->IP = 0;
|
*c->IP = 0;
|
||||||
}
|
}
|
||||||
DBG("got IP:%s", c->IP);
|
DBG("got IP:%s", c->IP);
|
||||||
if(newconnect_handler) newconnect_handler(c);
|
++nfd;
|
||||||
|
if(s->newconnect_handler && s->newconnect_handler(c) == FALSE){
|
||||||
|
DBG("Client %s rejected", c->IP);
|
||||||
|
disconnect_(c, nfd);
|
||||||
|
}else{
|
||||||
if(!c->buffer){ // allocate memory for client's ringbuffer
|
if(!c->buffer){ // allocate memory for client's ringbuffer
|
||||||
DBG("allocate ringbuffer");
|
DBG("allocate ringbuffer");
|
||||||
c->buffer = sl_RB_new(s->buffer->length); // the same size as for master
|
c->buffer = sl_RB_new(s->buffer->length); // the same size as for master
|
||||||
}
|
}
|
||||||
++nfd;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// scan connections
|
// scan connections
|
||||||
@ -323,6 +496,7 @@ static void *serverthread(void _U_ *d){
|
|||||||
// check for RB overflow
|
// check for RB overflow
|
||||||
if(sl_RB_hasbyte(c->buffer, '\n') < 0){ // -1 - buffer empty (can't be), -2 - buffer overflow
|
if(sl_RB_hasbyte(c->buffer, '\n') < 0){ // -1 - buffer empty (can't be), -2 - buffer overflow
|
||||||
WARNX(_("Server thread: ring buffer overflow for fd=%d"), fd);
|
WARNX(_("Server thread: ring buffer overflow for fd=%d"), fd);
|
||||||
|
LOGERR(_("Server thread: ring buffer overflow for fd=%d"), fd);
|
||||||
disconnect_(c, fdidx);
|
disconnect_(c, fdidx);
|
||||||
--fdidx;
|
--fdidx;
|
||||||
}
|
}
|
||||||
@ -352,15 +526,39 @@ static void *serverthread(void _U_ *d){
|
|||||||
disconnect_(c, fdidx);
|
disconnect_(c, fdidx);
|
||||||
--fdidx;
|
--fdidx;
|
||||||
continue;
|
continue;
|
||||||
}else if(got == 0) continue;
|
}else if(got == 0){ // check last data in POST/GET methods
|
||||||
|
if(c->sockmethod == SOCKM_RAW) continue;
|
||||||
|
size_t l = sl_RB_datalen(c->buffer);
|
||||||
|
if(c->sockmethod == SOCKM_POST){
|
||||||
|
if(l != (size_t)c->contlen) continue; // wait for last data
|
||||||
|
if(l < bufsize - 1){
|
||||||
|
sl_RB_read(c->buffer, buf, l);
|
||||||
|
buf[l] = 0;
|
||||||
|
parse_post_data(c, (char*)buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disconnect_(c, fdidx);
|
||||||
|
--fdidx;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(got > 1 && *buf && *buf != '\r'){ // not empty line
|
||||||
|
if(buf[got-2] == '\r'){
|
||||||
|
buf[got-2] = 0; // omit '\r' for "\r\n"
|
||||||
|
DBG("delete \\r: _%s_", buf);
|
||||||
|
}
|
||||||
sl_sock_hresult_e r = msgparser(c, (char*)buf);
|
sl_sock_hresult_e r = msgparser(c, (char*)buf);
|
||||||
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
|
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
|
||||||
|
}else{
|
||||||
|
DBG("EMPTY line");
|
||||||
|
if(c->sockmethod != SOCKM_RAW) c->gotemptyline = TRUE;
|
||||||
|
}
|
||||||
|
++c->lineno;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clear memory
|
// clear memory
|
||||||
FREE(buf);
|
FREE(buf);
|
||||||
FREE(poll_set);
|
FREE(poll_set);
|
||||||
for(int i = maxclients; i > 0; --i){
|
for(int i = s->maxclients; i > 0; --i){
|
||||||
DBG("Clear %dth client data", i);
|
DBG("Clear %dth client data", i);
|
||||||
sl_sock_t *c = clients[i];
|
sl_sock_t *c = clients[i];
|
||||||
if(c->fd > -1) close(c->fd);
|
if(c->fd > -1) close(c->fd);
|
||||||
@ -372,6 +570,7 @@ static void *serverthread(void _U_ *d){
|
|||||||
FREE(c);
|
FREE(c);
|
||||||
}
|
}
|
||||||
FREE(clients);
|
FREE(clients);
|
||||||
|
s->clients = NULL;
|
||||||
errex:
|
errex:
|
||||||
s->rthread = 0;
|
s->rthread = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -420,11 +619,14 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
|
|||||||
sl_sock_t *s = MALLOC(sl_sock_t, 1);
|
sl_sock_t *s = MALLOC(sl_sock_t, 1);
|
||||||
s->type = type;
|
s->type = type;
|
||||||
s->fd = -1;
|
s->fd = -1;
|
||||||
|
s->maxclients = SL_DEF_MAXCLIENTS;
|
||||||
s->handlers = handlers;
|
s->handlers = handlers;
|
||||||
s->buffer = sl_RB_new(bufsiz);
|
s->buffer = sl_RB_new(bufsiz);
|
||||||
if(!s->buffer) sl_sock_delete(&s);
|
if(!s->buffer){
|
||||||
else{ // fill node/service
|
sl_sock_delete(&s);
|
||||||
if(type == SOCKT_UNIX) s->node = str; // str now is converted path
|
return NULL;
|
||||||
|
}else{ // fill node/service
|
||||||
|
if(type == SOCKT_UNIX) s->node = strdup(str); // str now is converted path
|
||||||
else{
|
else{
|
||||||
char *delim = strchr(path, ':');
|
char *delim = strchr(path, ':');
|
||||||
if(!delim) s->service = strdup(path); // only port
|
if(!delim) s->service = strdup(path); // only port
|
||||||
@ -444,13 +646,19 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
|
|||||||
// now try to open socket
|
// now try to open socket
|
||||||
if(type != SOCKT_UNIX){
|
if(type != SOCKT_UNIX){
|
||||||
DBG("try to get addrinfo for node '%s' and service '%s'", s->node, s->service);
|
DBG("try to get addrinfo for node '%s' and service '%s'", s->node, s->service);
|
||||||
char *node = s->node;
|
|
||||||
if(isserver){
|
if(isserver){
|
||||||
if(s->type == SOCKT_NET) node = NULL; // common net server -> node==NULL
|
if(!s->node){
|
||||||
else node = "127.0.0.1"; // localhost
|
DBG("Socket type now is SOCKT_NET");
|
||||||
|
s->type = SOCKT_NET;
|
||||||
}
|
}
|
||||||
DBG("---> node '%s', service '%s'", node, s->service);
|
if(s->type == SOCKT_NETLOCAL){
|
||||||
int e = getaddrinfo(node, s->service, &ai, &res);
|
DBG("SOCKT_NETLOCAL: change `node` to localhost");
|
||||||
|
FREE(s->node);
|
||||||
|
s->node = strdup("127.0.0.1"); // localhost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG("---> node '%s', service '%s'", s->node, s->service);
|
||||||
|
int e = getaddrinfo(s->node, s->service, &ai, &res);
|
||||||
if(e){
|
if(e){
|
||||||
WARNX("getaddrinfo(): %s", gai_strerror(e));
|
WARNX("getaddrinfo(): %s", gai_strerror(e));
|
||||||
sl_sock_delete(&s);
|
sl_sock_delete(&s);
|
||||||
@ -491,7 +699,7 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
|
|||||||
DBG("s->fd=%d, node=%s, service=%s", s->fd, s->node, s->service);
|
DBG("s->fd=%d, node=%s, service=%s", s->fd, s->node, s->service);
|
||||||
int r = -1;
|
int r = -1;
|
||||||
if(isserver){
|
if(isserver){
|
||||||
if(s->handlers)
|
if(s->handlers || s->defmsg_handler)
|
||||||
r = pthread_create(&s->rthread, NULL, serverthread, (void*)s);
|
r = pthread_create(&s->rthread, NULL, serverthread, (void*)s);
|
||||||
else r = 0;
|
else r = 0;
|
||||||
}else{
|
}else{
|
||||||
@ -543,6 +751,15 @@ sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz,
|
|||||||
*/
|
*/
|
||||||
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l){
|
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l){
|
||||||
if(!msg || l < 1) return -1;
|
if(!msg || l < 1) return -1;
|
||||||
|
if(socket->sockmethod != SOCKM_RAW){ // just fill buffer while socket isn't marked as "RAW"
|
||||||
|
DBG("Put to buffer: _%s_", (char*)msg);
|
||||||
|
size_t L = BUFSIZ - socket->outplen;
|
||||||
|
if(l > L) l = L;
|
||||||
|
memcpy(socket->outbuffer + socket->outplen, msg, l);
|
||||||
|
socket->outplen += l;
|
||||||
|
DBG("Now buflen=%zd, buf: ```%s```", socket->outplen, socket->outbuffer);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
DBG("send to fd=%d message with len=%zd (%s)", socket->fd, l, msg);
|
DBG("send to fd=%d message with len=%zd (%s)", socket->fd, l, msg);
|
||||||
while(socket && socket->connected && !sl_canwrite(socket->fd));
|
while(socket && socket->connected && !sl_canwrite(socket->fd));
|
||||||
if(!socket || !socket->connected) return -1;
|
if(!socket || !socket->connected) return -1;
|
||||||
@ -578,6 +795,13 @@ ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg){
|
|||||||
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte){
|
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte){
|
||||||
while(socket && socket->connected && !sl_canwrite(socket->fd));
|
while(socket && socket->connected && !sl_canwrite(socket->fd));
|
||||||
if(!socket || !socket->connected) return -1;
|
if(!socket || !socket->connected) return -1;
|
||||||
|
if(socket->sockmethod != SOCKM_RAW){ // just fill buffer while socket isn't marked as "RAW"
|
||||||
|
DBG("Put to buffer: _%c_", (char)byte);
|
||||||
|
if(socket->outplen == BUFSIZ) return 0;
|
||||||
|
socket->outbuffer[socket->outplen++] = (char)byte;
|
||||||
|
DBG("Now buflen=%zd, buf: ```%s```", socket->outplen, socket->outbuffer);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
DBG("lock");
|
DBG("lock");
|
||||||
pthread_mutex_lock(&socket->mutex);
|
pthread_mutex_lock(&socket->mutex);
|
||||||
ssize_t r = send(socket->fd, &byte, 1, MSG_NOSIGNAL);
|
ssize_t r = send(socket->fd, &byte, 1, MSG_NOSIGNAL);
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#ifdef SL_USE_OLD_TTY
|
#ifdef SL_USE_OLD_TTY
|
||||||
#include <termios.h> // termios
|
#include <termios.h> // termios
|
||||||
#else
|
#else
|
||||||
@ -445,7 +446,7 @@ struct sl_sock_hitem;
|
|||||||
struct sl_sock;
|
struct sl_sock;
|
||||||
|
|
||||||
// socket `key` handlers
|
// socket `key` handlers
|
||||||
typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *client, struct sl_sock_hitem *item, const char *val);
|
typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *s, struct sl_sock_hitem *item, const char *val);
|
||||||
// handler item
|
// handler item
|
||||||
typedef struct sl_sock_hitem{
|
typedef struct sl_sock_hitem{
|
||||||
sl_sock_msghandler handler; // function-handler
|
sl_sock_msghandler handler; // function-handler
|
||||||
@ -462,6 +463,30 @@ typedef enum{
|
|||||||
SOCKT_AMOUNT // amount of types
|
SOCKT_AMOUNT // amount of types
|
||||||
} sl_socktype_e;
|
} sl_socktype_e;
|
||||||
|
|
||||||
|
struct sl_sock;
|
||||||
|
|
||||||
|
// default max clients amount
|
||||||
|
#define SL_DEF_MAXCLIENTS (32)
|
||||||
|
// custom socket handlers: connect/disconnect/etc
|
||||||
|
// max clients handler
|
||||||
|
void sl_sock_maxclhandler(struct sl_sock *s, void (*h)(int));
|
||||||
|
// client connected handler
|
||||||
|
void sl_sock_connhandler(struct sl_sock *s, int(*h)(struct sl_sock*));
|
||||||
|
// client disconnected handler
|
||||||
|
void sl_sock_dischandler(struct sl_sock *s, void(*h)(struct sl_sock*));
|
||||||
|
// unknown message handler (instead of "BADKEY" default message)
|
||||||
|
void sl_sock_defmsghandler(struct sl_sock *s, sl_sock_hresult_e(*h)(struct sl_sock *s, const char *str));
|
||||||
|
|
||||||
|
typedef enum{
|
||||||
|
SOCKM_RAW = 0, // default sockets
|
||||||
|
SOCKM_GET, // http methods - client should be closed after data processing
|
||||||
|
SOCKM_PUT,
|
||||||
|
SOCKM_POST,
|
||||||
|
SOCKM_PATCH,
|
||||||
|
SOCKM_DELETE,
|
||||||
|
SOCKM_AMOUNT
|
||||||
|
} sl_sockmethod_e;
|
||||||
|
|
||||||
// socket itself
|
// socket itself
|
||||||
typedef struct sl_sock{
|
typedef struct sl_sock{
|
||||||
int fd; // file descriptor
|
int fd; // file descriptor
|
||||||
@ -476,29 +501,33 @@ typedef struct sl_sock{
|
|||||||
pthread_t rthread; // reading ring buffer thread for client and main server thread for server
|
pthread_t rthread; // reading ring buffer thread for client and main server thread for server
|
||||||
char IP[INET_ADDRSTRLEN]; // client's IP address
|
char IP[INET_ADDRSTRLEN]; // client's IP address
|
||||||
sl_sock_hitem_t *handlers; // if non-NULL, run handler's thread when opened
|
sl_sock_hitem_t *handlers; // if non-NULL, run handler's thread when opened
|
||||||
|
sl_sockmethod_e sockmethod; // method
|
||||||
|
uint64_t lineno; // number of line read
|
||||||
|
int contlen; // content length for POST method
|
||||||
|
int gotemptyline; // == TRUE when found empty line in web request (to know that header is over)
|
||||||
|
char outbuffer[BUFSIZ]; // buffer for output data (if client is WEB)
|
||||||
|
size_t outplen; // amount of bytes in `outbuffer`
|
||||||
|
// server-only items
|
||||||
|
int maxclients; // max clients amount
|
||||||
|
void (*toomuch_handler)(int); // too much clients handler; it is running for client connected with number>maxclients (before closing its fd)
|
||||||
|
int (*newconnect_handler)(struct sl_sock*); // new client connected handler; it will be run each new connection
|
||||||
|
void (*disconnect_handler)(struct sl_sock*); // client disconnected handler
|
||||||
|
sl_sock_hresult_e(*defmsg_handler)(struct sl_sock *s, const char *str); // default message handler (the only without `handlers` array or instead of "BADKEY" answer
|
||||||
|
struct sl_sock **clients; // pointer to clients array for `sendall`
|
||||||
} sl_sock_t;
|
} sl_sock_t;
|
||||||
|
|
||||||
const char *sl_sock_hresult2str(sl_sock_hresult_e r);
|
const char *sl_sock_hresult2str(sl_sock_hresult_e r);
|
||||||
void sl_sock_delete(sl_sock_t **sock);
|
void sl_sock_delete(sl_sock_t **sock);
|
||||||
sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz);
|
sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz);
|
||||||
sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz, sl_sock_hitem_t *handlers);
|
sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz, sl_sock_hitem_t *handlers);
|
||||||
void sl_sock_changemaxclients(int val);
|
void sl_sock_changemaxclients(sl_sock_t *sock, int val);
|
||||||
int sl_sock_getmaxclients();
|
int sl_sock_getmaxclients(sl_sock_t *sock);
|
||||||
// max clients handler
|
|
||||||
typedef void (*sl_sock_maxclh_t)(int fd);
|
|
||||||
void sl_sock_maxclhandler(sl_sock_maxclh_t h);
|
|
||||||
// new client connected handler
|
|
||||||
typedef void (*sl_sock_connh_t)(sl_sock_t *s);
|
|
||||||
void sl_sock_connhandler(sl_sock_connh_t h);
|
|
||||||
// client disconnected handler
|
|
||||||
typedef void (*sl_sock_disch_t)(sl_sock_t *s);
|
|
||||||
void sl_sock_dischandler(sl_sock_disch_t h);
|
|
||||||
|
|
||||||
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l);
|
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l);
|
||||||
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte);
|
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte);
|
||||||
ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg);
|
ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg);
|
||||||
ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len);
|
ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len);
|
||||||
int sl_sock_sendall(uint8_t *data, size_t len);
|
int sl_sock_sendall(sl_sock_t *sock, uint8_t *data, size_t len);
|
||||||
|
|
||||||
sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
|
sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
|
||||||
sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
|
sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
exec_prefix=${prefix}
|
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||||
libdir=${exec_prefix}/lib
|
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||||
includedir=${prefix}/include
|
|
||||||
|
|
||||||
Name: @PROJ@
|
Name: @PROJ@
|
||||||
Description: Library with a lot of usefull snippets
|
Description: Library with a lot of usefull snippets
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user