Compare commits
91 Commits
cf3cff0746
...
dev
Author | SHA1 | Date | |
---|---|---|---|
af62a3065a
|
|||
c36ad8c113
|
|||
7f7a07c4d2
|
|||
4ff22bd0c6
|
|||
83ca2467de
|
|||
0a1fb977d6
|
|||
1405036cf2
|
|||
5f3cd03a6f
|
|||
819b71f285
|
|||
ceaa384fce
|
|||
99c4eb1c8a
|
|||
fb59b0d8c4
|
|||
946adb54d7
|
|||
b369a1116e
|
|||
950bf19331
|
|||
993cb65724
|
|||
c7be0adc66
|
|||
4782707049
|
|||
3d1451448d
|
|||
1619e01174
|
|||
6112e719e5
|
|||
6f2751f69b
|
|||
925ff2b9e4
|
|||
c67edd4195
|
|||
d8fd552b40
|
|||
ec6457465c
|
|||
490c8a2ae0
|
|||
f87fb74ced
|
|||
7b9844e267
|
|||
308534b883
|
|||
b93d97258c
|
|||
cd80a194dd
|
|||
ffc5c80b65
|
|||
896486b64a
|
|||
c1706edcea
|
|||
f8d3253a9f
|
|||
ec3183e99b
|
|||
27d514ae12
|
|||
e82869e993
|
|||
a54a6af497
|
|||
c13bea5ab4
|
|||
fd1630a554
|
|||
f7f245c931
|
|||
e72cb57b4a
|
|||
1cf9172194
|
|||
f416c2f05c
|
|||
300e3dc0aa
|
|||
26869c0df5
|
|||
c7488b0f59
|
|||
4e6930692e
|
|||
24cb993cb8
|
|||
fc9538fa6d
|
|||
9d8120966d
|
|||
3c5ecacecc
|
|||
606865e5dc
|
|||
b79c9c710b
|
|||
4b4e7bd257
|
|||
8d6b7105f3
|
|||
95e2ddb24c
|
|||
535e1add96
|
|||
51d85cc99f
|
|||
a4c0093fbe
|
|||
6ff6f0c73b
|
|||
555e8f62a1
|
|||
03c2c7b364
|
|||
abb40a659c
|
|||
6eb3b99bfb
|
|||
9cb278eb2a
|
|||
33feabdec0
|
|||
7995a9699e
|
|||
680344fbec
|
|||
e667a79ab2
|
|||
816c8c0bfc
|
|||
1547805e4c
|
|||
96f3225f51
|
|||
9ad1ecf1da
|
|||
28d7cf68df
|
|||
204317f46a
|
|||
a56002a409
|
|||
db966b3a66
|
|||
5d27cf0398
|
|||
7a2acb0e66
|
|||
cff5d558d6
|
|||
665e4ceabc
|
|||
f241913620
|
|||
2969e435d1
|
|||
c4289ac018
|
|||
672745f6df
|
|||
f92c26c350
|
|||
f9b3cc29ab
|
|||
5c72a0cb60
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,8 @@
|
||||
*
|
||||
!src
|
||||
!src/**
|
||||
!res
|
||||
!res/**
|
||||
!doc
|
||||
!doc/**
|
||||
!test
|
||||
|
54
Makefile
54
Makefile
@@ -7,7 +7,7 @@ DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKE
|
||||
|
||||
.PHONY: all prod debug default debian permit clean test
|
||||
all: prod
|
||||
default: bin bin/lib bin/worker bin/sesimos
|
||||
default: bin bin/lib bin/worker bin/res bin/sesimos
|
||||
|
||||
prod: CFLAGS += -O3
|
||||
prod: default
|
||||
@@ -32,7 +32,11 @@ bin/lib:
|
||||
bin/worker:
|
||||
mkdir -p bin/worker
|
||||
|
||||
bin/test: test/mock_*.c test/test_*.c src/lib/utils.c src/lib/sock.c
|
||||
bin/res:
|
||||
mkdir -p bin/res
|
||||
|
||||
bin/test: test/mock_*.c test/test_*.c \
|
||||
src/lib/utils.c src/lib/sock.c src/lib/list.c src/lib/http.c src/lib/http_static.c src/logger.c
|
||||
$(CC) -o $@ $(CFLAGS) $^ -lcriterion
|
||||
|
||||
|
||||
@@ -45,32 +49,48 @@ bin/lib/%.o: src/lib/%.c
|
||||
bin/worker/%.o: src/worker/%.c
|
||||
$(CC) -c -o $@ $(CFLAGS) $<
|
||||
|
||||
bin/sesimos: bin/server.o bin/client.o bin/logger.o bin/cache_handler.o bin/async.o \
|
||||
bin/worker/request_handler.o bin/worker/tcp_acceptor.o bin/worker/tcp_closer.o \
|
||||
bin/lib/compress.o bin/lib/config.o bin/lib/fastcgi.o bin/lib/geoip.o \
|
||||
bin/lib/http.o bin/lib/http_static.o bin/lib/proxy.o bin/lib/sock.o bin/lib/uri.o \
|
||||
bin/lib/utils.o bin/lib/websocket.o bin/lib/mpmc.o
|
||||
bin/res/%.o: bin/res/%.txt
|
||||
objcopy -I binary --rename-section .data=.rodata -O elf64-x86-64 $^ $@
|
||||
|
||||
bin/res/%.txt: res/%.*
|
||||
cp $^ $@
|
||||
echo -ne "\x00" >> $@
|
||||
|
||||
bin/sesimos: bin/server.o bin/logger.o bin/cache_handler.o bin/async.o bin/workers.o \
|
||||
bin/worker/request_handler.o bin/worker/tcp_acceptor.o \
|
||||
bin/worker/fastcgi_handler.o bin/worker/local_handler.o bin/worker/proxy_handler.o \
|
||||
bin/worker/ws_frame_handler.o \
|
||||
bin/lib/http_static.o bin/res/default.o bin/res/proxy.o bin/res/style.o \
|
||||
bin/res/icon_error.o bin/res/icon_info.o bin/res/icon_success.o bin/res/icon_warning.o \
|
||||
bin/res/globe.o \
|
||||
bin/lib/compress.o bin/lib/config.o bin/lib/fastcgi.o bin/lib/geoip.o bin/lib/error.o \
|
||||
bin/lib/http.o bin/lib/proxy.o bin/lib/sock.o bin/lib/uri.o \
|
||||
bin/lib/utils.o bin/lib/websocket.o bin/lib/mpmc.o bin/lib/list.o
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
||||
bin/server.o: src/server.h src/defs.h src/client.h src/cache_handler.h src/lib/config.h src/lib/sock.h \
|
||||
bin/server.o: src/server.h src/defs.h src/cache_handler.h src/lib/config.h src/lib/sock.h \
|
||||
src/lib/proxy.h src/lib/geoip.h src/lib/utils.h src/logger.h
|
||||
|
||||
bin/client.o: src/client.h src/defs.h src/server.h src/lib/utils.h src/lib/config.h src/lib/sock.h \
|
||||
src/lib/http.h src/lib/proxy.h src/lib/fastcgi.h src/cache_handler.h src/lib/geoip.h src/lib/compress.h \
|
||||
src/lib/websocket.h src/logger.h
|
||||
|
||||
bin/logger.o: src/logger.h
|
||||
|
||||
bin/cache_handler.o: src/cache_handler.h src/lib/utils.h src/lib/uri.h src/lib/compress.h src/logger.h
|
||||
|
||||
bin/async.o: src/async.h src/logger.h
|
||||
|
||||
bin/worker/request_handler.o: src/worker/request_handler.h
|
||||
bin/workers.o: src/workers.h src/lib/mpmc.h src/worker/func.h
|
||||
|
||||
bin/worker/tcp_acceptor.o: src/worker/tcp_acceptor.h
|
||||
bin/worker/request_handler.o: src/worker/func.h
|
||||
|
||||
bin/worker/tcp_closer.o: src/worker/tcp_closer.h
|
||||
bin/worker/tcp_acceptor.o: src/worker/func.h
|
||||
|
||||
bin/worker/fastcgi_handler.o: src/worker/func.h
|
||||
|
||||
bin/worker/local_handler.o: src/worker/func.h
|
||||
|
||||
bin/worker/proxy_handler.o: src/worker/func.h
|
||||
|
||||
bin/worker/ws_frame_handler.o: src/worker/func.h
|
||||
|
||||
bin/lib/compress.o: src/lib/compress.h
|
||||
|
||||
@@ -79,10 +99,14 @@ bin/lib/config.o: src/lib/config.h src/lib/utils.h src/lib/uri.h src/logger.h
|
||||
bin/lib/fastcgi.o: src/lib/fastcgi.h src/server.h src/lib/utils.h src/lib/compress.h src/lib/http.h \
|
||||
src/lib/uri.h src/lib/include/fastcgi.h src/logger.h
|
||||
|
||||
bin/lib/error.o: src/lib/error.h
|
||||
|
||||
bin/lib/geoip.o: src/lib/geoip.h
|
||||
|
||||
bin/lib/http.o: src/lib/http.h src/lib/utils.h src/lib/compress.h src/lib/sock.h src/logger.h
|
||||
|
||||
bin/lib/list.o: src/lib/list.h
|
||||
|
||||
bin/lib/mpmc.o: src/lib/mpmc.h src/logger.h
|
||||
|
||||
bin/lib/proxy.o: src/lib/proxy.h src/defs.h src/server.h src/lib/compress.h src/logger.h
|
||||
|
22
res/default.html
Normal file
22
res/default.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>%1$i %2$s - %7$s</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="theme-color" content="%6$s"/>
|
||||
<meta name="color-scheme" content="light dark"/>
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico"/>
|
||||
<link rel="stylesheet" type="text/css" href="/.sesimos/res/style.css"/>
|
||||
<link rel="alternate icon" type="image/svg+xml" sizes="any" href="%5$s"/>
|
||||
<style>html{--color:var(--%4$s);}</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<section>
|
||||
%3$s%9$s <div class="footer"><a href="https://%7$s/">%7$s</a> - %10$s</div>
|
||||
</section>
|
||||
%8$s </main>
|
||||
</body>
|
||||
</html>
|
3
res/globe.svg
Normal file
3
res/globe.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2,32 a30,30,0,1,0,60,0 a30,30,0,1,0,-60,0 L62,32 M6,16 L58,16 M6,48 L58,48 M32,2 L32,62 a15,30,0,1,0,0,-60 a15,30,0,1,0,0,60 Z" stroke="#008000" stroke-width="2" fill="#00000000"/>
|
||||
</svg>
|
After Width: | Height: | Size: 266 B |
3
res/icon_error.svg
Normal file
3
res/icon_error.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<text x="4" y="12" fill="#C00000" style="font-family:'Arial',sans-serif">:(</text>
|
||||
</svg>
|
After Width: | Height: | Size: 158 B |
3
res/icon_info.svg
Normal file
3
res/icon_info.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<text x="4" y="12" fill="#606060" style="font-family:'Arial',sans-serif">:)</text>
|
||||
</svg>
|
After Width: | Height: | Size: 158 B |
3
res/icon_success.svg
Normal file
3
res/icon_success.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<text x="4" y="12" fill="#008000" style="font-family:'Arial',sans-serif">:)</text>
|
||||
</svg>
|
After Width: | Height: | Size: 158 B |
3
res/icon_warning.svg
Normal file
3
res/icon_warning.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<text x="4" y="12" fill="#E0C000" style="font-family:'Arial',sans-serif">:)</text>
|
||||
</svg>
|
After Width: | Height: | Size: 158 B |
31
res/proxy.html
Normal file
31
res/proxy.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<section class="error-ctx">
|
||||
<div class="box%1$s">
|
||||
<div class="content">
|
||||
<span>Client</span>
|
||||
<img src="/.sesimos/res/globe.svg"/>
|
||||
<span>Your Browser</span>
|
||||
</div>
|
||||
<div class="arrow request%2$s"></div>
|
||||
</div>
|
||||
<div class="border%8$s"></div>
|
||||
<div class="box%3$s">
|
||||
<div class="content">
|
||||
<span>Reverse Proxy</span>
|
||||
<h3>%10$03i</h3>
|
||||
<h4>%11$s</h4>
|
||||
<span>%15$s</span>
|
||||
</div>
|
||||
<div class="arrow request%4$s"></div>
|
||||
<div class="arrow response%5$s"></div>
|
||||
</div>
|
||||
<div class="border%9$s"></div>
|
||||
<div class="box%6$s">
|
||||
<div class="content">
|
||||
<span>Server</span>
|
||||
<h3>%12$s</h3>
|
||||
<h4>%13$s</h4>
|
||||
<span>%14$s</span>
|
||||
</div>
|
||||
<div class="arrow response%7$s"></div>
|
||||
</div>
|
||||
</section>
|
66
res/style.css
Normal file
66
res/style.css
Normal file
@@ -0,0 +1,66 @@
|
||||
html{
|
||||
font-family:"Arial",sans-serif;
|
||||
--error:#C00000;
|
||||
--warning:#E0C000;
|
||||
--success:#008000;
|
||||
--info:#606060;
|
||||
--soft:#808080;
|
||||
}
|
||||
|
||||
body{background-color:#F0F0F0;margin:0;}
|
||||
main{max-width:650px;margin:2em auto;}
|
||||
section{margin:2em 1em;background-color:#FFFFFF;border: 1px solid var(--color);border-radius:4px;padding:1em;}
|
||||
h1,h2,h3,h4,h5,h6{text-align:center;color:var(--color);font-weight:normal;}
|
||||
h1{font-size:3em;margin:0.125em 0;}
|
||||
h2{font-size:1.5em;margin:0.25em 0 1em 0;}
|
||||
p{text-align:center;font-size:0.875em;}
|
||||
div.footer{color:var(--soft);font-size:0.75em;text-align:center;margin:2em 0 0.5em 0;}
|
||||
div.footer a{color:var(--soft);}
|
||||
ul,ol{width:fit-content;margin:auto;}
|
||||
pre{width:fit-content;margin:2em auto 0 auto;}
|
||||
|
||||
section.error-ctx{display:flex;padding:0;border:none;}
|
||||
div.box{flex:100% 1 1;border:1px solid var(--info);color:var(--info);position:relative;padding:1em;box-sizing:border-box;text-align:center;}
|
||||
div.box.error{border-color:var(--error);color:var(--error);}
|
||||
div.box.success{border-color:var(--success);color:var(--success);}
|
||||
div.arrow{position:absolute;height:20px;width:30px;z-index:10;background-repeat:no-repeat;background-size:contain;}
|
||||
div.arrow.response{left:-17.5px;bottom:calc(33.3333% - 10px);}
|
||||
div.arrow.request{right:-17.5px;top:calc(33.3333% - 10px);}
|
||||
div.border{flex:1px 0 0;background-color:var(--info);}
|
||||
div.border.error{background-color:var(--error);}
|
||||
div.border.success{background-color:var(--success);}
|
||||
div.content>span{display:block;color:var(--soft);font-size:0.75em;}
|
||||
div.content>img{height:3.75rem;margin:0.75rem auto;display:block;}
|
||||
h3{font-size:2.25em;margin:0.75rem 0 0 0;color:unset;height:2.5rem;}
|
||||
h4{font-size:1em;margin:0 0 0.75rem 0;color:unset;height:1.25rem;}
|
||||
|
||||
div.arrow.request.success{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjRkZGRkZGIiBzdHJva2U9IiMwMDgwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}
|
||||
div.arrow.request.error{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjRkZGRkZGIiBzdHJva2U9IiNDMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}
|
||||
div.arrow.response.success{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzAwODAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}
|
||||
div.arrow.response.error{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iI0MwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}
|
||||
|
||||
@media(prefers-color-scheme:dark){
|
||||
html{color:#FFFFFF;--soft:#404040;}
|
||||
body{background-color:#101010;}
|
||||
section{background-color:#181818;}
|
||||
|
||||
div.arrow.request.success{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjMTgxODE4IiBzdHJva2U9IiMwMDgwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}
|
||||
div.arrow.request.error{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjMTgxODE4IiBzdHJva2U9IiNDMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}
|
||||
div.arrow.response.success{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiMxODE4MTgiIHN0cm9rZT0iIzAwODAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}
|
||||
div.arrow.response.error{background-image:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiMxODE4MTgiIHN0cm9rZT0iI0MwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}
|
||||
}
|
||||
|
||||
@media(min-width:650px){
|
||||
div.box:first-child{border-top-left-radius:4px;border-bottom-left-radius:4px;border-right:none;}
|
||||
div.box:last-child{border-top-right-radius:4px;border-bottom-right-radius:4px;border-left:none;}
|
||||
div.box:not(:last-child):not(:first-child){border-left:none;border-right:none;}
|
||||
}
|
||||
|
||||
@media(max-width:650px){
|
||||
section.error-ctx{flex-direction:column;height:unset;}
|
||||
div.box:first-child{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom:none;padding-top:1em;}
|
||||
div.box:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top:none;padding-bottom:1em;}
|
||||
div.box:not(:last-child):not(:first-child){border-top:none;border-bottom:none;}
|
||||
div.arrow.response{transform:rotate(90deg);top:-10px;left:calc(33.3333% - 22.5px);right:unset;}
|
||||
div.arrow.request{transform:rotate(90deg);bottom:-10px;right:calc(33.3333% - 22.5px);top:unset;}
|
||||
}
|
290
src/async.c
290
src/async.c
@@ -8,74 +8,259 @@
|
||||
|
||||
#include "async.h"
|
||||
#include "logger.h"
|
||||
#include "lib/list.h"
|
||||
#include "lib/utils.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <memory.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ASYNC_MAX_EVENTS 16
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
short events;
|
||||
sock *socket;
|
||||
async_evt_t events;
|
||||
int flags;
|
||||
void (*cb)(void *);
|
||||
void *arg;
|
||||
void (*cb)(void *);
|
||||
void (*to_cb)(void *);
|
||||
void (*err_cb)(void *);
|
||||
void *err_arg;
|
||||
} evt_listen_t;
|
||||
|
||||
typedef struct {
|
||||
int n;
|
||||
evt_listen_t q[256];
|
||||
evt_listen_t *q[ASYNC_MAX_EVENTS];
|
||||
} listen_queue_t;
|
||||
|
||||
static listen_queue_t listen1, listen2, *listen = &listen1;
|
||||
static listen_queue_t listen1, listen2, *listen_q = &listen1;
|
||||
static volatile sig_atomic_t alive = 1;
|
||||
static pthread_t thread = -1;
|
||||
static sem_t lock;
|
||||
static int epoll_fd;
|
||||
|
||||
static short async_a2p(async_evt_t events) {
|
||||
short ret = 0;
|
||||
if (events & ASYNC_IN) ret |= POLLIN;
|
||||
if (events & ASYNC_PRI) ret |= POLLPRI;
|
||||
if (events & ASYNC_OUT) ret |= POLLOUT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int async_a2e(async_evt_t events) {
|
||||
unsigned int ret = 0;
|
||||
if (events & ASYNC_IN) ret |= EPOLLIN;
|
||||
if (events & ASYNC_PRI) ret |= EPOLLPRI;
|
||||
if (events & ASYNC_OUT) ret |= EPOLLOUT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static async_evt_t async_p2a(short events) {
|
||||
async_evt_t ret = 0;
|
||||
if (events & POLLIN) ret |= ASYNC_IN;
|
||||
if (events & POLLPRI) ret |= ASYNC_PRI;
|
||||
if (events & POLLOUT) ret |= ASYNC_OUT;
|
||||
if (events & POLLERR) ret |= ASYNC_ERR;
|
||||
if (events & POLLHUP) ret |= ASYNC_HUP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static async_evt_t async_e2a(unsigned int events) {
|
||||
async_evt_t ret = 0;
|
||||
if (events & EPOLLIN) ret |= ASYNC_IN;
|
||||
if (events & EPOLLPRI) ret |= ASYNC_PRI;
|
||||
if (events & EPOLLOUT) ret |= ASYNC_OUT;
|
||||
if (events & EPOLLERR) ret |= ASYNC_ERR;
|
||||
if (events & EPOLLHUP) ret |= ASYNC_HUP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int async_add_to_queue(evt_listen_t *evt) {
|
||||
// TODO locking
|
||||
memcpy(&listen->q[listen->n++], evt, sizeof(*evt));
|
||||
if (thread != -1) pthread_kill(thread, SIGUSR1);
|
||||
try_again:
|
||||
if (sem_wait(&lock) != 0) {
|
||||
if (errno == EINTR) {
|
||||
goto try_again;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
evt_listen_t *ptr = malloc(sizeof(evt_listen_t));
|
||||
if (ptr == NULL) {
|
||||
sem_post(&lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(ptr, evt, sizeof(*evt));
|
||||
listen_q->q[listen_q->n++] = ptr;
|
||||
|
||||
sem_post(&lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int async(int fd, short events, int flags, void cb(void *), void *arg, void err_cb(void *), void *err_arg) {
|
||||
static int async_exec(evt_listen_t *evt, async_evt_t r_events) {
|
||||
int ret, e = errno;
|
||||
if (r_events & evt->events) {
|
||||
// specified event(s) occurred
|
||||
if (evt->socket && !sock_has_pending(evt->socket)) {
|
||||
evt->err_cb(evt->arg);
|
||||
ret = 0;
|
||||
} else {
|
||||
evt->cb(evt->arg);
|
||||
ret = (evt->flags & ASYNC_KEEP) ? 1 : 0;
|
||||
}
|
||||
} else if (r_events & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||
// error occurred
|
||||
evt->err_cb(evt->arg);
|
||||
ret = 0;
|
||||
} else {
|
||||
// no event occurred
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
logger_set_prefix("");
|
||||
errno = e;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int async_check(evt_listen_t *evt) {
|
||||
struct pollfd fds[1] = {{
|
||||
.fd = evt->fd,
|
||||
.events = async_a2p(evt->events)
|
||||
}};
|
||||
|
||||
// check, if fd is already ready
|
||||
if (poll(fds, 1, 0) == 1) {
|
||||
// fd already ready
|
||||
if (async_exec(evt, async_p2a(fds[0].revents)) == 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int async_add(evt_listen_t *evt) {
|
||||
if (async_check(evt) == 1)
|
||||
return 0;
|
||||
|
||||
int ret = async_add_to_queue(evt);
|
||||
if (ret == 0 && thread != -1)
|
||||
pthread_kill(thread, SIGUSR1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *)) {
|
||||
evt_listen_t evt = {
|
||||
.fd = fd,
|
||||
.socket = NULL,
|
||||
.events = events,
|
||||
.flags = flags,
|
||||
.cb = cb,
|
||||
.arg = arg,
|
||||
.cb = cb,
|
||||
.to_cb = to_cb,
|
||||
.err_cb = err_cb,
|
||||
.err_arg = err_arg,
|
||||
};
|
||||
return async_add_to_queue(&evt);
|
||||
return async_add(&evt);
|
||||
}
|
||||
|
||||
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *)) {
|
||||
evt_listen_t evt = {
|
||||
.fd = s->socket,
|
||||
.socket = s,
|
||||
.events = events,
|
||||
.flags = flags,
|
||||
.arg = arg,
|
||||
.cb = cb,
|
||||
.to_cb = to_cb,
|
||||
.err_cb = err_cb,
|
||||
};
|
||||
return async_add(&evt);
|
||||
}
|
||||
|
||||
int async_init(void) {
|
||||
if (sem_init(&lock, 0, 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
listen1.n = 0;
|
||||
listen2.n = 0;
|
||||
|
||||
if ((epoll_fd = epoll_create1(0)) == -1) {
|
||||
async_free();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void async_free(void) {
|
||||
int e = errno;
|
||||
sem_destroy(&lock);
|
||||
close(epoll_fd);
|
||||
errno = e;
|
||||
}
|
||||
|
||||
void async_thread(void) {
|
||||
struct epoll_event ev, events[ASYNC_MAX_EVENTS];
|
||||
int num_fds;
|
||||
struct pollfd fds[256]; // TODO dynamic
|
||||
long ts, min_ts, cur_ts;
|
||||
listen_queue_t *l;
|
||||
evt_listen_t **local = list_create(sizeof(evt_listen_t *), 16);
|
||||
if (local == NULL) {
|
||||
critical("Unable to create async local list");
|
||||
return;
|
||||
}
|
||||
|
||||
thread = pthread_self();
|
||||
|
||||
// main event loop
|
||||
while (alive) {
|
||||
// swap listen queue
|
||||
listen_queue_t *l = listen;
|
||||
listen = (listen == &listen1) ? &listen2 : &listen1;
|
||||
l = listen_q;
|
||||
listen_q = (listen_q == &listen1) ? &listen2 : &listen1;
|
||||
|
||||
// fill fds with newly added queue entries
|
||||
for (num_fds = 0; num_fds < l->n; num_fds++) {
|
||||
fds[num_fds].fd = l->q[num_fds].fd;
|
||||
fds[num_fds].events = l->q[num_fds].events;
|
||||
// fill local list and epoll instance with previously added queue entries
|
||||
for (int i = 0; i < l->n; i++) {
|
||||
evt_listen_t *evt = l->q[i];
|
||||
local = list_append(local, &evt);
|
||||
if (local == NULL) {
|
||||
critical("Unable to resize async local list");
|
||||
return;
|
||||
}
|
||||
|
||||
ev.events = async_a2e(evt->events);
|
||||
ev.data.ptr = evt;
|
||||
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, evt->fd, &ev) == -1) {
|
||||
critical("Unable to add file descriptor to epoll instance");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// reset size of queue
|
||||
l->n = 0;
|
||||
|
||||
// calculate wait timeout
|
||||
min_ts = -1000, cur_ts = clock_micros();;
|
||||
for (int i = 0; i < list_size(local); i++) {
|
||||
evt_listen_t *evt = local[i];
|
||||
if (!evt->socket) continue;
|
||||
|
||||
ts = evt->socket->ts_last + evt->socket->timeout_us - cur_ts;
|
||||
if (min_ts == -1000 || ts < min_ts) min_ts = ts;
|
||||
}
|
||||
|
||||
if (poll(fds, num_fds, -1) < 0) {
|
||||
if ((num_fds = epoll_wait(epoll_fd, events, ASYNC_MAX_EVENTS, (int) (min_ts / 1000))) == -1) {
|
||||
if (errno == EINTR) {
|
||||
// interrupt
|
||||
errno = 0;
|
||||
continue;
|
||||
} else {
|
||||
// other error
|
||||
critical("Unable to poll for events");
|
||||
@@ -84,28 +269,59 @@ void async_thread(void) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_fds; i++) {
|
||||
evt_listen_t *e = &l->q[i];
|
||||
if (fds[i].revents & e->events) {
|
||||
// specified event(s) occurred
|
||||
e->cb(e->arg);
|
||||
evt_listen_t *evt = events[i].data.ptr;
|
||||
if (async_exec(evt, async_e2a(events[i].events)) == 0) {
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, evt->fd, NULL) == -1) {
|
||||
if (errno == EBADF) {
|
||||
// already closed fd, do not die
|
||||
errno = 0;
|
||||
} else {
|
||||
critical("Unable to remove file descriptor from epoll instance");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e->flags & ASYNC_KEEP)
|
||||
async_add_to_queue(e);
|
||||
} else if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||
// error occurred
|
||||
e->err_cb(e->err_arg);
|
||||
} else {
|
||||
// no event occurred
|
||||
async_add_to_queue(e);
|
||||
local = list_delete(local, &evt);
|
||||
if (local == NULL) {
|
||||
critical("Unable to resize async local list");
|
||||
return;
|
||||
}
|
||||
|
||||
free(evt);
|
||||
}
|
||||
|
||||
// reset errno to prevent strange behaviour
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
// reset size of queue
|
||||
l->n = 0;
|
||||
// check, if some socket ran into a timeout
|
||||
cur_ts = clock_micros();
|
||||
for (int i = 0; i < list_size(local); i++) {
|
||||
evt_listen_t *evt = local[i];
|
||||
if (!evt->socket) continue;
|
||||
|
||||
if ((cur_ts - evt->socket->ts_last) >= evt->socket->timeout_us) {
|
||||
evt->to_cb(evt->arg);
|
||||
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, evt->fd, NULL) == -1) {
|
||||
if (errno == EBADF) {
|
||||
// already closed fd, do not die
|
||||
errno = 0;
|
||||
} else {
|
||||
critical("Unable to remove file descriptor from epoll instance");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
local = list_remove(local, i--);
|
||||
}
|
||||
}
|
||||
logger_set_prefix("");
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
// cleanup
|
||||
for (int i = 0; i < list_size(local); i++) {
|
||||
free(local[i]);
|
||||
}
|
||||
list_free(local);
|
||||
}
|
||||
|
||||
void async_stop(void) {
|
||||
|
21
src/async.h
21
src/async.h
@@ -9,11 +9,28 @@
|
||||
#ifndef SESIMOS_ASYNC_H
|
||||
#define SESIMOS_ASYNC_H
|
||||
|
||||
#include <poll.h>
|
||||
#include "lib/sock.h"
|
||||
|
||||
#define ASYNC_KEEP 1
|
||||
|
||||
int async(int fd, short events, int flags, void cb(void *), void *arg, void err_cb(void *), void *err_arg);
|
||||
#define ASYNC_IN 0x01
|
||||
#define ASYNC_PRI 0x02
|
||||
#define ASYNC_OUT 0x04
|
||||
#define ASYNC_ERR 0x08
|
||||
#define ASYNC_HUP 0x10
|
||||
|
||||
#define ASYNC_WAIT_READ ASYNC_IN
|
||||
#define ASYNC_WAIT_WRITE ASYNC_OUT
|
||||
|
||||
typedef unsigned int async_evt_t;
|
||||
|
||||
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *));
|
||||
|
||||
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *));
|
||||
|
||||
int async_init(void);
|
||||
|
||||
void async_free(void);
|
||||
|
||||
void async_thread(void);
|
||||
|
||||
|
@@ -6,7 +6,6 @@
|
||||
* @date 2020-12-19
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
#include "logger.h"
|
||||
#include "cache_handler.h"
|
||||
#include "lib/utils.h"
|
||||
@@ -20,7 +19,9 @@
|
||||
#include <openssl/evp.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define CACHE_BUF_SIZE 16
|
||||
|
||||
@@ -28,6 +29,7 @@
|
||||
static magic_t magic;
|
||||
static pthread_t thread;
|
||||
static sem_t sem_free, sem_used, sem_lock;
|
||||
volatile sig_atomic_t alive = 1;
|
||||
|
||||
typedef struct {
|
||||
int rd;
|
||||
@@ -203,7 +205,7 @@ static void cache_process_entry(cache_entry_t *entry) {
|
||||
static void *cache_thread(void *arg) {
|
||||
logger_set_name("cache");
|
||||
|
||||
while (server_alive) {
|
||||
while (alive) {
|
||||
pthread_testcancel();
|
||||
if (sem_wait(&sem_used) != 0) {
|
||||
if (errno == EINTR) {
|
||||
@@ -280,6 +282,11 @@ int cache_init(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cache_stop(void) {
|
||||
alive = 0;
|
||||
pthread_kill(thread, SIGUSR1);
|
||||
}
|
||||
|
||||
int cache_join(void) {
|
||||
return pthread_join(thread, NULL);
|
||||
}
|
||||
|
@@ -35,6 +35,8 @@ typedef struct {
|
||||
|
||||
int cache_init(void);
|
||||
|
||||
void cache_stop(void);
|
||||
|
||||
int cache_join(void);
|
||||
|
||||
void cache_mark_dirty(cache_t *cache, const char *filename);
|
||||
|
844
src/client.c
844
src/client.c
@@ -1,844 +0,0 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Client connection and request handlers
|
||||
* @file src/client.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2020-12-03
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "lib/utils.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/sock.h"
|
||||
#include "lib/http.h"
|
||||
#include "lib/proxy.h"
|
||||
#include "lib/fastcgi.h"
|
||||
#include "cache_handler.h"
|
||||
#include "lib/geoip.h"
|
||||
#include "lib/compress.h"
|
||||
#include "lib/websocket.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <signal.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
static const char *color_table[] = {"\x1B[31m", "\x1B[32m", "\x1B[33m", "\x1B[34m", "\x1B[35m", "\x1B[36m"};
|
||||
|
||||
host_config_t *get_host_config(const char *host) {
|
||||
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
|
||||
host_config_t *hc = &config.hosts[i];
|
||||
if (hc->type == CONFIG_TYPE_UNSET) break;
|
||||
if (strcmp(hc->name, host) == 0) return hc;
|
||||
if (hc->name[0] == '*' && hc->name[1] == '.') {
|
||||
const char *pos = strstr(host, hc->name + 1);
|
||||
if (pos != NULL && strlen(pos) == strlen(hc->name + 1)) return hc;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
void client_terminate(int _) {
|
||||
server_keep_alive = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
int client_request_handler(client_ctx_t *cctx, sock *client, unsigned long client_num, unsigned int req_num, const char *restrict log_client_prefix) {
|
||||
struct timespec begin, end;
|
||||
long ret;
|
||||
|
||||
char buf0[1024], buf1[1024];
|
||||
char msg_buf[8192], msg_pre_buf_1[4096], msg_pre_buf_2[4096], err_msg[256];
|
||||
char msg_content[1024];
|
||||
char buffer[CHUNK_SIZE];
|
||||
char host[256];
|
||||
const char *host_ptr, *hdr_connection;
|
||||
char log_req_prefix[512];
|
||||
|
||||
msg_buf[0] = 0;
|
||||
err_msg[0] = 0;
|
||||
msg_content[0] = 0;
|
||||
|
||||
host_config_t *conf = NULL;
|
||||
FILE *file = NULL;
|
||||
|
||||
long content_length = 0;
|
||||
int accept_if_modified_since = 0;
|
||||
int use_fastcgi = 0;
|
||||
int use_proxy = 0;
|
||||
int p_len;
|
||||
|
||||
fastcgi_conn fcgi_conn = {.socket = 0, .req_id = 0, .ctx = cctx};
|
||||
http_status custom_status;
|
||||
|
||||
http_res res = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0, .hdr.last_field_num = -1};
|
||||
http_status_ctx ctx = {.status = 0, .origin = NONE, .ws_key = NULL};
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &begin);
|
||||
|
||||
ret = sock_poll_read(&client, NULL, NULL, 1, NULL, NULL, CLIENT_TIMEOUT * 1000);
|
||||
|
||||
http_add_header_field(&res.hdr, "Date", http_get_date(buf0, sizeof(buf0)));
|
||||
http_add_header_field(&res.hdr, "Server", SERVER_STR);
|
||||
if (ret <= 0) {
|
||||
if (errno != 0) return 1;
|
||||
|
||||
cctx->c_keep_alive = 0;
|
||||
res.status = http_get_status(408);
|
||||
goto respond;
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &begin);
|
||||
|
||||
http_req req;
|
||||
ret = http_receive_request(client, &req);
|
||||
if (ret != 0) {
|
||||
cctx->c_keep_alive = 0;
|
||||
if (ret < 0) {
|
||||
goto abort;
|
||||
} else if (ret == 1) {
|
||||
sprintf(err_msg, "Unable to parse http header: Invalid header format.");
|
||||
} else if (ret == 2) {
|
||||
sprintf(err_msg, "Unable to parse http header: Invalid method.");
|
||||
} else if (ret == 3) {
|
||||
sprintf(err_msg, "Unable to parse http header: Invalid version.");
|
||||
} else if (ret == 4) {
|
||||
sprintf(err_msg, "Unable to parse http header: Header contains illegal characters.");
|
||||
} else if (ret == 5) {
|
||||
sprintf(err_msg, "Unable to parse http header: End of header not found.");
|
||||
}
|
||||
res.status = http_get_status(400);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
hdr_connection = http_get_header_field(&req.hdr, "Connection");
|
||||
cctx->c_keep_alive = (hdr_connection != NULL && (strstr(hdr_connection, "keep-alive") != NULL || strstr(hdr_connection, "Keep-Alive") != NULL));
|
||||
host_ptr = http_get_header_field(&req.hdr, "Host");
|
||||
if (host_ptr != NULL && strlen(host_ptr) > 255) {
|
||||
host[0] = 0;
|
||||
res.status = http_get_status(400);
|
||||
sprintf(err_msg, "Host header field is too long.");
|
||||
goto respond;
|
||||
} else if (host_ptr == NULL || strchr(host_ptr, '/') != NULL) {
|
||||
if (strchr(cctx->addr, ':') == NULL) {
|
||||
strcpy(host, cctx->addr);
|
||||
} else {
|
||||
sprintf(host, "[%s]", cctx->addr);
|
||||
}
|
||||
res.status = http_get_status(400);
|
||||
sprintf(err_msg, "The client provided no or an invalid Host header field.");
|
||||
goto respond;
|
||||
} else {
|
||||
strcpy(host, host_ptr);
|
||||
}
|
||||
|
||||
sprintf(log_req_prefix, "[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, host, CLR_STR, log_client_prefix);
|
||||
logger_set_prefix(log_req_prefix);
|
||||
info(BLD_STR "%s %s", req.method, req.uri);
|
||||
|
||||
conf = get_host_config(host);
|
||||
if (conf == NULL) {
|
||||
info("Unknown host, redirecting to default");
|
||||
res.status = http_get_status(307);
|
||||
sprintf(buf0, "https://%s%s", DEFAULT_HOST, req.uri);
|
||||
http_add_header_field(&res.hdr, "Location", buf0);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
http_uri uri;
|
||||
unsigned char dir_mode = (conf->type == CONFIG_TYPE_LOCAL ? conf->local.dir_mode : URI_DIR_MODE_NO_VALIDATION);
|
||||
ret = uri_init(&uri, conf->local.webroot, req.uri, dir_mode);
|
||||
if (ret != 0) {
|
||||
if (ret == 1) {
|
||||
sprintf(err_msg, "Invalid URI: has to start with slash.");
|
||||
res.status = http_get_status(400);
|
||||
} else if (ret == 2) {
|
||||
sprintf(err_msg, "Invalid URI: contains relative path change (/../).");
|
||||
res.status = http_get_status(400);
|
||||
} else if (ret == 3) {
|
||||
sprintf(err_msg, "The specified webroot directory does not exist.");
|
||||
res.status = http_get_status(404);
|
||||
} else {
|
||||
res.status = http_get_status(500);
|
||||
}
|
||||
goto respond;
|
||||
}
|
||||
|
||||
if (dir_mode != URI_DIR_MODE_NO_VALIDATION) {
|
||||
ssize_t size = sizeof(buf0);
|
||||
url_decode(req.uri, buf0, &size);
|
||||
int change_proto = strncmp(uri.uri, "/.well-known/", 13) != 0 && !client->enc;
|
||||
if (strcmp(uri.uri, buf0) != 0 || change_proto) {
|
||||
res.status = http_get_status(308);
|
||||
size = url_encode(uri.uri, strlen(uri.uri), buf0, sizeof(buf0));
|
||||
if (change_proto) {
|
||||
p_len = snprintf(buf1, sizeof(buf1), "https://%s%s", host, buf0);
|
||||
if (p_len < 0 || p_len >= sizeof(buf1)) {
|
||||
res.status = http_get_status(500);
|
||||
error("Header field 'Location' too long");
|
||||
goto respond;
|
||||
}
|
||||
http_add_header_field(&res.hdr, "Location", buf1);
|
||||
} else {
|
||||
http_add_header_field(&res.hdr, "Location", buf0);
|
||||
}
|
||||
goto respond;
|
||||
}
|
||||
} else if (!client->enc) {
|
||||
res.status = http_get_status(308);
|
||||
sprintf(buf0, "https://%s%s", host, req.uri);
|
||||
http_add_header_field(&res.hdr, "Location", buf0);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
if (conf->type == CONFIG_TYPE_LOCAL) {
|
||||
if (strcmp(req.method, "TRACE") == 0) {
|
||||
res.status = http_get_status(200);
|
||||
http_add_header_field(&res.hdr, "Content-Type", "message/http");
|
||||
|
||||
content_length = snprintf(msg_buf, sizeof(msg_buf) - content_length, "%s %s HTTP/%s\r\n", req.method, req.uri, req.version);
|
||||
for (int i = 0; i < req.hdr.field_num; i++) {
|
||||
const http_field *f = &req.hdr.fields[i];
|
||||
content_length += snprintf(msg_buf + content_length, sizeof(msg_buf) - content_length, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
|
||||
}
|
||||
|
||||
goto respond;
|
||||
}
|
||||
|
||||
if (strncmp(uri.req_path, "/.well-known/", 13) == 0) {
|
||||
http_add_header_field(&res.hdr, "Access-Control-Allow-Origin", "*");
|
||||
}
|
||||
|
||||
if (strncmp(uri.req_path, "/.well-known/", 13) != 0 && strstr(uri.path, "/.") != NULL) {
|
||||
res.status = http_get_status(403);
|
||||
sprintf(err_msg, "Parts of this URI are hidden.");
|
||||
goto respond;
|
||||
} else if (uri.filename == NULL && (int) uri.is_static && (int) uri.is_dir && strlen(uri.pathinfo) == 0) {
|
||||
res.status = http_get_status(403);
|
||||
sprintf(err_msg, "It is not allowed to list the contents of this directory.");
|
||||
goto respond;
|
||||
} else if (uri.filename == NULL && (int) !uri.is_static && (int) uri.is_dir && strlen(uri.pathinfo) == 0) {
|
||||
// TODO list directory contents
|
||||
res.status = http_get_status(501);
|
||||
sprintf(err_msg, "Listing contents of an directory is currently not implemented.");
|
||||
goto respond;
|
||||
} else if (uri.filename == NULL || (strlen(uri.pathinfo) > 0 && (int) uri.is_static)) {
|
||||
res.status = http_get_status(404);
|
||||
goto respond;
|
||||
} else if (strlen(uri.pathinfo) != 0 && conf->local.dir_mode != URI_DIR_MODE_INFO) {
|
||||
res.status = http_get_status(404);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
if (uri.is_static) {
|
||||
res.status = http_get_status(200);
|
||||
http_add_header_field(&res.hdr, "Accept-Ranges", "bytes");
|
||||
if (strcmp(req.method, "GET") != 0 && strcmp(req.method, "HEAD") != 0) {
|
||||
res.status = http_get_status(405);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
if (http_get_header_field(&req.hdr, "Content-Length") != NULL || http_get_header_field(&req.hdr, "Transfer-Encoding") != NULL) {
|
||||
res.status = http_get_status(400);
|
||||
sprintf(err_msg, "A GET request must not contain a payload");
|
||||
goto respond;
|
||||
}
|
||||
|
||||
cache_init_uri(conf->cache, &uri);
|
||||
|
||||
const char *last_modified = http_format_date(uri.meta->stat.st_mtime, buf0, sizeof(buf0));
|
||||
http_add_header_field(&res.hdr, "Last-Modified", last_modified);
|
||||
sprintf(buf1, "%s; charset=%s", uri.meta->type, uri.meta->charset);
|
||||
http_add_header_field(&res.hdr, "Content-Type", buf1);
|
||||
|
||||
|
||||
const char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding");
|
||||
int enc = 0;
|
||||
if (accept_encoding != NULL) {
|
||||
if (uri.meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != NULL) {
|
||||
file = fopen(uri.meta->filename_comp_br, "rb");
|
||||
if (file == NULL) {
|
||||
cache_mark_dirty(conf->cache, uri.filename);
|
||||
} else {
|
||||
http_add_header_field(&res.hdr, "Content-Encoding", "br");
|
||||
enc = COMPRESS_BR;
|
||||
}
|
||||
} else if (uri.meta->filename_comp_gz[0] != 0 && strstr(accept_encoding, "gzip") != NULL) {
|
||||
file = fopen(uri.meta->filename_comp_gz, "rb");
|
||||
if (file == NULL) {
|
||||
cache_mark_dirty(conf->cache, uri.filename);
|
||||
} else {
|
||||
http_add_header_field(&res.hdr, "Content-Encoding", "gzip");
|
||||
enc = COMPRESS_GZ;
|
||||
}
|
||||
}
|
||||
if (enc != 0) {
|
||||
http_add_header_field(&res.hdr, "Vary", "Accept-Encoding");
|
||||
}
|
||||
}
|
||||
|
||||
if (uri.meta->etag[0] != 0) {
|
||||
if (enc) {
|
||||
sprintf(buf0, "%s-%s", uri.meta->etag, (enc & COMPRESS_BR) ? "br" : (enc & COMPRESS_GZ) ? "gzip" : "");
|
||||
http_add_header_field(&res.hdr, "ETag", buf0);
|
||||
} else {
|
||||
http_add_header_field(&res.hdr, "ETag", uri.meta->etag);
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(uri.meta->type, "text/", 5) == 0) {
|
||||
http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=3600");
|
||||
} else {
|
||||
http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=86400");
|
||||
}
|
||||
|
||||
const char *if_modified_since = http_get_header_field(&req.hdr, "If-Modified-Since");
|
||||
const char *if_none_match = http_get_header_field(&req.hdr, "If-None-Match");
|
||||
if ((if_none_match != NULL && strstr(if_none_match, uri.meta->etag) == NULL) ||
|
||||
(accept_if_modified_since && if_modified_since != NULL && strcmp(if_modified_since, last_modified) == 0))
|
||||
{
|
||||
res.status = http_get_status(304);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
const char *range = http_get_header_field(&req.hdr, "Range");
|
||||
if (range != NULL) {
|
||||
if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) {
|
||||
res.status = http_get_status(416);
|
||||
http_remove_header_field(&res.hdr, "Content-Type", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Last-Modified", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "ETag", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Cache-Control", HTTP_REMOVE_ALL);
|
||||
goto respond;
|
||||
}
|
||||
range += 6;
|
||||
char *ptr = strchr(range, '-');
|
||||
if (ptr == NULL) {
|
||||
res.status = http_get_status(416);
|
||||
goto respond;
|
||||
}
|
||||
file = fopen(uri.filename, "rb");
|
||||
fseek(file, 0, SEEK_END);
|
||||
unsigned long file_len = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if (file_len == 0) {
|
||||
content_length = 0;
|
||||
goto respond;
|
||||
}
|
||||
long num1 = 0;
|
||||
long num2 = (long) file_len - 1;
|
||||
|
||||
if (ptr != range) num1 = (long) strtoul(range, NULL, 10);
|
||||
if (ptr[1] != 0) num2 = (long) strtoul(ptr + 1, NULL, 10);
|
||||
|
||||
if (num1 >= file_len || num2 >= file_len || num1 > num2) {
|
||||
res.status = http_get_status(416);
|
||||
goto respond;
|
||||
}
|
||||
sprintf(buf0, "bytes %li-%li/%li", num1, num2, file_len);
|
||||
http_add_header_field(&res.hdr, "Content-Range", buf0);
|
||||
|
||||
res.status = http_get_status(206);
|
||||
fseek(file, num1, SEEK_SET);
|
||||
content_length = num2 - num1 + 1;
|
||||
|
||||
goto respond;
|
||||
}
|
||||
|
||||
if (file == NULL) {
|
||||
file = fopen(uri.filename, "rb");
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
content_length = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
} else {
|
||||
int mode;
|
||||
if (strcmp(uri.filename + strlen(uri.filename) - 4, ".ncr") == 0) {
|
||||
mode = FASTCGI_SESIMOS;
|
||||
} else if (strcmp(uri.filename + strlen(uri.filename) - 4, ".php") == 0) {
|
||||
mode = FASTCGI_PHP;
|
||||
} else {
|
||||
res.status = http_get_status(500);
|
||||
error("Invalid FastCGI extension: %s", uri.filename);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
stat(uri.filename, &statbuf);
|
||||
char *last_modified = http_format_date(statbuf.st_mtime, buf0, sizeof(buf0));
|
||||
http_add_header_field(&res.hdr, "Last-Modified", last_modified);
|
||||
|
||||
res.status = http_get_status(200);
|
||||
if (fastcgi_init(&fcgi_conn, mode, client_num, req_num, client, &req, &uri) != 0) {
|
||||
res.status = http_get_status(503);
|
||||
sprintf(err_msg, "Unable to communicate with FastCGI socket.");
|
||||
goto respond;
|
||||
}
|
||||
|
||||
const char *client_content_length = http_get_header_field(&req.hdr, "Content-Length");
|
||||
const char *client_transfer_encoding = http_get_header_field(&req.hdr, "Transfer-Encoding");
|
||||
if (client_content_length != NULL) {
|
||||
unsigned long client_content_len = strtoul(client_content_length, NULL, 10);
|
||||
ret = fastcgi_receive(&fcgi_conn, client, client_content_len);
|
||||
} else if (client_transfer_encoding != NULL && strstr(client_transfer_encoding, "chunked") != NULL) {
|
||||
ret = fastcgi_receive_chunked(&fcgi_conn, client);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
if (ret != 0) {
|
||||
if (ret < 0) {
|
||||
goto abort;
|
||||
} else {
|
||||
sprintf(err_msg, "Unable to communicate with FastCGI socket.");
|
||||
}
|
||||
res.status = http_get_status(502);
|
||||
goto respond;
|
||||
}
|
||||
fastcgi_close_stdin(&fcgi_conn);
|
||||
|
||||
ret = fastcgi_header(&fcgi_conn, &res, err_msg);
|
||||
if (ret != 0) {
|
||||
if (ret < 0) goto abort;
|
||||
goto respond;
|
||||
}
|
||||
|
||||
const char *status = http_get_header_field(&res.hdr, "Status");
|
||||
if (status != NULL) {
|
||||
int status_code = (int) strtoul(status, NULL, 10);
|
||||
res.status = http_get_status(status_code);
|
||||
http_remove_header_field(&res.hdr, "Status", HTTP_REMOVE_ALL);
|
||||
if (res.status == NULL && status_code >= 100 && status_code <= 999) {
|
||||
custom_status.code = status_code;
|
||||
strcpy(custom_status.type, "");
|
||||
strcpy(custom_status.msg, status + 4);
|
||||
res.status = &custom_status;
|
||||
} else if (res.status == NULL) {
|
||||
res.status = http_get_status(500);
|
||||
sprintf(err_msg, "The status code was set to an invalid or unknown value.");
|
||||
goto respond;
|
||||
}
|
||||
}
|
||||
|
||||
const char *content_length_f = http_get_header_field(&res.hdr, "Content-Length");
|
||||
content_length = (content_length_f == NULL) ? -1 : strtol(content_length_f, NULL, 10);
|
||||
|
||||
const char *content_type = http_get_header_field(&res.hdr, "Content-Type");
|
||||
const char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding");
|
||||
if (content_encoding == NULL &&
|
||||
content_type != NULL &&
|
||||
strncmp(content_type, "text/html", 9) == 0 &&
|
||||
content_length != -1 &&
|
||||
content_length <= sizeof(msg_content) - 1)
|
||||
{
|
||||
fastcgi_dump(&fcgi_conn, msg_content, sizeof(msg_content));
|
||||
goto respond;
|
||||
}
|
||||
|
||||
use_fastcgi = 1;
|
||||
|
||||
if (content_length != -1 && content_length < 1024000) {
|
||||
use_fastcgi |= FASTCGI_COMPRESS_HOLD;
|
||||
}
|
||||
|
||||
content_length = -1;
|
||||
|
||||
int http_comp = http_get_compression(&req, &res);
|
||||
if (http_comp & COMPRESS) {
|
||||
if (http_comp & COMPRESS_BR) {
|
||||
use_fastcgi |= FASTCGI_COMPRESS_BR;
|
||||
sprintf(buf0, "br");
|
||||
} else if (http_comp & COMPRESS_GZ) {
|
||||
use_fastcgi |= FASTCGI_COMPRESS_GZ;
|
||||
sprintf(buf0, "gzip");
|
||||
}
|
||||
http_add_header_field(&res.hdr, "Vary", "Accept-Encoding");
|
||||
http_add_header_field(&res.hdr, "Content-Encoding", buf0);
|
||||
http_remove_header_field(&res.hdr, "Content-Length", HTTP_REMOVE_ALL);
|
||||
}
|
||||
|
||||
if (http_get_header_field(&res.hdr, "Content-Length") == NULL) {
|
||||
http_add_header_field(&res.hdr, "Transfer-Encoding", "chunked");
|
||||
}
|
||||
}
|
||||
} else if (conf->type == CONFIG_TYPE_REVERSE_PROXY) {
|
||||
info("Reverse proxy for " BLD_STR "%s:%i" CLR_STR, conf->proxy.hostname, conf->proxy.port);
|
||||
http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL);
|
||||
|
||||
ret = proxy_init(&req, &res, &ctx, conf, client, cctx, &custom_status, err_msg);
|
||||
use_proxy = (ret == 0);
|
||||
|
||||
if (res.status->code == 101) {
|
||||
const char *connection = http_get_header_field(&res.hdr, "Connection");
|
||||
const char *upgrade = http_get_header_field(&res.hdr, "Upgrade");
|
||||
if (connection != NULL && upgrade != NULL &&
|
||||
(strstr(connection, "upgrade") != NULL || strstr(connection, "Upgrade") != NULL) &&
|
||||
strcmp(upgrade, "websocket") == 0)
|
||||
{
|
||||
const char *ws_accept = http_get_header_field(&res.hdr, "Sec-WebSocket-Accept");
|
||||
if (ws_calc_accept_key(ctx.ws_key, buf0) == 0) {
|
||||
use_proxy = (strcmp(buf0, ws_accept) == 0) ? 2 : 1;
|
||||
}
|
||||
} else {
|
||||
ctx.status = 101;
|
||||
ctx.origin = INTERNAL;
|
||||
res.status = http_get_status(501);
|
||||
}
|
||||
}
|
||||
|
||||
// Let 300 be formatted by origin server
|
||||
if (use_proxy && res.status->code >= 301 && res.status->code < 600) {
|
||||
const char *content_type = http_get_header_field(&res.hdr, "Content-Type");
|
||||
const char *content_length_f = http_get_header_field(&res.hdr, "Content-Length");
|
||||
const char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding");
|
||||
if (content_encoding == NULL && content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0) {
|
||||
long content_len = strtol(content_length_f, NULL, 10);
|
||||
if (content_len <= sizeof(msg_content) - 1) {
|
||||
if (ctx.status != 101) {
|
||||
ctx.status = res.status->code;
|
||||
ctx.origin = res.status->code >= 400 ? SERVER : NONE;
|
||||
}
|
||||
use_proxy = 0;
|
||||
proxy_dump(msg_content, content_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding");
|
||||
if (use_proxy && content_encoding == NULL) {
|
||||
int http_comp = http_get_compression(&req, &res);
|
||||
if (http_comp & COMPRESS_BR) {
|
||||
use_proxy |= PROXY_COMPRESS_BR;
|
||||
} else if (http_comp & COMPRESS_GZ) {
|
||||
use_proxy |= PROXY_COMPRESS_GZ;
|
||||
}
|
||||
}
|
||||
|
||||
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
|
||||
http_remove_header_field(&res.hdr, "Transfer-Encoding", HTTP_REMOVE_ALL);
|
||||
ret = sprintf(buf0, "%s%s%s",
|
||||
(use_proxy & PROXY_COMPRESS_BR) ? "br" :
|
||||
((use_proxy & PROXY_COMPRESS_GZ) ? "gzip" : ""),
|
||||
((use_proxy & PROXY_COMPRESS) && chunked) ? ", " : "",
|
||||
chunked ? "chunked" : "");
|
||||
if (ret > 0) {
|
||||
http_add_header_field(&res.hdr, "Transfer-Encoding", buf0);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
error("Unknown host type: %i", conf->type);
|
||||
res.status = http_get_status(501);
|
||||
}
|
||||
|
||||
respond:
|
||||
if (!use_proxy) {
|
||||
if (conf != NULL && conf->type == CONFIG_TYPE_LOCAL && uri.is_static && res.status->code == 405) {
|
||||
http_add_header_field(&res.hdr, "Allow", "GET, HEAD, TRACE");
|
||||
}
|
||||
if (http_get_header_field(&res.hdr, "Accept-Ranges") == NULL) {
|
||||
http_add_header_field(&res.hdr, "Accept-Ranges", "none");
|
||||
}
|
||||
if (!use_fastcgi && file == NULL) {
|
||||
http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Cache-Control", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Content-Type", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Content-Encoding", HTTP_REMOVE_ALL);
|
||||
http_add_header_field(&res.hdr, "Date", http_get_date(buf0, sizeof(buf0)));
|
||||
http_add_header_field(&res.hdr, "Server", SERVER_STR);
|
||||
http_add_header_field(&res.hdr, "Cache-Control", "no-cache");
|
||||
http_add_header_field(&res.hdr, "Content-Type", "text/html; charset=UTF-8");
|
||||
|
||||
// TODO list Locations on 3xx Redirects
|
||||
const http_doc_info *info = http_get_status_info(res.status);
|
||||
const http_status_msg *http_msg = http_get_error_msg(res.status);
|
||||
|
||||
if (msg_content[0] == 0) {
|
||||
if (res.status->code >= 300 && res.status->code < 400) {
|
||||
const char *location = http_get_header_field(&res.hdr, "Location");
|
||||
if (location != NULL) {
|
||||
snprintf(msg_content, sizeof(msg_content), "<ul>\n\t<li><a href=\"%s\">%s</a></li>\n</ul>\n", location, location);
|
||||
}
|
||||
}
|
||||
} else if (strncmp(msg_content, "<!DOCTYPE html>", 15) == 0 || strncmp(msg_content, "<html", 5) == 0) {
|
||||
msg_content[0] = 0;
|
||||
// TODO let relevant information pass?
|
||||
}
|
||||
|
||||
char *proxy_doc = "";
|
||||
if (conf != NULL && conf->type == CONFIG_TYPE_REVERSE_PROXY) {
|
||||
const http_status *status = http_get_status(ctx.status);
|
||||
char stat_str[8];
|
||||
sprintf(stat_str, "%03i", ctx.status);
|
||||
sprintf(msg_pre_buf_2, http_proxy_document,
|
||||
" success",
|
||||
(ctx.origin == CLIENT_REQ) ? " error" : " success",
|
||||
(ctx.origin == INTERNAL) ? " error" : " success",
|
||||
(ctx.origin == SERVER_REQ) ? " error" : (ctx.status == 0 ? "" : " success"),
|
||||
(ctx.origin == CLIENT_RES) ? " error" : " success",
|
||||
(ctx.origin == SERVER) ? " error" : (ctx.status == 0 ? "" : " success"),
|
||||
(ctx.origin == SERVER_RES) ? " error" : (ctx.status == 0 ? "" : " success"),
|
||||
(ctx.origin == INTERNAL) ? " error" : " success",
|
||||
(ctx.origin == INTERNAL || ctx.origin == SERVER) ? " error" : " success",
|
||||
res.status->code,
|
||||
res.status->msg,
|
||||
(ctx.status == 0) ? "???" : stat_str,
|
||||
(status != NULL) ? status->msg : "",
|
||||
host);
|
||||
proxy_doc = msg_pre_buf_2;
|
||||
}
|
||||
|
||||
sprintf(msg_pre_buf_1, info->doc, res.status->code, res.status->msg, http_msg != NULL ? http_msg->msg : "", err_msg[0] != 0 ? err_msg : "");
|
||||
content_length = snprintf(msg_buf, sizeof(msg_buf), http_default_document, res.status->code,
|
||||
res.status->msg, msg_pre_buf_1, info->mode, info->icon, info->color, host,
|
||||
proxy_doc, msg_content[0] != 0 ? msg_content : "");
|
||||
}
|
||||
if (content_length >= 0) {
|
||||
sprintf(buf0, "%li", content_length);
|
||||
http_remove_header_field(&res.hdr, "Content-Length", HTTP_REMOVE_ALL);
|
||||
http_add_header_field(&res.hdr, "Content-Length", buf0);
|
||||
} else if (http_get_header_field(&res.hdr, "Transfer-Encoding") == NULL) {
|
||||
cctx->s_keep_alive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int close_proxy = 0;
|
||||
if (use_proxy != 2) {
|
||||
const char *conn = http_get_header_field(&res.hdr, "Connection");
|
||||
close_proxy = (conn == NULL || (strstr(conn, "keep-alive") == NULL && strstr(conn, "Keep-Alive") == NULL));
|
||||
http_remove_header_field(&res.hdr, "Connection", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Keep-Alive", HTTP_REMOVE_ALL);
|
||||
if (cctx->s_keep_alive && cctx->c_keep_alive) {
|
||||
http_add_header_field(&res.hdr, "Connection", "keep-alive");
|
||||
sprintf(buf0, "timeout=%i, max=%i", CLIENT_TIMEOUT, REQ_PER_CONNECTION);
|
||||
http_add_header_field(&res.hdr, "Keep-Alive", buf0);
|
||||
} else {
|
||||
http_add_header_field(&res.hdr, "Connection", "close");
|
||||
}
|
||||
}
|
||||
|
||||
http_send_response(client, &res);
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
const char *location = http_get_header_field(&res.hdr, "Location");
|
||||
unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
||||
info("%s%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), use_proxy ? "-> " : "", res.status->code,
|
||||
res.status->msg, location != NULL ? " -> " : "", location != NULL ? location : "",
|
||||
format_duration(micros, buf0), CLR_STR);
|
||||
|
||||
// TODO access/error log file
|
||||
|
||||
if (use_proxy == 2) {
|
||||
// WebSocket
|
||||
info("Upgrading connection to WebSocket connection");
|
||||
ret = ws_handle_connection(client, &proxy);
|
||||
if (ret != 0) {
|
||||
cctx->c_keep_alive = 0;
|
||||
close_proxy = 1;
|
||||
}
|
||||
info("WebSocket connection closed");
|
||||
} else if (strcmp(req.method, "HEAD") != 0) {
|
||||
// default response
|
||||
unsigned long snd_len = 0;
|
||||
unsigned long len;
|
||||
if (msg_buf[0] != 0) {
|
||||
ret = sock_send(client, msg_buf, content_length, 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
}
|
||||
snd_len += ret;
|
||||
} else if (file != NULL) {
|
||||
while (snd_len < content_length) {
|
||||
len = fread(buffer, 1, CHUNK_SIZE, file);
|
||||
if (snd_len + len > content_length) {
|
||||
len = content_length - snd_len;
|
||||
}
|
||||
ret = sock_send(client, buffer, len, feof(file) ? 0 : MSG_MORE);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
break;
|
||||
}
|
||||
snd_len += ret;
|
||||
}
|
||||
} else if (use_fastcgi) {
|
||||
const char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||
int chunked = (transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL);
|
||||
|
||||
int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD));
|
||||
ret = fastcgi_send(&fcgi_conn, client, flags);
|
||||
} else if (use_proxy) {
|
||||
const char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||
int chunked = transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL;
|
||||
|
||||
const char *content_len = http_get_header_field(&res.hdr, "Content-Length");
|
||||
unsigned long len_to_send = 0;
|
||||
if (content_len != NULL) {
|
||||
len_to_send = strtol(content_len, NULL, 10);
|
||||
}
|
||||
|
||||
int flags = (chunked ? PROXY_CHUNKED : 0) | (use_proxy & PROXY_COMPRESS);
|
||||
ret = proxy_send(client, len_to_send, flags);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
cctx->c_keep_alive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (close_proxy && proxy.socket != 0) {
|
||||
info(BLUE_STR "Closing proxy connection");
|
||||
sock_close(&proxy);
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
||||
info("Transfer complete: %s", format_duration(micros, buf0));
|
||||
|
||||
uri_free(&uri);
|
||||
abort:
|
||||
if (fcgi_conn.socket != 0) {
|
||||
shutdown(fcgi_conn.socket, SHUT_RDWR);
|
||||
close(fcgi_conn.socket);
|
||||
fcgi_conn.socket = 0;
|
||||
}
|
||||
http_free_req(&req);
|
||||
http_free_res(&res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int client_connection_handler(client_ctx_t *ctx, unsigned long client_num, const char *restrict log_conn_prefix, const char *restrict log_client_prefix) {
|
||||
struct timespec begin, end;
|
||||
int ret;
|
||||
char buf[1024];
|
||||
sock *client = &ctx->socket;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &begin);
|
||||
|
||||
if (config.dns_server[0] != 0) {
|
||||
sprintf(buf, "dig @%s +short +time=1 -x %s", config.dns_server, ctx->addr);
|
||||
FILE *dig = popen(buf, "r");
|
||||
if (dig == NULL) {
|
||||
error("Unable to start dig: %s", strerror(errno));
|
||||
goto dig_err;
|
||||
}
|
||||
unsigned long read = fread(buf, 1, sizeof(buf), dig);
|
||||
ret = pclose(dig);
|
||||
if (ret != 0) {
|
||||
error("Dig terminated with exit code %i", ret);
|
||||
goto dig_err;
|
||||
}
|
||||
char *ptr = memchr(buf, '\n', read);
|
||||
if (ptr == buf || ptr == NULL) {
|
||||
goto dig_err;
|
||||
}
|
||||
ptr[-1] = 0;
|
||||
strncpy(ctx->host, buf, sizeof(ctx->host));
|
||||
} else {
|
||||
dig_err:
|
||||
ctx->host[0] = 0;
|
||||
}
|
||||
|
||||
ctx->cc[0] = 0;
|
||||
geoip_lookup_country(&client->addr.sock, ctx->cc);
|
||||
|
||||
info("Connection accepted from %s %s%s%s[%s]", ctx->addr, ctx->host[0] != 0 ? "(" : "",
|
||||
ctx->host[0] != 0 ? ctx->host : "", ctx->host[0] != 0 ? ") " : "",
|
||||
ctx->cc[0] != 0 ? ctx->cc : "N/A");
|
||||
|
||||
struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0};
|
||||
if (setsockopt(client->socket, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout)) == -1 ||
|
||||
setsockopt(client->socket, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout)) == -1)
|
||||
{
|
||||
error("Unable to set timeout for socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (client->enc) {
|
||||
client->ssl = SSL_new(client->ctx);
|
||||
SSL_set_fd(client->ssl, client->socket);
|
||||
SSL_set_accept_state(client->ssl);
|
||||
|
||||
ret = SSL_accept(client->ssl);
|
||||
client->_last_ret = ret;
|
||||
client->_errno = errno;
|
||||
client->_ssl_error = ERR_get_error();
|
||||
if (ret <= 0) {
|
||||
error("Unable to perform handshake: %s", sock_strerror(client));
|
||||
ret = -1;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
|
||||
int req_num = 0;
|
||||
ctx->s_keep_alive = 1;
|
||||
ctx->c_keep_alive = 1;
|
||||
while (ctx->c_keep_alive && ctx->s_keep_alive && req_num < REQ_PER_CONNECTION) {
|
||||
ret = client_request_handler(ctx, client, client_num, req_num++, log_client_prefix);
|
||||
logger_set_prefix(log_conn_prefix);
|
||||
}
|
||||
|
||||
close:
|
||||
sock_close(client);
|
||||
|
||||
if (proxy.socket != 0) {
|
||||
info(BLUE_STR "Closing proxy connection");
|
||||
sock_close(&proxy);
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
||||
|
||||
info("Connection closed (%s)", format_duration(micros, buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *client_handler(client_ctx_t *ctx) {
|
||||
struct sockaddr_in6 *server_addr;
|
||||
struct sockaddr_storage server_addr_storage;
|
||||
char log_client_prefix[256], log_conn_prefix[512];
|
||||
|
||||
logger_set_name("client");
|
||||
|
||||
//signal(SIGINT, client_terminate);
|
||||
//signal(SIGTERM, client_terminate);
|
||||
|
||||
inet_ntop(ctx->socket.addr.ipv6.sin6_family, &ctx->socket.addr.ipv6.sin6_addr, ctx->_c_addr, sizeof(ctx->_c_addr));
|
||||
if (strncmp(ctx->_c_addr, "::ffff:", 7) == 0) {
|
||||
ctx->addr = ctx->_c_addr + 7;
|
||||
} else {
|
||||
ctx->addr = ctx->_c_addr;
|
||||
}
|
||||
|
||||
socklen_t len = sizeof(server_addr_storage);
|
||||
getsockname(ctx->socket.socket, (struct sockaddr *) &server_addr_storage, &len);
|
||||
server_addr = (struct sockaddr_in6 *) &server_addr_storage;
|
||||
inet_ntop(server_addr->sin6_family, (void *) &server_addr->sin6_addr, ctx->_s_addr, sizeof(ctx->_s_addr));
|
||||
if (strncmp(ctx->_s_addr, "::ffff:", 7) == 0) {
|
||||
ctx->s_addr = ctx->_s_addr + 7;
|
||||
} else {
|
||||
ctx->s_addr = ctx->_s_addr;
|
||||
}
|
||||
|
||||
sprintf(log_client_prefix, "[%s%4i%s]%s[%*s][%5i]%s", (int) ctx->socket.enc ? HTTPS_STR : HTTP_STR,
|
||||
ntohs(server_addr->sin6_port), CLR_STR, color_table[0], INET6_ADDRSTRLEN, ctx->addr,
|
||||
ntohs(ctx->socket.addr.ipv6.sin6_port), CLR_STR);
|
||||
|
||||
sprintf(log_conn_prefix, "[%*s]%s", INET6_ADDRSTRLEN, ctx->s_addr, log_client_prefix);
|
||||
logger_set_prefix(log_conn_prefix);
|
||||
|
||||
info("Started thread");
|
||||
|
||||
client_connection_handler(ctx, 0, log_conn_prefix, log_client_prefix);
|
||||
|
||||
return NULL;
|
||||
}
|
32
src/client.h
32
src/client.h
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Client connection and request handlers (header file)
|
||||
* @file src/client.h
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-08-16
|
||||
*/
|
||||
|
||||
#ifndef SESIMOS_CLIENT_H
|
||||
#define SESIMOS_CLIENT_H
|
||||
|
||||
#include "lib/config.h"
|
||||
#include "lib/sock.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
typedef struct {
|
||||
sock socket;
|
||||
int req_num;
|
||||
char *addr, *s_addr;
|
||||
unsigned char in_use: 1, s_keep_alive:1, c_keep_alive:1;
|
||||
char cc[3], host[256];
|
||||
char log_prefix[512];
|
||||
char _c_addr[INET6_ADDRSTRLEN + 1], _s_addr[INET6_ADDRSTRLEN + 1];
|
||||
struct timespec begin, end;
|
||||
} client_ctx_t;
|
||||
|
||||
host_config_t *get_host_config(const char *host);
|
||||
|
||||
void *client_handler(client_ctx_t *client);
|
||||
|
||||
#endif //SESIMOS_CLIENT_H
|
@@ -9,11 +9,12 @@
|
||||
#ifndef SESIMOS_DEF_H
|
||||
#define SESIMOS_DEF_H
|
||||
|
||||
#define SERVER_VERSION "4.6"
|
||||
#define SERVER_STR "Sesimos/" SERVER_VERSION
|
||||
#define SERVER_STR_HTML "Sesimos web server " SERVER_VERSION
|
||||
#define SERVER_VERSION "5.0-wip"
|
||||
#define SERVER_STR "sesimos/" SERVER_VERSION
|
||||
#define SERVER_STR_HTML "sesimos web server " SERVER_VERSION
|
||||
|
||||
#define CHUNK_SIZE 8192
|
||||
#define MAX_PROXY_CNX_PER_HOST 16
|
||||
|
||||
#ifndef DEFAULT_HOST
|
||||
# define DEFAULT_HOST "www.necronda.net"
|
||||
|
@@ -208,3 +208,16 @@ int config_load(const char *filename) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
host_config_t *get_host_config(const char *host) {
|
||||
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
|
||||
host_config_t *hc = &config.hosts[i];
|
||||
if (hc->type == CONFIG_TYPE_UNSET) break;
|
||||
if (strcmp(hc->name, host) == 0) return hc;
|
||||
if (hc->name[0] == '*' && hc->name[1] == '.') {
|
||||
const char *pos = strstr(host, hc->name + 1);
|
||||
if (pos != NULL && strlen(pos) == strlen(hc->name + 1)) return hc;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -60,4 +60,6 @@ extern config_t config;
|
||||
|
||||
int config_load(const char *filename);
|
||||
|
||||
host_config_t *get_host_config(const char *host);
|
||||
|
||||
#endif //SESIMOS_CONFIG_H
|
||||
|
134
src/lib/error.c
Normal file
134
src/lib/error.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Error interface
|
||||
* @file src/lib/error.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2023-01-08
|
||||
*/
|
||||
|
||||
#include "error.h"
|
||||
#include "http.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <maxminddb.h>
|
||||
|
||||
static const char *error_ssl_strerror(int err) {
|
||||
switch (err) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return "closed";
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return "want read";
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return "want write";
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
return "want connect";
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
return "want accept";
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
return "want x509 lookup";
|
||||
case SSL_ERROR_WANT_ASYNC:
|
||||
return "want async";
|
||||
case SSL_ERROR_WANT_ASYNC_JOB:
|
||||
return "want async job";
|
||||
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
|
||||
return "want client hello callback";
|
||||
case SSL_ERROR_WANT_RETRY_VERIFY:
|
||||
return "want retry verify";
|
||||
case SSL_ERROR_SSL:
|
||||
return ERR_reason_error_string(ERR_get_error());
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *error_http_strerror(int err) {
|
||||
switch (err) {
|
||||
case HTTP_ERROR_TOO_MANY_HEADER_FIELDS:
|
||||
return "too many header fields";
|
||||
case HTTP_ERROR_EOH_NOT_FOUND:
|
||||
return "end of http header not found";
|
||||
case HTTP_ERROR_HEADER_MALFORMED:
|
||||
return "http header malformed";
|
||||
case HTTP_ERROR_INVALID_VERSION:
|
||||
return "invalid http version";
|
||||
case HTTP_ERROR_URI_TOO_LONG:
|
||||
return "uri too long";
|
||||
case HTTP_ERROR_GENERAL:
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
const char *error_str(int err_no, char *buf, int buf_len) {
|
||||
buf[0] = 0;
|
||||
unsigned char mode = (unsigned char) (err_no >> 24);
|
||||
int e = err_no & 0x00FFFFFF;
|
||||
if (mode == 0x00) {
|
||||
// normal
|
||||
strerror_r(e, buf, buf_len);
|
||||
return buf;
|
||||
} else if (mode == 0x01) {
|
||||
// ssl
|
||||
return error_ssl_strerror(e);
|
||||
} else if (mode == 0x02) {
|
||||
// mmdb
|
||||
return MMDB_strerror(e);
|
||||
} else if (mode == 0x03) {
|
||||
// http
|
||||
return error_http_strerror(e);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void error_ssl(int err) {
|
||||
if (err == SSL_ERROR_NONE) {
|
||||
errno = 0;
|
||||
} else if (err == SSL_ERROR_SYSCALL) {
|
||||
// errno already set
|
||||
} else {
|
||||
errno = 0x01000000 | err;
|
||||
}
|
||||
}
|
||||
|
||||
void error_mmdb(int err) {
|
||||
if (err == MMDB_SUCCESS) {
|
||||
errno = 0;
|
||||
} else if (err == MMDB_IO_ERROR) {
|
||||
// errno already set
|
||||
} else {
|
||||
errno = 0x02000000 | err;
|
||||
}
|
||||
}
|
||||
|
||||
int error_http(int err) {
|
||||
if (err == 0) {
|
||||
errno = 0;
|
||||
} else if (err == HTTP_ERROR_SYSCALL) {
|
||||
// errno already set
|
||||
} else {
|
||||
errno = 0x03000000 | err;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int error_get(unsigned char prefix) {
|
||||
return (errno >> 24 != prefix) ? 0 : errno & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
int error_get_sys() {
|
||||
return error_get(0x00);
|
||||
}
|
||||
|
||||
int error_get_ssl() {
|
||||
return error_get(0x01);
|
||||
}
|
||||
|
||||
int error_get_mmdb() {
|
||||
return error_get(0x02);
|
||||
}
|
||||
|
||||
int error_get_http() {
|
||||
return error_get(0x03);
|
||||
}
|
28
src/lib/error.h
Normal file
28
src/lib/error.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Error interface (header fie)
|
||||
* @file src/lib/error.h
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2023-01-08
|
||||
*/
|
||||
|
||||
#ifndef SESIMOS_ERROR_H
|
||||
#define SESIMOS_ERROR_H
|
||||
|
||||
const char *error_str(int err_no, char *buf, int buf_len);
|
||||
|
||||
void error_ssl(int err);
|
||||
|
||||
void error_mmdb(int err);
|
||||
|
||||
int error_http(int err);
|
||||
|
||||
int error_get_sys();
|
||||
|
||||
int error_get_ssl();
|
||||
|
||||
int error_get_mmdb();
|
||||
|
||||
int error_get_http();
|
||||
|
||||
#endif //SESIMOS_ERROR_H
|
@@ -9,12 +9,11 @@
|
||||
#include "fastcgi.h"
|
||||
#include "utils.h"
|
||||
#include "compress.h"
|
||||
#include "../server.h"
|
||||
#include "../logger.h"
|
||||
#include "list.h"
|
||||
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
@@ -53,15 +52,9 @@ char *fastcgi_add_param(char *buf, const char *key, const char *value) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri) {
|
||||
unsigned short req_id = (client_num & 0xFFF) << 4;
|
||||
if (client_num == 0) {
|
||||
req_id |= (req_num + 1) & 0xF;
|
||||
} else {
|
||||
req_id |= req_num & 0xF;
|
||||
}
|
||||
int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri) {
|
||||
conn->mode = mode;
|
||||
conn->req_id = req_id;
|
||||
conn->req_id = (req_num + 1) & 0xFFFF;
|
||||
conn->out_buf = NULL;
|
||||
conn->out_off = 0;
|
||||
conn->webroot = uri->webroot;
|
||||
@@ -87,8 +80,8 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned
|
||||
|
||||
FCGI_Header header = {
|
||||
.version = FCGI_VERSION_1,
|
||||
.requestIdB1 = req_id >> 8,
|
||||
.requestIdB0 = req_id & 0xFF,
|
||||
.requestIdB1 = conn->req_id >> 8,
|
||||
.requestIdB0 = conn->req_id & 0xFF,
|
||||
.paddingLength = 0,
|
||||
.reserved = 0
|
||||
};
|
||||
@@ -132,8 +125,8 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned
|
||||
addr = (struct sockaddr_in6 *) &addr_storage;
|
||||
sprintf(buf0, "%i", addr->sin6_port);
|
||||
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_PORT", buf0);
|
||||
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", conn->ctx->addr);
|
||||
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", conn->ctx->host[0] != 0 ? conn->ctx->host : conn->ctx->addr);
|
||||
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", conn->r_addr);
|
||||
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", conn->r_host != NULL ? conn->r_host : conn->r_addr);
|
||||
//param_ptr = fastcgi_add_param(param_ptr, "REMOTE_IDENT", "");
|
||||
//param_ptr = fastcgi_add_param(param_ptr, "REMOTE_USER", "");
|
||||
|
||||
@@ -160,7 +153,7 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned
|
||||
// param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", conn->ctx->geoip);
|
||||
//}
|
||||
|
||||
for (int i = 0; i < req->hdr.field_num; i++) {
|
||||
for (int i = 0; i < list_size(&req->hdr); i++) {
|
||||
const http_field *f = &req->hdr.fields[i];
|
||||
const char *name = http_field_get_name(f);
|
||||
char *ptr = buf0;
|
||||
@@ -201,7 +194,7 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastcgi_close_stdin(fastcgi_conn *conn) {
|
||||
int fastcgi_close_stdin(fastcgi_cnx_t *conn) {
|
||||
FCGI_Header header = {
|
||||
.version = FCGI_VERSION_1,
|
||||
.type = FCGI_STDIN,
|
||||
@@ -221,7 +214,7 @@ int fastcgi_close_stdin(fastcgi_conn *conn) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastcgi_php_error(const fastcgi_conn *conn, const char *msg, int msg_len, char *err_msg) {
|
||||
int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, char *err_msg) {
|
||||
char *msg_str = malloc(msg_len + 1);
|
||||
char *ptr0 = msg_str;
|
||||
memcpy(msg_str, msg, msg_len);
|
||||
@@ -288,7 +281,7 @@ int fastcgi_php_error(const fastcgi_conn *conn, const char *msg, int msg_len, ch
|
||||
return err;
|
||||
}
|
||||
|
||||
int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
|
||||
int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) {
|
||||
FCGI_Header header;
|
||||
char *content;
|
||||
unsigned short content_len, req_id;
|
||||
@@ -390,6 +383,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
|
||||
|
||||
ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
|
||||
if (ret != 0) return (int) ret;
|
||||
|
||||
if (pos0[2] == '\r' && pos0[3] == '\n') {
|
||||
return 0;
|
||||
}
|
||||
@@ -399,7 +393,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
||||
int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) {
|
||||
FCGI_Header header;
|
||||
long ret;
|
||||
char buf0[256];
|
||||
@@ -517,7 +511,7 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
||||
}
|
||||
}
|
||||
|
||||
int fastcgi_dump(fastcgi_conn *conn, char *buf, long len) {
|
||||
int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) {
|
||||
FCGI_Header header;
|
||||
long ret;
|
||||
char buf0[256];
|
||||
@@ -582,7 +576,7 @@ int fastcgi_dump(fastcgi_conn *conn, char *buf, long len) {
|
||||
}
|
||||
}
|
||||
|
||||
int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len) {
|
||||
int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) {
|
||||
unsigned long rcv_len = 0;
|
||||
char *buf[16384];
|
||||
long ret;
|
||||
@@ -600,7 +594,7 @@ int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len) {
|
||||
while (rcv_len < len) {
|
||||
ret = sock_recv(client, buf, sizeof(buf), 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to receive: %s", sock_strerror(client));
|
||||
error("Unable to receive");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -617,7 +611,7 @@ int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastcgi_receive_chunked(fastcgi_conn *conn, sock *client) {
|
||||
int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client) {
|
||||
long ret;
|
||||
unsigned long next_len;
|
||||
|
||||
|
@@ -12,7 +12,6 @@
|
||||
#include "include/fastcgi.h"
|
||||
#include "http.h"
|
||||
#include "uri.h"
|
||||
#include "../client.h"
|
||||
|
||||
#define FASTCGI_CHUNKED 1
|
||||
#define FASTCGI_COMPRESS_GZ 2
|
||||
@@ -37,26 +36,26 @@ typedef struct {
|
||||
const char *webroot;
|
||||
unsigned short out_len;
|
||||
unsigned short out_off;
|
||||
client_ctx_t *ctx;
|
||||
} fastcgi_conn;
|
||||
char *r_addr;
|
||||
char *r_host;
|
||||
} fastcgi_cnx_t;
|
||||
|
||||
char *fastcgi_add_param(char *buf, const char *key, const char *value);
|
||||
|
||||
int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned int req_num, const sock *client,
|
||||
const http_req *req, const http_uri *uri);
|
||||
int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri);
|
||||
|
||||
int fastcgi_close_stdin(fastcgi_conn *conn);
|
||||
int fastcgi_close_stdin(fastcgi_cnx_t *conn);
|
||||
|
||||
int fastcgi_php_error(const fastcgi_conn *conn, const char *msg, int msg_len, char *err_msg);
|
||||
int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, char *err_msg);
|
||||
|
||||
int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg);
|
||||
int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg);
|
||||
|
||||
int fastcgi_send(fastcgi_conn *conn, sock *client, int flags);
|
||||
int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags);
|
||||
|
||||
int fastcgi_dump(fastcgi_conn *conn, char *buf, long len);
|
||||
int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len);
|
||||
|
||||
int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len);
|
||||
int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len);
|
||||
|
||||
int fastcgi_receive_chunked(fastcgi_conn *conn, sock *client);
|
||||
int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client);
|
||||
|
||||
#endif //SESIMOS_FASTCGI_H
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "geoip.h"
|
||||
#include "../logger.h"
|
||||
#include "error.h"
|
||||
#include <memory.h>
|
||||
#include <dirent.h>
|
||||
|
||||
@@ -101,7 +102,8 @@ int geoip_init(const char *directory) {
|
||||
|
||||
sprintf(buf, "%s/%s", directory, entry->d_name);
|
||||
if ((status = MMDB_open(buf, 0, &mmdbs[i])) != MMDB_SUCCESS) {
|
||||
critical("Unable to initialize geoip: Unable to open .mmdb file: %s", MMDB_strerror(status));
|
||||
error_mmdb(status);
|
||||
critical("Unable to initialize geoip: Unable to open .mmdb file");
|
||||
closedir(geoip_dir);
|
||||
return 1;
|
||||
}
|
||||
@@ -164,7 +166,8 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) {
|
||||
int mmdb_res;
|
||||
MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdbs[i], addr, &mmdb_res);
|
||||
if (mmdb_res != MMDB_SUCCESS) {
|
||||
error("Unable to lookup geoip info: %s", MMDB_strerror(mmdb_res));
|
||||
error_mmdb(mmdb_res);
|
||||
error("Unable to lookup geoip info");
|
||||
continue;
|
||||
} else if (!result.found_entry) {
|
||||
continue;
|
||||
@@ -172,7 +175,8 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) {
|
||||
|
||||
MMDB_entry_data_list_s *list;
|
||||
if ((mmdb_res = MMDB_get_entry_data_list(&result.entry, &list)) != MMDB_SUCCESS) {
|
||||
error("Unable to lookup geoip info: %s", MMDB_strerror(mmdb_res));
|
||||
error_mmdb(mmdb_res);
|
||||
error("Unable to lookup geoip info");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
257
src/lib/http.c
257
src/lib/http.c
@@ -6,10 +6,11 @@
|
||||
* @date 2020-12-09
|
||||
*/
|
||||
|
||||
#include "../logger.h"
|
||||
#include "http.h"
|
||||
#include "utils.h"
|
||||
#include "compress.h"
|
||||
#include "list.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -69,10 +70,10 @@ void http_free_field(http_field *f) {
|
||||
}
|
||||
|
||||
void http_free_hdr(http_hdr *hdr) {
|
||||
for (int i = 0; i < hdr->field_num; i++) {
|
||||
for (int i = 0; i < list_size(hdr->fields); i++) {
|
||||
http_free_field(&hdr->fields[i]);
|
||||
}
|
||||
hdr->field_num = 0;
|
||||
list_free(hdr->fields);
|
||||
hdr->last_field_num = -1;
|
||||
}
|
||||
|
||||
@@ -86,18 +87,23 @@ void http_free_res(http_res *res) {
|
||||
http_free_hdr(&res->hdr);
|
||||
}
|
||||
|
||||
int http_init_hdr(http_hdr *hdr) {
|
||||
hdr->fields = list_create(sizeof(http_field), HTTP_INIT_HEADER_FIELD_NUM);
|
||||
if (hdr->fields == NULL)
|
||||
return error_http(HTTP_ERROR_SYSCALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags) {
|
||||
if (hdr->last_field_num > hdr->field_num) {
|
||||
error("Unable to parse header: Invalid state");
|
||||
return 3;
|
||||
}
|
||||
if (hdr->last_field_num > list_size(hdr->fields))
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
|
||||
char *pos1 = (char *) buf, *pos2 = (char *) end_ptr;
|
||||
if (buf[0] == ' ' || buf[0] == '\t') {
|
||||
if (hdr->last_field_num == -1) {
|
||||
error("Unable to parse header");
|
||||
return 3;
|
||||
}
|
||||
if (hdr->last_field_num == -1)
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
|
||||
http_field *f = &hdr->fields[(int) hdr->last_field_num];
|
||||
|
||||
str_trim_lws(&pos1, &pos2);
|
||||
@@ -107,117 +113,115 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr,
|
||||
}
|
||||
|
||||
pos1 = memchr(buf, ':', end_ptr - buf);
|
||||
if (pos1 == NULL) {
|
||||
error("Unable to parse header");
|
||||
return 3;
|
||||
}
|
||||
if (pos1 == NULL)
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
|
||||
long len1 = pos1 - buf;
|
||||
|
||||
pos1++;
|
||||
str_trim_lws(&pos1, &pos2);
|
||||
long len2 = pos2 - pos1;
|
||||
|
||||
char field_num = hdr->field_num;
|
||||
int field_num = list_size(hdr->fields);
|
||||
int found = http_get_header_field_num_len(hdr, buf, len1);
|
||||
if (!(flags & HTTP_MERGE_FIELDS) || found == -1) {
|
||||
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) {
|
||||
error("Unable to parse header: Too many header fields");
|
||||
return 3;
|
||||
}
|
||||
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0)
|
||||
return error_http(HTTP_ERROR_TOO_MANY_HEADER_FIELDS);
|
||||
} else {
|
||||
field_num = (char) found;
|
||||
field_num = found;
|
||||
http_append_to_header_field(&hdr->fields[found], ", ", 2);
|
||||
http_append_to_header_field(&hdr->fields[found], pos1, len2);
|
||||
}
|
||||
|
||||
hdr->last_field_num = (char) field_num;
|
||||
hdr->last_field_num = field_num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_receive_request(sock *client, http_req *req) {
|
||||
long rcv_len, len;
|
||||
char buf[CLIENT_MAX_HEADER_SIZE];
|
||||
int http_parse_request(char *buf, http_req *req) {
|
||||
char *ptr, *pos0 = buf, *pos1, *pos2;
|
||||
long len;
|
||||
|
||||
unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4;
|
||||
if (header_len <= 0)
|
||||
return error_http(HTTP_ERROR_EOH_NOT_FOUND);
|
||||
|
||||
for (int i = 0; i < header_len; i++) {
|
||||
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F)
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
}
|
||||
|
||||
ptr = buf;
|
||||
while (header_len > (ptr - buf + 2)) {
|
||||
pos0 = strstr(ptr, "\r\n");
|
||||
if (pos0 == NULL)
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
|
||||
if (req->version[0] == 0) {
|
||||
pos1 = (char *) strchr(ptr, ' ') + 1;
|
||||
if (pos1 == NULL) goto err_hdr_fmt;
|
||||
|
||||
if (pos1 - ptr - 1 >= sizeof(req->method))
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
|
||||
for (int i = 0; i < (pos1 - ptr - 1); i++) {
|
||||
if (ptr[i] < 'A' || ptr[i] > 'Z')
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
}
|
||||
snprintf(req->method, sizeof(req->method), "%.*s", (int) (pos1 - ptr - 1), ptr);
|
||||
|
||||
pos2 = (char *) strchr(pos1, ' ') + 1;
|
||||
if (pos2 == NULL) {
|
||||
err_hdr_fmt:
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
}
|
||||
|
||||
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0)
|
||||
return error_http(HTTP_ERROR_INVALID_VERSION);
|
||||
|
||||
len = pos2 - pos1 - 1;
|
||||
if (len >= 2048)
|
||||
return error_http(HTTP_ERROR_URI_TOO_LONG);
|
||||
|
||||
req->uri = malloc(len + 1);
|
||||
sprintf(req->uri, "%.*s", (int) len, pos1);
|
||||
sprintf(req->version, "%.3s", pos2 + 5);
|
||||
} else {
|
||||
if (http_parse_header_field(&req->hdr, ptr, pos0, HTTP_MERGE_FIELDS) != 0)
|
||||
return -1;
|
||||
}
|
||||
ptr = pos0 + 2;
|
||||
}
|
||||
|
||||
if (pos0[2] == '\r' && pos0[3] == '\n') {
|
||||
return (int) header_len;
|
||||
}
|
||||
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
}
|
||||
|
||||
int http_receive_request(sock *client, http_req *req) {
|
||||
long rcv_len;
|
||||
char buf[CLIENT_MAX_HEADER_SIZE];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(req->method, 0, sizeof(req->method));
|
||||
memset(req->version, 0, sizeof(req->version));
|
||||
req->uri = NULL;
|
||||
req->hdr.field_num = 0;
|
||||
req->hdr.last_field_num = -1;
|
||||
http_init_hdr(&req->hdr);
|
||||
|
||||
while (1) {
|
||||
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE, MSG_PEEK);
|
||||
if (rcv_len <= 0) {
|
||||
error("Unable to receive http header: %s", sock_strerror(client));
|
||||
return -1;
|
||||
}
|
||||
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE - 1, MSG_PEEK);
|
||||
if (rcv_len <= 0)
|
||||
return -1;
|
||||
|
||||
unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4;
|
||||
if (header_len <= 0) {
|
||||
error("Unable to parse http header: End of header not found");
|
||||
return 5;
|
||||
} else {
|
||||
rcv_len = sock_recv(client, buf, header_len, 0);
|
||||
}
|
||||
buf[rcv_len] = 0;
|
||||
|
||||
for (int i = 0; i < header_len; i++) {
|
||||
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) {
|
||||
error("Unable to parse http header: Header contains illegal characters");
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
long header_len = http_parse_request(buf, req);
|
||||
if (header_len < 0)
|
||||
return (int) -header_len;
|
||||
|
||||
ptr = buf;
|
||||
while (header_len > (ptr - buf + 2)) {
|
||||
pos0 = strstr(ptr, "\r\n");
|
||||
if (pos0 == NULL) {
|
||||
error("Unable to parse http header: Invalid header format");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (req->version[0] == 0) {
|
||||
pos1 = (char *) memchr(ptr, ' ', rcv_len - (ptr - buf)) + 1;
|
||||
if (pos1 == NULL) goto err_hdr_fmt;
|
||||
|
||||
if (pos1 - ptr - 1 >= sizeof(req->method)) {
|
||||
error("Unable to parse http header: Method name too long");
|
||||
return 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (pos1 - ptr - 1); i++) {
|
||||
if (ptr[i] < 'A' || ptr[i] > 'Z') {
|
||||
error("Unable to parse http header: Invalid method");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
snprintf(req->method, sizeof(req->method), "%.*s", (int) (pos1 - ptr - 1), ptr);
|
||||
|
||||
pos2 = (char *) memchr(pos1, ' ', rcv_len - (pos1 - buf)) + 1;
|
||||
if (pos2 == NULL) {
|
||||
err_hdr_fmt:
|
||||
error("Unable to parse http header: Invalid header format");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) {
|
||||
error("Unable to parse http header: Invalid version");
|
||||
return 3;
|
||||
}
|
||||
|
||||
len = pos2 - pos1 - 1;
|
||||
req->uri = malloc(len + 1);
|
||||
sprintf(req->uri, "%.*s", (int) len, pos1);
|
||||
sprintf(req->version, "%.3s", pos2 + 5);
|
||||
} else {
|
||||
int ret = http_parse_header_field(&req->hdr, ptr, pos0, HTTP_MERGE_FIELDS);
|
||||
if (ret != 0) return ret;
|
||||
}
|
||||
ptr = pos0 + 2;
|
||||
}
|
||||
if (pos0[2] == '\r' && pos0[3] == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcv_len = sock_recv(client, buf, header_len, 0);
|
||||
if (rcv_len != header_len)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -228,7 +232,7 @@ const char *http_get_header_field(const http_hdr *hdr, const char *field_name) {
|
||||
|
||||
const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len) {
|
||||
int num = http_get_header_field_num_len(hdr, field_name, len);
|
||||
return (num >= 0 && num < HTTP_MAX_HEADER_FIELD_NUM) ? http_field_get_value(&hdr->fields[num]) : NULL;
|
||||
return (num >= 0 && num < list_size(hdr->fields)) ? http_field_get_value(&hdr->fields[num]) : NULL;
|
||||
}
|
||||
|
||||
int http_get_header_field_num(const http_hdr *hdr, const char *field_name) {
|
||||
@@ -236,16 +240,8 @@ int http_get_header_field_num(const http_hdr *hdr, const char *field_name) {
|
||||
}
|
||||
|
||||
int http_get_header_field_num_len(const http_hdr *hdr, const char *field_name, unsigned long len) {
|
||||
char field_name_1[256], field_name_2[256];
|
||||
memcpy(field_name_1, field_name, len);
|
||||
field_name_1[len] = 0;
|
||||
http_to_camel_case(field_name_1, HTTP_LOWER);
|
||||
|
||||
for (int i = 0; i < hdr->field_num; i++) {
|
||||
strcpy(field_name_2, http_field_get_name(&hdr->fields[i]));
|
||||
http_to_camel_case(field_name_2, HTTP_LOWER);
|
||||
|
||||
if (strcmp(field_name_1, field_name_2) == 0)
|
||||
for (int i = 0; i < list_size(hdr->fields); i++) {
|
||||
if (strncasecmp(field_name, http_field_get_name(&hdr->fields[i]), len) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -257,10 +253,8 @@ int http_add_header_field(http_hdr *hdr, const char *field_name, const char *fie
|
||||
}
|
||||
|
||||
int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long name_len, const char *value, unsigned long value_len) {
|
||||
if (hdr->field_num >= HTTP_MAX_HEADER_FIELD_NUM)
|
||||
return -1;
|
||||
|
||||
http_field *f = &hdr->fields[(int) hdr->field_num];
|
||||
http_field *f;
|
||||
hdr->fields = list_append_ptr(hdr->fields, (void **) &f);
|
||||
|
||||
if (name_len < sizeof(f->normal.name) && value_len < sizeof(f->normal.value)) {
|
||||
f->type = HTTP_FIELD_NORMAL;
|
||||
@@ -288,7 +282,6 @@ int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long nam
|
||||
http_to_camel_case(f->ex_name.name, HTTP_PRESERVE);
|
||||
}
|
||||
|
||||
hdr->field_num++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -314,23 +307,16 @@ void http_append_to_header_field(http_field *field, const char *value, unsigned
|
||||
}
|
||||
|
||||
void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode) {
|
||||
char field_name_1[256], field_name_2[256];
|
||||
strcpy(field_name_1, field_name);
|
||||
http_to_camel_case(field_name_1, HTTP_LOWER);
|
||||
|
||||
int i = 0;
|
||||
int diff = 1;
|
||||
if (mode == HTTP_REMOVE_LAST) {
|
||||
i = hdr->field_num - 1;
|
||||
i = list_size(hdr->fields) - 1;
|
||||
diff = -1;
|
||||
}
|
||||
for (; i < hdr->field_num && i >= 0; i += diff) {
|
||||
strcpy(field_name_2, http_field_get_name(&hdr->fields[i]));
|
||||
http_to_camel_case(field_name_2, HTTP_LOWER);
|
||||
if (strcmp(field_name_1, field_name_2) == 0) {
|
||||
for (; i < list_size(hdr->fields) && i >= 0; i += diff) {
|
||||
if (strcasecmp(field_name, http_field_get_name(&hdr->fields[i])) == 0) {
|
||||
http_free_field(&hdr->fields[i]);
|
||||
memmove(&hdr->fields[i], &hdr->fields[i + 1], sizeof(hdr->fields[0]) * (hdr->field_num - i));
|
||||
hdr->field_num--;
|
||||
list_remove(hdr->fields, i);
|
||||
if (mode == HTTP_REMOVE_ALL) {
|
||||
i -= diff;
|
||||
} else {
|
||||
@@ -343,34 +329,33 @@ void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode) {
|
||||
int http_send_response(sock *client, http_res *res) {
|
||||
char buf[CLIENT_MAX_HEADER_SIZE];
|
||||
long off = sprintf(buf, "HTTP/%s %03i %s\r\n", res->version, res->status->code, res->status->msg);
|
||||
for (int i = 0; i < res->hdr.field_num; i++) {
|
||||
for (int i = 0; i < list_size(res->hdr.fields); i++) {
|
||||
const http_field *f = &res->hdr.fields[i];
|
||||
off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
|
||||
}
|
||||
off += sprintf(buf + off, "\r\n");
|
||||
if (sock_send(client, buf, off, 0) < 0) {
|
||||
if (sock_send(client, buf, off, 0) != off)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_send_request(sock *server, http_req *req) {
|
||||
char buf[CLIENT_MAX_HEADER_SIZE];
|
||||
long off = sprintf(buf, "%s %s HTTP/%s\r\n", req->method, req->uri, req->version);
|
||||
for (int i = 0; i < req->hdr.field_num; i++) {
|
||||
for (int i = 0; i < list_size(req->hdr.fields); i++) {
|
||||
const http_field *f = &req->hdr.fields[i];
|
||||
off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
|
||||
}
|
||||
off += sprintf(buf + off, "\r\n");
|
||||
long ret = sock_send(server, buf, off, 0);
|
||||
if (ret <= 0) {
|
||||
if (sock_send(server, buf, off, 0) != off)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const http_status *http_get_status(unsigned short status_code) {
|
||||
for (int i = 0; i < http_statuses_size / sizeof(http_status); i++) {
|
||||
for (int i = 0; i < http_statuses_size; i++) {
|
||||
if (http_statuses[i].code == status_code) {
|
||||
return &http_statuses[i];
|
||||
}
|
||||
@@ -380,7 +365,7 @@ const http_status *http_get_status(unsigned short status_code) {
|
||||
|
||||
const http_status_msg *http_get_error_msg(const http_status *status) {
|
||||
unsigned short code = status->code;
|
||||
for (int i = 0; i < http_status_messages_size / sizeof(http_status_msg); i++) {
|
||||
for (int i = 0; i < http_status_messages_size; i++) {
|
||||
if (http_status_messages[i].code == code) {
|
||||
return &http_status_messages[i];
|
||||
}
|
||||
@@ -419,10 +404,10 @@ char *http_get_date(char *buf, size_t size) {
|
||||
const http_doc_info *http_get_status_info(const http_status *status) {
|
||||
unsigned short code = status->code;
|
||||
static http_doc_info info[] = {
|
||||
{"info", HTTP_COLOR_INFO, http_info_icon, http_info_document},
|
||||
{"success", HTTP_COLOR_SUCCESS, http_success_icon, http_success_document},
|
||||
{"warning", HTTP_COLOR_WARNING, http_warning_icon, http_warning_document},
|
||||
{"error", HTTP_COLOR_ERROR, http_error_icon, http_error_document}
|
||||
{"info", HTTP_COLOR_INFO, "/.sesimos/res/icon-info.svg", http_info_doc},
|
||||
{"success", HTTP_COLOR_SUCCESS, "/.sesimos/res/icon-success.svg", http_success_doc},
|
||||
{"warning", HTTP_COLOR_WARNING, "/.sesimos/res/icon-warning.svg", http_warning_doc},
|
||||
{"error", HTTP_COLOR_ERROR, "/.sesimos/res/icon-error.svg", http_error_doc}
|
||||
};
|
||||
if (code >= 100 && code < 200) {
|
||||
return &info[0];
|
||||
|
@@ -32,29 +32,44 @@
|
||||
#define HTTP_5XX_STR "\x1B[1;31m"
|
||||
|
||||
#define HTTP_COLOR_SUCCESS "#008000"
|
||||
#define HTTP_COLOR_INFO "#606060"
|
||||
#define HTTP_COLOR_INFO "#606060"
|
||||
#define HTTP_COLOR_WARNING "#E0C000"
|
||||
#define HTTP_COLOR_ERROR "#C00000"
|
||||
#define HTTP_COLOR_ERROR "#C00000"
|
||||
|
||||
#define CLIENT_MAX_HEADER_SIZE 8192
|
||||
#define HTTP_MAX_HEADER_FIELD_NUM 64
|
||||
#define HTTP_INIT_HEADER_FIELD_NUM 16
|
||||
|
||||
#define HTTP_TYPE_INFORMATIONAL 1
|
||||
#define HTTP_TYPE_SUCCESS 2
|
||||
#define HTTP_TYPE_REDIRECTION 3
|
||||
#define HTTP_TYPE_CLIENT_ERROR 4
|
||||
#define HTTP_TYPE_SERVER_ERROR 5
|
||||
|
||||
#define HTTP_ERROR_GENERAL 1
|
||||
#define HTTP_ERROR_SYSCALL 2
|
||||
#define HTTP_ERROR_TOO_MANY_HEADER_FIELDS 3
|
||||
#define HTTP_ERROR_EOH_NOT_FOUND 4
|
||||
#define HTTP_ERROR_HEADER_MALFORMED 5
|
||||
#define HTTP_ERROR_INVALID_VERSION 6
|
||||
#define HTTP_ERROR_URI_TOO_LONG 7
|
||||
#define HTTP_ERROR_
|
||||
|
||||
#ifndef SERVER_STR
|
||||
# define SERVER_STR "Sesimos"
|
||||
# define SERVER_STR "sesimos"
|
||||
#endif
|
||||
|
||||
#ifndef SERVER_STR_HTML
|
||||
# define SERVER_STR_HTML "Sesimos web server"
|
||||
# define SERVER_STR_HTML "sesimos web server"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned short code;
|
||||
char type[16];
|
||||
unsigned short code:10;
|
||||
unsigned char type:3;
|
||||
char msg[64];
|
||||
} http_status;
|
||||
|
||||
typedef struct {
|
||||
unsigned short code;
|
||||
unsigned short code:10;
|
||||
const char *msg;
|
||||
} http_status_msg;
|
||||
|
||||
@@ -69,11 +84,11 @@ typedef struct {
|
||||
char type;
|
||||
union {
|
||||
struct {
|
||||
char name[64];
|
||||
char value[192];
|
||||
char name[32];
|
||||
char value[32];
|
||||
} normal;
|
||||
struct {
|
||||
char name[192];
|
||||
char name[64 - sizeof(char *)];
|
||||
char *value;
|
||||
} ex_value;
|
||||
struct {
|
||||
@@ -84,9 +99,8 @@ typedef struct {
|
||||
} http_field;
|
||||
|
||||
typedef struct {
|
||||
char field_num;
|
||||
char last_field_num;
|
||||
http_field fields[HTTP_MAX_HEADER_FIELD_NUM];
|
||||
int last_field_num;
|
||||
http_field *fields;
|
||||
} http_hdr;
|
||||
|
||||
typedef struct {
|
||||
@@ -117,16 +131,7 @@ extern const http_status_msg http_status_messages[];
|
||||
extern const int http_statuses_size;
|
||||
extern const int http_status_messages_size;
|
||||
|
||||
extern const char http_default_document[];
|
||||
extern const char http_proxy_document[];
|
||||
extern const char http_error_document[];
|
||||
extern const char http_error_icon[];
|
||||
extern const char http_warning_document[];
|
||||
extern const char http_warning_icon[];
|
||||
extern const char http_success_document[];
|
||||
extern const char http_success_icon[];
|
||||
extern const char http_info_document[];
|
||||
extern const char http_info_icon[];
|
||||
extern const char http_error_doc[], http_warning_doc[], http_success_doc[], http_info_doc[];
|
||||
|
||||
void http_to_camel_case(char *str, int mode);
|
||||
|
||||
@@ -134,6 +139,8 @@ const char *http_field_get_name(const http_field *field);
|
||||
|
||||
const char *http_field_get_value(const http_field *field);
|
||||
|
||||
int http_init_hdr(http_hdr *hdr);
|
||||
|
||||
void http_free_field(http_field *f);
|
||||
|
||||
void http_free_hdr(http_hdr *hdr);
|
||||
@@ -142,6 +149,8 @@ void http_free_req(http_req *req);
|
||||
|
||||
void http_free_res(http_res *res);
|
||||
|
||||
int http_parse_request(char *buf, http_req *req);
|
||||
|
||||
int http_receive_request(sock *client, http_req *req);
|
||||
|
||||
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags);
|
||||
|
@@ -4,302 +4,209 @@
|
||||
* @file src/lib/http_static.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2021-05-03
|
||||
* @details https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
*/
|
||||
|
||||
#include "../defs.h"
|
||||
#include "http.h"
|
||||
|
||||
|
||||
const http_status http_statuses[] = {
|
||||
{100, "Informational", "Continue"},
|
||||
{101, "Informational", "Switching Protocols"},
|
||||
{100, HTTP_TYPE_INFORMATIONAL, "Continue"},
|
||||
{101, HTTP_TYPE_INFORMATIONAL, "Switching Protocols"},
|
||||
{102, HTTP_TYPE_INFORMATIONAL, "Processing"},
|
||||
{103, HTTP_TYPE_INFORMATIONAL, "Early Hints"},
|
||||
|
||||
{200, "Success", "OK"},
|
||||
{201, "Success", "Created"},
|
||||
{202, "Success", "Accepted"},
|
||||
{203, "Success", "Non-Authoritative Information"},
|
||||
{204, "Success", "No Content"},
|
||||
{205, "Success", "Reset Content"},
|
||||
{206, "Success", "Partial Content"},
|
||||
{200, HTTP_TYPE_SUCCESS, "OK"},
|
||||
{201, HTTP_TYPE_SUCCESS, "Created"},
|
||||
{202, HTTP_TYPE_SUCCESS, "Accepted"},
|
||||
{203, HTTP_TYPE_SUCCESS, "Non-Authoritative Information"},
|
||||
{204, HTTP_TYPE_SUCCESS, "No Content"},
|
||||
{205, HTTP_TYPE_SUCCESS, "Reset Content"},
|
||||
{206, HTTP_TYPE_SUCCESS, "Partial Content"},
|
||||
{207, HTTP_TYPE_SUCCESS, "Multi-Status"},
|
||||
{208, HTTP_TYPE_SUCCESS, "Already Reported"},
|
||||
{226, HTTP_TYPE_SUCCESS, "Instance Manipulation Used"},
|
||||
|
||||
{300, "Redirection", "Multiple Choices"},
|
||||
{301, "Redirection", "Moved Permanently"},
|
||||
{302, "Redirection", "Found"},
|
||||
{303, "Redirection", "See Other"},
|
||||
{304, "Success", "Not Modified"},
|
||||
{305, "Redirection", "Use Proxy"},
|
||||
{307, "Redirection", "Temporary Redirect"},
|
||||
{308, "Redirection", "Permanent Redirect"},
|
||||
{300, HTTP_TYPE_REDIRECTION, "Multiple Choices"},
|
||||
{301, HTTP_TYPE_REDIRECTION, "Moved Permanently"},
|
||||
{302, HTTP_TYPE_REDIRECTION, "Found"},
|
||||
{303, HTTP_TYPE_REDIRECTION, "See Other"},
|
||||
{304, HTTP_TYPE_SUCCESS, "Not Modified"},
|
||||
{305, HTTP_TYPE_REDIRECTION, "Use Proxy"},
|
||||
{307, HTTP_TYPE_REDIRECTION, "Temporary Redirect"},
|
||||
{308, HTTP_TYPE_REDIRECTION, "Permanent Redirect"},
|
||||
|
||||
{400, "Client Error", "Bad Request"},
|
||||
{401, "Client Error", "Unauthorized"},
|
||||
{402, "Client Error", "Payment Required"},
|
||||
{403, "Client Error", "Forbidden"},
|
||||
{404, "Client Error", "Not Found"},
|
||||
{405, "Client Error", "Method Not Allowed"},
|
||||
{406, "Client Error", "Not Acceptable"},
|
||||
{407, "Client Error", "Proxy Authentication Required"},
|
||||
{408, "Client Error", "Request Timeout"},
|
||||
{409, "Client Error", "Conflict"},
|
||||
{410, "Client Error", "Gone"},
|
||||
{411, "Client Error", "Length Required"},
|
||||
{412, "Client Error", "Precondition Failed"},
|
||||
{413, "Client Error", "Request Entity Too Large"},
|
||||
{414, "Client Error", "Request-URI Too Long"},
|
||||
{415, "Client Error", "Unsupported Media Type"},
|
||||
{416, "Client Error", "Range Not Satisfiable"},
|
||||
{417, "Client Error", "Expectation Failed"},
|
||||
{400, HTTP_TYPE_CLIENT_ERROR, "Bad Request"},
|
||||
{401, HTTP_TYPE_CLIENT_ERROR, "Unauthorized"},
|
||||
{402, HTTP_TYPE_CLIENT_ERROR, "Payment Required"},
|
||||
{403, HTTP_TYPE_CLIENT_ERROR, "Forbidden"},
|
||||
{404, HTTP_TYPE_CLIENT_ERROR, "Not Found"},
|
||||
{405, HTTP_TYPE_CLIENT_ERROR, "Method Not Allowed"},
|
||||
{406, HTTP_TYPE_CLIENT_ERROR, "Not Acceptable"},
|
||||
{407, HTTP_TYPE_CLIENT_ERROR, "Proxy Authentication Required"},
|
||||
{408, HTTP_TYPE_CLIENT_ERROR, "Request Timeout"},
|
||||
{409, HTTP_TYPE_CLIENT_ERROR, "Conflict"},
|
||||
{410, HTTP_TYPE_CLIENT_ERROR, "Gone"},
|
||||
{411, HTTP_TYPE_CLIENT_ERROR, "Length Required"},
|
||||
{412, HTTP_TYPE_CLIENT_ERROR, "Precondition Failed"},
|
||||
{413, HTTP_TYPE_CLIENT_ERROR, "Request Entity Too Large"},
|
||||
{414, HTTP_TYPE_CLIENT_ERROR, "Request-URI Too Long"},
|
||||
{415, HTTP_TYPE_CLIENT_ERROR, "Unsupported Media Type"},
|
||||
{416, HTTP_TYPE_CLIENT_ERROR, "Range Not Satisfiable"},
|
||||
{417, HTTP_TYPE_CLIENT_ERROR, "Expectation Failed"},
|
||||
{421, HTTP_TYPE_CLIENT_ERROR, "Misdirected Request"},
|
||||
{422, HTTP_TYPE_CLIENT_ERROR, "Unprocessable Content"},
|
||||
{423, HTTP_TYPE_CLIENT_ERROR, "Locked"},
|
||||
{424, HTTP_TYPE_CLIENT_ERROR, "Failed Dependency"},
|
||||
{425, HTTP_TYPE_CLIENT_ERROR, "Too Early"},
|
||||
{426, HTTP_TYPE_CLIENT_ERROR, "Upgrade Required"},
|
||||
{428, HTTP_TYPE_CLIENT_ERROR, "Precondition Required"},
|
||||
{429, HTTP_TYPE_CLIENT_ERROR, "Too Many Requests"},
|
||||
{431, HTTP_TYPE_CLIENT_ERROR, "Request Header Fields Too Large"},
|
||||
{451, HTTP_TYPE_CLIENT_ERROR, "Unavailable For Legal Reasons"},
|
||||
|
||||
{500, "Server Error", "Internal Server Error"},
|
||||
{501, "Server Error", "Not Implemented"},
|
||||
{502, "Server Error", "Bad Gateway"},
|
||||
{503, "Server Error", "Service Unavailable"},
|
||||
{504, "Server Error", "Gateway Timeout"},
|
||||
{505, "Server Error", "HTTP Version Not Supported"},
|
||||
{500, HTTP_TYPE_SERVER_ERROR, "Internal Server Error"},
|
||||
{501, HTTP_TYPE_SERVER_ERROR, "Not Implemented"},
|
||||
{502, HTTP_TYPE_SERVER_ERROR, "Bad Gateway"},
|
||||
{503, HTTP_TYPE_SERVER_ERROR, "Service Unavailable"},
|
||||
{504, HTTP_TYPE_SERVER_ERROR, "Gateway Timeout"},
|
||||
{505, HTTP_TYPE_SERVER_ERROR, "HTTP Version Not Supported"},
|
||||
{506, HTTP_TYPE_SERVER_ERROR, "Variant Also Negotiates"},
|
||||
{507, HTTP_TYPE_SERVER_ERROR, "Insufficient Storage"},
|
||||
{508, HTTP_TYPE_SERVER_ERROR, "Loop Detected"},
|
||||
{511, HTTP_TYPE_SERVER_ERROR, "Network Authentication Required"},
|
||||
};
|
||||
|
||||
const http_status_msg http_status_messages[] = {
|
||||
{100, "The client SHOULD continue with its request."},
|
||||
{101, "The server understands and is willing to comply with the clients request, via the Upgrade message header field, for a change in the application protocol being used on this connection."},
|
||||
{100, "The client SHOULD continue with its request. The server MUST send a final response after the request "
|
||||
"has been completed."},
|
||||
{101, "The server understands and is willing to comply with the clients request, via the Upgrade message "
|
||||
"header field, for a change in the application protocol being used on this connection."},
|
||||
{102, "The server has a reasonable expectation that the request will take significant time to complete. The "
|
||||
"server MUST send a final response after the request has been completed."},
|
||||
{103, "The client can speculatively evaluate the header fields included in the response while waiting for the "
|
||||
"final response. The server MUST send a final response after the request has been completed."},
|
||||
|
||||
{200, "The request has succeeded."},
|
||||
{201, "The request has been fulfilled and resulted in a new resource being created."},
|
||||
{202, "The request has been accepted for processing, but the processing has not been completed."},
|
||||
{203, "The returned meta information in the entity-header is not the definitive set as available from the origin server, but is gathered from a local or a third-party copy."},
|
||||
{204, "The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta information."},
|
||||
{205, "The server has fulfilled the request and the user agent SHOULD reset the document view which caused the request to be sent."},
|
||||
{203, "The returned meta information in the entity-header is not the definitive set as available from the "
|
||||
"origin server, but is gathered from a local or a third-party copy."},
|
||||
{204, "The server has fulfilled the request but does not need to return an entity-body, and might want to "
|
||||
"return updated meta information."},
|
||||
{205, "The server has fulfilled the request and the user agent SHOULD reset the document view which caused the "
|
||||
"request to be sent."},
|
||||
{206, "The server has fulfilled the partial GET request for the resource."},
|
||||
{207, "The response provides status for multiple independent operations."},
|
||||
{208, "The response is used to avoid enumerating the internal members of multiple bindings to the same "
|
||||
"collection repeatedly."},
|
||||
{226, "The server has fulfilled a GET request for the resource, and the response is a representation of the "
|
||||
"result of one or more instance-manipulations applied to the current instance."},
|
||||
|
||||
{300, "The requested resource corresponds to any one of a set of representations, each with its own specific location, and agent-driven negotiation information is being provided so that the user (or user agent) can select a preferred representation and redirect its request to that location."},
|
||||
{301, "The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs."},
|
||||
{300, "The requested resource corresponds to any one of a set of representations, each with its own specific "
|
||||
"location, and agent-driven negotiation information is being provided so that the user (or user agent) "
|
||||
"can select a preferred representation and redirect its request to that location."},
|
||||
{301, "The requested resource has been assigned a new permanent URI and any future references to this resource "
|
||||
"SHOULD use one of the returned URIs."},
|
||||
{302, "The requested resource resides temporarily under a different URI."},
|
||||
{303, "The response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource."},
|
||||
{303, "The response to the request can be found under a different URI and SHOULD be retrieved using a GET "
|
||||
"method on that resource."},
|
||||
{304, "The request has been fulfilled and the requested resource has not been modified."},
|
||||
{305, "The requested resource MUST be accessed through the proxy given by the Location field."},
|
||||
{307, "The requested resource resides temporarily under a different URI."},
|
||||
{308, "The requested resource has been assigned a new permanent URI and any future references to this resource ought to use one of the enclosed URIs."},
|
||||
{308, "The requested resource has been assigned a new permanent URI and any future references to this resource "
|
||||
"ought to use one of the enclosed URIs."},
|
||||
|
||||
{400, "The request could not be understood by the server due to malformed syntax."},
|
||||
{401, "The request requires user authentication."},
|
||||
{403, "The server understood the request, but is refusing to fulfill it."},
|
||||
{404, "The server has not found anything matching the Request-URI."},
|
||||
{405, "The method specified in the Request-Line is not allowed for the resource identified by the Request-URI."},
|
||||
{406, "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request."},
|
||||
{405, "The method specified in the Request-Line is not allowed for the resource identified by the "
|
||||
"Request-URI."},
|
||||
{406, "The resource identified by the request is only capable of generating response entities which have "
|
||||
"content characteristics not acceptable according to the accept headers sent in the request."},
|
||||
{407, "The request requires user authentication on the proxy."},
|
||||
{408, "The client did not produce a request within the time that the server was prepared to wait."},
|
||||
{409, "The request could not be completed due to a conflict with the current state of the resource."},
|
||||
{410, "The requested resource is no longer available at the server and no forwarding address is known."},
|
||||
{411, "The server refuses to accept the request without a defined Content-Length."},
|
||||
{412, "The precondition given in one or more of the request-header fields evaluated to false when it was tested on the server."},
|
||||
{413, "The server is refusing to process a request because the request entity is larger than the server is willing or able to process."},
|
||||
{414, "The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret."},
|
||||
{415, "The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method."},
|
||||
{416, "None of the ranges in the requests Range header field overlap the current extent of the selected resource or that the set of ranges requested has been rejected due to invalid ranges or an excessive request of small or overlapping ranges."},
|
||||
{417, "The expectation given in an Expect request-header field could not be met by this server, or, if the server is a proxy, the server has unambiguous evidence that the request could not be met by the next-hop server."},
|
||||
{412, "The precondition given in one or more of the request-header fields evaluated to false when it was "
|
||||
"tested on the server."},
|
||||
{413, "The server is refusing to process a request because the request entity is larger than the server is "
|
||||
"willing or able to process."},
|
||||
{414, "The server is refusing to service the request because the Request-URI is longer than the server is "
|
||||
"willing to interpret."},
|
||||
{415, "The server is refusing to service the request because the entity of the request is in a format not "
|
||||
"supported by the requested resource for the requested method."},
|
||||
{416, "None of the ranges in the requests Range header field overlap the current extent of the selected "
|
||||
"resource or that the set of ranges requested has been rejected due to invalid ranges or an excessive "
|
||||
"request of small or overlapping ranges."},
|
||||
{417, "The expectation given in an Expect request-header field could not be met by this server, or, if the "
|
||||
"server is a proxy, the server has unambiguous evidence that the request could not be met by the "
|
||||
"next-hop server."},
|
||||
{421, "The server is not able to produce a response. The client MAY retry the request over a different "
|
||||
"connection."},
|
||||
{422, "The server understands the content type of the request content, and the syntax of the request content "
|
||||
"is correct, but the server was unable to process the contained information."},
|
||||
{423, "The source or destination resource of a method is locked."},
|
||||
{424, "The method could not be performed on the resource because the requested action depended on another "
|
||||
"action and that action failed."},
|
||||
{425, "The server is unwilling to risk processing a request that might be replayed."},
|
||||
{426, "The server refuses to perform the request using the current protocol but might be willing to do so "
|
||||
"after the client upgrades to a different protocol. The server MUST send an Upgrade header field to"
|
||||
"indicate the required protocol(s)."},
|
||||
{428, "The origin server requires the request to be conditional. By requiring requests to be conditional, the "
|
||||
"server can assure that clients are working with the correct copies and thus avoiding a lost update."},
|
||||
{429, "The client has sent too many requests in a given amount of time."},
|
||||
{431, "The server is unwilling to process the request because its header fields are too large. The request MAY "
|
||||
"be resubmitted after reducing the size of the request header fields."},
|
||||
{451, "The server is denying access to the resource as a consequence of a legal demand."},
|
||||
|
||||
{500, "The server encountered an unexpected condition which prevented it from fulfilling the request."},
|
||||
{501, "The server does not support the functionality required to fulfill the request."},
|
||||
{502, "The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request."},
|
||||
{503, "The server is currently unable to handle the request due to a temporary overloading or maintenance of the server."},
|
||||
{504, "The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI or some other auxiliary server it needed to access in attempting to complete the request."},
|
||||
{505, "The server does not support, or refuses to support, the HTTP protocol version that was used in the request message."}
|
||||
{502, "The server, while acting as a gateway or proxy, received an invalid response from the upstream server "
|
||||
"it accessed in attempting to fulfill the request."},
|
||||
{503, "The server is currently unable to handle the request due to a temporary overloading or maintenance of "
|
||||
"the server."},
|
||||
{504, "The server, while acting as a gateway or proxy, did not receive a timely response from the upstream "
|
||||
"server specified by the URI or some other auxiliary server it needed to access in attempting to "
|
||||
"complete the request."},
|
||||
{505, "The server does not support, or refuses to support, the HTTP protocol version that was used in the "
|
||||
"request message."},
|
||||
{506, "The server has an internal configuration error: the chosen variant resource is configured to engage in "
|
||||
"transparent content negotiation itself, and is therefore not a proper end point in the negotiation "
|
||||
"process."},
|
||||
{507, "The method could not be performed on the resource because the server is unable to store the "
|
||||
"representation needed to successfully complete the request. This condition is considered to be "
|
||||
"temporary."},
|
||||
{508, "The server terminated an operation because it encountered an infinite loop while processing the "
|
||||
"request."},
|
||||
{511, "The client needs to authenticate to gain network access. The response representation SHOULD contain a "
|
||||
"link to a resource that allows the user to submit credentials."},
|
||||
};
|
||||
|
||||
const char http_default_document[] =
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html lang=\"en\">\n"
|
||||
"<head>\n"
|
||||
"\t<title>%1$i %2$s - %7$s</title>\n"
|
||||
"\t<meta charset=\"UTF-8\"/>\n"
|
||||
"\t<meta name=\"theme-color\" content=\"%6$s\"/>\n"
|
||||
"\t<meta name=\"color-scheme\" content=\"light dark\"/>\n"
|
||||
"\t<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\"/>\n"
|
||||
"\t<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"/>\n"
|
||||
"\t<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\"/>\n"
|
||||
"%5$s"
|
||||
"\t<style>\n"
|
||||
"\t\thtml{font-family:\"Arial\",sans-serif;--error:" HTTP_COLOR_ERROR ";--warning:" HTTP_COLOR_WARNING ";--success:" HTTP_COLOR_SUCCESS ";--info:" HTTP_COLOR_INFO ";--soft:#808080;--color:var(--%4$s);}\n"
|
||||
"\t\tbody{background-color:#F0F0F0;margin:0;}\n"
|
||||
"\t\tmain{max-width:650px;margin:2em auto;}\n"
|
||||
"\t\tsection{margin:2em 1em;background-color:#FFFFFF;border: 1px solid var(--color);border-radius:4px;padding:1em;}\n"
|
||||
"\t\th1,h2,h3,h4,h5,h6{text-align:center;color:var(--color);font-weight:normal;}\n"
|
||||
"\t\th1{font-size:3em;margin:0.125em 0;}\n"
|
||||
"\t\th2{font-size:1.5em;margin:0.25em 0 1em 0;}\n"
|
||||
"\t\tp{text-align:center;font-size:0.875em;}\n"
|
||||
"\t\tdiv.footer{color:var(--soft);font-size:0.75em;text-align:center;margin:2em 0 0.5em 0;}\n"
|
||||
"\t\tdiv.footer a{color:var(--soft);}\n"
|
||||
"\t\tul,ol{width:fit-content;margin:auto;}\n"
|
||||
"\t\tpre{width:fit-content;margin:2em auto 0 auto;}\n"
|
||||
"\n"
|
||||
"\t\tsection.error-ctx{display:flex;padding:0;border:none;}\n"
|
||||
"\t\tdiv.box{flex:100%% 1 1;border:1px solid var(--info);color:var(--info);position:relative;padding:1em;box-sizing:border-box;text-align:center;}\n"
|
||||
"\t\tdiv.box.error{border-color:var(--error);color:var(--error);}\n"
|
||||
"\t\tdiv.box.success{border-color:var(--success);color:var(--success);}\n"
|
||||
"\t\tdiv.arrow{position:absolute;height:20px;width:30px;z-index:10;background-repeat:no-repeat;background-size:contain;}\n"
|
||||
"\t\tdiv.arrow.response{left:-17.5px;bottom:calc(33.3333%% - 10px);}\n"
|
||||
"\t\tdiv.arrow.request{right:-17.5px;top:calc(33.3333%% - 10px);}\n"
|
||||
"\t\tdiv.border{flex:1px 0 0;background-color:var(--info);}\n"
|
||||
"\t\tdiv.border.error{background-color:var(--error);}\n"
|
||||
"\t\tdiv.border.success{background-color:var(--success);}\n"
|
||||
"\t\tdiv.content>span{display:block;color:var(--soft);font-size:0.75em;}\n"
|
||||
"\t\tdiv.content>img{height:3.75rem;margin:0.75rem auto;display:block;}\n"
|
||||
"\t\th3{font-size:2.25em;margin:0.75rem 0 0 0;color:unset;height:2.5rem;}\n"
|
||||
"\t\th4{font-size:1em;margin:0 0 0.75rem 0;color:unset;height:1.25rem;}\n"
|
||||
"\n"
|
||||
"\t\tdiv.arrow.request.success{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjRkZG"
|
||||
"RkZGIiBzdHJva2U9IiMwMDgwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}\n"
|
||||
"\t\tdiv.arrow.request.error{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjRkZG"
|
||||
"RkZGIiBzdHJva2U9IiNDMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}\n"
|
||||
"\t\tdiv.arrow.response.success{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiNGRkZG"
|
||||
"RkYiIHN0cm9rZT0iIzAwODAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}\n"
|
||||
"\t\tdiv.arrow.response.error{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiNGRkZG"
|
||||
"RkYiIHN0cm9rZT0iI0MwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}\n"
|
||||
"\n"
|
||||
"\t\t@media(prefers-color-scheme:dark){\n"
|
||||
"\t\t\thtml{color:#FFFFFF;--soft:#404040;}\n"
|
||||
"\t\t\tbody{background-color:#101010;}\n"
|
||||
"\t\t\tsection{background-color:#181818;}\n"
|
||||
"\n"
|
||||
"\t\t\tdiv.arrow.request.success{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjMTgx"
|
||||
"ODE4IiBzdHJva2U9IiMwMDgwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}\n"
|
||||
"\t\t\tdiv.arrow.request.error{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTEsMSBMMjUsMSBMMjksMTAgTDI1LDE5IEwxLDE5IiBmaWxsPSIjMTgx"
|
||||
"ODE4IiBzdHJva2U9IiNDMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIvPjwvc3ZnPgo=');}\n"
|
||||
"\t\t\tdiv.arrow.response.success{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiMxODE4"
|
||||
"MTgiIHN0cm9rZT0iIzAwODAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}\n"
|
||||
"\t\t\tdiv.arrow.response.error{background-image:url('data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTI5LDE5IEw1LDE5IEwxLDEwIEw1LDEgTDI5LDEiIGZpbGw9IiMxODE4"
|
||||
"MTgiIHN0cm9rZT0iI0MwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+Cg==');}\n"
|
||||
"\t\t}\n"
|
||||
"\t\t@media(min-width:650px){\n"
|
||||
"\t\t\tdiv.box:first-child{border-top-left-radius:4px;border-bottom-left-radius:4px;border-right:none;}\n"
|
||||
"\t\t\tdiv.box:last-child{border-top-right-radius:4px;border-bottom-right-radius:4px;border-left:none;}\n"
|
||||
"\t\t\tdiv.box:not(:last-child):not(:first-child){border-left:none;border-right:none;}\n"
|
||||
"\t\t}\n"
|
||||
"\t\t@media(max-width:650px){\n"
|
||||
"\t\t\tsection.error-ctx{flex-direction:column;height:unset;}\n"
|
||||
"\t\t\tdiv.box:first-child{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom:none;padding-top:1em;}\n"
|
||||
"\t\t\tdiv.box:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top:none;padding-bottom:1em;}\n"
|
||||
"\t\t\tdiv.box:not(:last-child):not(:first-child){border-top:none;border-bottom:none;}\n"
|
||||
"\t\t\tdiv.arrow.response{transform:rotate(90deg);top:-10px;left:calc(33.3333%% - 22.5px);right:unset;}\n"
|
||||
"\t\t\tdiv.arrow.request{transform:rotate(90deg);bottom:-10px;right:calc(33.3333%% - 22.5px);top:unset;}\n"
|
||||
"\t\t}\n"
|
||||
"\t</style>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"\t<main>\n"
|
||||
"\t\t<section>\n"
|
||||
"%3$s"
|
||||
"%9$s"
|
||||
"\t\t\t<div class=\"footer\"><a href=\"https://%7$s/\">%7$s</a> - " SERVER_STR_HTML "</div>\n"
|
||||
"\t\t</section>\n"
|
||||
"%8$s"
|
||||
"\t</main>\n"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
const int http_statuses_size = sizeof(http_statuses) / sizeof(http_status);
|
||||
const int http_status_messages_size = sizeof(http_status_messages) / sizeof(http_status_msg);
|
||||
|
||||
const char http_proxy_document[] =
|
||||
"\t\t<section class=\"error-ctx\">\n"
|
||||
"\t\t\t<div class=\"box%1$s\">\n"
|
||||
"\t\t\t\t<div class=\"content\">\n"
|
||||
"\t\t\t\t\t<span>Client</span>\n"
|
||||
"\t\t\t\t\t<img src=\"data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHBhdGggZD0iTTIsMzIgYTMwLDMwLDAsMSwwLDYwLDAgYTMwLDMwLDAsMSwwLC02MCww"
|
||||
"IEw2MiwzMiBNNiwxNiBMNTgsMTYgTTYsNDggTDU4LDQ4IE0zMiwyIEwzMiw2MiBhMTUsMzAsMCwx"
|
||||
"LDAsMCwtNjAgYTE1LDMwLDAsMSwwLDAsNjAgWiIgc3Ryb2tlPSIjMDA4MDAwIiBzdHJva2Utd2lk"
|
||||
"dGg9IjIiIGZpbGw9IiMwMDAwMDAwMCIvPjwvc3ZnPgo=\"/>\n"
|
||||
"\t\t\t\t\t<span>Your Browser</span>\n"
|
||||
"\t\t\t\t</div>\n"
|
||||
"\t\t\t\t<div class=\"arrow request%2$s\"></div>\n"
|
||||
"\t\t\t</div>\n"
|
||||
"\t\t\t<div class=\"border%8$s\"></div>\n"
|
||||
"\t\t\t<div class=\"box%3$s\">\n"
|
||||
"\t\t\t\t<div class=\"content\">\n"
|
||||
"\t\t\t\t\t<span>Reverse Proxy</span>\n"
|
||||
"\t\t\t\t\t<h3>%10$03i</h3>\n"
|
||||
"\t\t\t\t\t<h4>%11$s</h4>\n"
|
||||
"\t\t\t\t\t<span>" SERVER_NAME "</span>\n"
|
||||
"\t\t\t\t</div>\n"
|
||||
"\t\t\t\t<div class=\"arrow request%4$s\"></div>\n"
|
||||
"\t\t\t\t<div class=\"arrow response%5$s\"></div>\n"
|
||||
"\t\t\t</div>\n"
|
||||
"\t\t\t<div class=\"border%9$s\"></div>\n"
|
||||
"\t\t\t<div class=\"box%6$s\">\n"
|
||||
"\t\t\t\t<div class=\"content\">\n"
|
||||
"\t\t\t\t\t<span>Server</span>\n"
|
||||
"\t\t\t\t\t<h3>%12$s</h3>\n"
|
||||
"\t\t\t\t\t<h4>%13$s</h4>\n"
|
||||
"\t\t\t\t\t<span>%14$s</span>\n"
|
||||
"\t\t\t\t</div>\n"
|
||||
"\t\t\t\t<div class=\"arrow response%7$s\"></div>\n"
|
||||
"\t\t\t</div>\n"
|
||||
"\t\t</section>\n";
|
||||
const char http_error_doc[] =
|
||||
" <h1>%1$i</h1>\n"
|
||||
" <h2>%2$s :(</h2>\n"
|
||||
" <p>%3$s</p>\n"
|
||||
" <p>%4$s</p>\n";
|
||||
|
||||
const char http_error_document[] =
|
||||
"\t\t\t<h1>%1$i</h1>\n"
|
||||
"\t\t\t<h2>%2$s :(</h2>\n"
|
||||
"\t\t\t<p>%3$s</p>\n"
|
||||
"\t\t\t<p>%4$s</p>\n";
|
||||
const char http_warning_doc[] =
|
||||
" <h1>%1$i</h1>\n"
|
||||
" <h2>%2$s :)</h2>\n"
|
||||
" <p>%3$s</p>\n"
|
||||
" <p>%4$s</p>\n";
|
||||
|
||||
const char http_error_icon[] =
|
||||
"\t<link rel=\"alternate icon\" type=\"image/svg+xml\" sizes=\"any\" href=\"data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHRleHQgeD0iNCIgeT0iMTIiIGZpbGw9IiNDMDAwMDAiIHN0eWxlPSJmb250LWZhbWls"
|
||||
"eTonQXJpYWwnLHNhbnMtc2VyaWYiPjooPC90ZXh0Pjwvc3ZnPgo=\"/>\n";
|
||||
const char http_success_doc[] =
|
||||
" <h1>%1$i</h1>\n"
|
||||
" <h2>%2$s :)</h2>\n"
|
||||
" <p>%3$s</p>\n"
|
||||
" <p>%4$s</p>\n";
|
||||
|
||||
|
||||
const char http_warning_document[] =
|
||||
"\t\t\t<h1>%1$i</h1>\n"
|
||||
"\t\t\t<h2>%2$s :)</h2>\n"
|
||||
"\t\t\t<p>%3$s</p>\n"
|
||||
"\t\t\t<p>%4$s</p>\n";
|
||||
|
||||
const char http_warning_icon[] =
|
||||
"\t<link rel=\"alternate icon\" type=\"image/svg+xml\" sizes=\"any\" href=\"data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHRleHQgeD0iNCIgeT0iMTIiIGZpbGw9IiNFMEMwMDAiIHN0eWxlPSJmb250LWZhbWls"
|
||||
"eTonQXJpYWwnLHNhbnMtc2VyaWYiPjopPC90ZXh0Pjwvc3ZnPgo=\"/>\n";
|
||||
|
||||
|
||||
const char http_success_document[] =
|
||||
"\t\t\t<h1>%1$i</h1>\n"
|
||||
"\t\t\t<h2>%2$s :)</h2>\n"
|
||||
"\t\t\t<p>%3$s</p>\n"
|
||||
"\t\t\t<p>%4$s</p>\n";
|
||||
|
||||
const char http_success_icon[] =
|
||||
"\t<link rel=\"alternate icon\" type=\"image/svg+xml\" sizes=\"any\" href=\"data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHRleHQgeD0iNCIgeT0iMTIiIGZpbGw9IiMwMDgwMDAiIHN0eWxlPSJmb250LWZhbWls"
|
||||
"eTonQXJpYWwnLHNhbnMtc2VyaWYiPjopPC90ZXh0Pjwvc3ZnPgo=\"/>\n";
|
||||
|
||||
|
||||
const char http_info_document[] =
|
||||
"\t\t\t<h1>%1$i</h1>\n"
|
||||
"\t\t\t<h2>%2$s :)</h2>\n"
|
||||
"\t\t\t<p>%3$s</p>\n"
|
||||
"\t\t\t<p>%4$s</p>\n";
|
||||
|
||||
const char http_info_icon[] =
|
||||
"\t<link rel=\"alternate icon\" type=\"image/svg+xml\" sizes=\"any\" href=\"data:image/svg+xml;base64,"
|
||||
"PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAw"
|
||||
"L3N2ZyI+PHRleHQgeD0iNCIgeT0iMTIiIGZpbGw9IiM2MDYwNjAiIHN0eWxlPSJmb250LWZhbWls"
|
||||
"eTonQXJpYWwnLHNhbnMtc2VyaWYiPjopPC90ZXh0Pjwvc3ZnPgo=\"/>\n";
|
||||
|
||||
const int http_statuses_size = sizeof(http_statuses);
|
||||
const int http_status_messages_size = sizeof(http_status_messages);
|
||||
const char http_info_doc[] =
|
||||
" <h1>%1$i</h1>\n"
|
||||
" <h2>%2$s :)</h2>\n"
|
||||
" <p>%3$s</p>\n"
|
||||
" <p>%4$s</p>\n";
|
||||
|
139
src/lib/list.c
Normal file
139
src/lib/list.c
Normal file
@@ -0,0 +1,139 @@
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define FACTOR 4
|
||||
|
||||
typedef struct {
|
||||
int init_size, elem_size, max_size, size;
|
||||
} list_meta_t;
|
||||
|
||||
static void *list_resize(list_meta_t *list, int new_size) {
|
||||
if (new_size <= 0)
|
||||
return NULL;
|
||||
|
||||
list_meta_t *new_ptr = realloc(list, sizeof(list_meta_t) + list->elem_size * new_size);
|
||||
if (new_ptr == NULL)
|
||||
return NULL;
|
||||
|
||||
new_ptr->max_size = new_size;
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
void *list_create(int elem_size, int init_elem_n) {
|
||||
if (elem_size <= 0 || init_elem_n <= 0) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *list_ptr = malloc(sizeof(list_meta_t) + elem_size * init_elem_n);
|
||||
list_meta_t *list = list_ptr;
|
||||
list->init_size = init_elem_n;
|
||||
list->elem_size = elem_size;
|
||||
list->max_size = init_elem_n;
|
||||
list->size = 0;
|
||||
return (unsigned char *) list_ptr + sizeof(list_meta_t);
|
||||
}
|
||||
|
||||
int list_size(const void *list_ptr) {
|
||||
list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t));
|
||||
return list->size;
|
||||
}
|
||||
|
||||
int list_find(void *list_ptr, void *elem) {
|
||||
list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t));
|
||||
unsigned char *array = list_ptr;
|
||||
|
||||
for (int i = 0; i < list->size; i++) {
|
||||
if (memcmp(array + i * list->elem_size, elem, list->elem_size) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *list_insert(void *list_ptr, void *elem, int n) {
|
||||
void *ptr = NULL;
|
||||
list_ptr = list_insert_ptr(list_ptr, &ptr, n);
|
||||
if (list_ptr != NULL && ptr != NULL) {
|
||||
list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t));
|
||||
memcpy(ptr, elem, list->elem_size);
|
||||
}
|
||||
|
||||
return list_ptr;
|
||||
}
|
||||
|
||||
void *list_insert_ptr(void *list_ptr, void **elem, int n) {
|
||||
list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t));
|
||||
if (n < 0)
|
||||
n = list->size + n + 1;
|
||||
|
||||
if (list->size >= list->max_size) {
|
||||
if ((list = list_resize(list, list->max_size * FACTOR)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *array = (unsigned char *) list + sizeof(list_meta_t);
|
||||
|
||||
if (n < list->size)
|
||||
memmove(array + (n + 1) * list->elem_size, array + n * list->elem_size, (list->size - n) * list->elem_size);
|
||||
*elem = array + n * list->elem_size;
|
||||
|
||||
list->size++;
|
||||
return (unsigned char *) list + sizeof(list_meta_t);
|
||||
}
|
||||
|
||||
void *list_append(void *list_ptr, void *elem) {
|
||||
return list_insert(list_ptr, elem, -1);
|
||||
}
|
||||
|
||||
void *list_append_ptr(void *list_ptr, void **elem) {
|
||||
return list_insert_ptr(list_ptr, elem, -1);
|
||||
}
|
||||
|
||||
void *list_remove(void *list_ptr, int n) {
|
||||
list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t));
|
||||
if (n < 0)
|
||||
n = list->size + n;
|
||||
|
||||
unsigned char *array = list_ptr;
|
||||
|
||||
if (list->size > 1 && n < list->size)
|
||||
memmove(array + n * list->elem_size, array + (n + 1) * list->elem_size, (list->size - n - 1) * list->elem_size);
|
||||
|
||||
list->size--;
|
||||
if (list->size < list->max_size / FACTOR / 2 && list->max_size / FACTOR >= list->init_size) {
|
||||
if ((list = list_resize(list, list->max_size / FACTOR)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return (unsigned char *) list + sizeof(list_meta_t);
|
||||
}
|
||||
|
||||
void *list_delete(void *list_ptr, void *elem) {
|
||||
int idx = list_find(list_ptr, elem);
|
||||
if (idx == -1) {
|
||||
return list_ptr;
|
||||
} else {
|
||||
return list_remove(list_ptr, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void *list_clear(void *list_ptr) {
|
||||
list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t));
|
||||
list->size = 0;
|
||||
memset(list_ptr, 0, list->max_size * list->elem_size);
|
||||
list->max_size = list->init_size;
|
||||
return (unsigned char *) list_resize(list, list->max_size * list->elem_size) + sizeof(list_meta_t);
|
||||
}
|
||||
|
||||
void list_free(void *list_ptr) {
|
||||
list_meta_t *list = (void *) ((unsigned char *) list_ptr - sizeof(list_meta_t));
|
||||
free(list);
|
||||
}
|
27
src/lib/list.h
Normal file
27
src/lib/list.h
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
#ifndef SESIMOS_LIST_H
|
||||
#define SESIMOS_LIST_H
|
||||
|
||||
void *list_create(int elem_size, int init_elem_n);
|
||||
|
||||
int list_size(const void *list_ptr);
|
||||
|
||||
int list_find(void *list_ptr, void *elem);
|
||||
|
||||
void *list_insert(void *list_ptr, void *elem, int n);
|
||||
|
||||
void *list_insert_ptr(void *list_ptr, void **elem, int n);
|
||||
|
||||
void *list_append(void *list_ptr, void *elem);
|
||||
|
||||
void *list_append_ptr(void *list_ptr, void **elem);
|
||||
|
||||
void *list_remove(void *list_ptr, int n);
|
||||
|
||||
void *list_delete(void *list_ptr, void *elem);
|
||||
|
||||
void *list_clear(void *list_ptr);
|
||||
|
||||
void list_free(void *list_ptr);
|
||||
|
||||
#endif //SESIMOS_LIST_H
|
@@ -10,13 +10,14 @@
|
||||
|
||||
static void *mpmc_worker(void *arg);
|
||||
|
||||
int mpmc_init(mpmc_t *ctx, int n_workers, int buf_size, void (*consumer)(void *obj), const char *prefix) {
|
||||
int mpmc_init(mpmc_t *ctx, int n_workers, int buf_size, void (*consumer)(void *obj), const char *name) {
|
||||
ctx->alive = 1;
|
||||
ctx->n_workers = n_workers;
|
||||
ctx->size = buf_size, ctx->max_size = buf_size;
|
||||
ctx->rd = 0, ctx->wr = 0;
|
||||
ctx->buffer = NULL, ctx->workers = NULL;
|
||||
ctx->consumer = consumer;
|
||||
ctx->name = name;
|
||||
|
||||
if (sem_init(&ctx->free, 0, ctx->size) != 0 ||
|
||||
sem_init(&ctx->used, 0, 0) != 0 ||
|
||||
@@ -34,8 +35,8 @@ int mpmc_init(mpmc_t *ctx, int n_workers, int buf_size, void (*consumer)(void *o
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(ctx->buffer, 0, ctx->size * sizeof(void *));
|
||||
memset(ctx->workers, 0, ctx->n_workers * sizeof(pthread_t));
|
||||
memset(ctx->buffer, 0, ctx->size * sizeof(void *));
|
||||
memset(ctx->workers, -1, ctx->n_workers * sizeof(pthread_t));
|
||||
|
||||
for (int i = 0; i < ctx->n_workers; i++) {
|
||||
int ret;
|
||||
@@ -61,9 +62,9 @@ int mpmc_queue(mpmc_t *ctx, void *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
// lock rd field
|
||||
// lock wr field
|
||||
try_again_2:
|
||||
if (sem_wait(&ctx->lck_rd) != 0) {
|
||||
if (sem_wait(&ctx->lck_wr) != 0) {
|
||||
if (errno == EINTR) {
|
||||
errno = 0;
|
||||
goto try_again_2;
|
||||
@@ -73,11 +74,11 @@ int mpmc_queue(mpmc_t *ctx, void *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
int p = ctx->rd;
|
||||
ctx->rd = (ctx->rd + 1) % ctx->size;
|
||||
int p = ctx->wr;
|
||||
ctx->wr = (ctx->wr + 1) % ctx->size;
|
||||
|
||||
// unlock rd field
|
||||
sem_post(&ctx->lck_rd);
|
||||
// unlock wr field
|
||||
sem_post(&ctx->lck_wr);
|
||||
|
||||
// fill buffer with object
|
||||
ctx->buffer[p] = obj;
|
||||
@@ -90,6 +91,11 @@ int mpmc_queue(mpmc_t *ctx, void *obj) {
|
||||
|
||||
static void *mpmc_worker(void *arg) {
|
||||
mpmc_t *ctx = arg;
|
||||
|
||||
int id;
|
||||
for (id = 0; id < ctx->n_workers && ctx->workers[id] != pthread_self(); id++);
|
||||
logger_set_name("%s/%i", ctx->name, id);
|
||||
|
||||
while (ctx->alive) {
|
||||
// wait for buffer to be filled
|
||||
if (sem_wait(&ctx->used) != 0) {
|
||||
@@ -103,8 +109,8 @@ static void *mpmc_worker(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
// lock wr field
|
||||
if (sem_wait(&ctx->lck_wr) != 0) {
|
||||
// lock rd field
|
||||
if (sem_wait(&ctx->lck_rd) != 0) {
|
||||
if (errno == EINTR) {
|
||||
errno = 0;
|
||||
sem_post(&ctx->used);
|
||||
@@ -117,11 +123,11 @@ static void *mpmc_worker(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
int p = ctx->wr;
|
||||
ctx->wr = (ctx->wr + 1) % ctx->size;
|
||||
int p = ctx->rd;
|
||||
ctx->rd = (ctx->rd + 1) % ctx->size;
|
||||
|
||||
// unlock wr field
|
||||
sem_post(&ctx->lck_wr);
|
||||
// unlock rd field
|
||||
sem_post(&ctx->lck_rd);
|
||||
|
||||
// consume object
|
||||
ctx->consumer(ctx->buffer[p]);
|
||||
@@ -144,11 +150,9 @@ void mpmc_destroy(mpmc_t *ctx) {
|
||||
// stop threads, if running
|
||||
mpmc_stop(ctx);
|
||||
for (int i = 0; i < ctx->n_workers; i++) {
|
||||
if (ctx->workers[i] == 0) break;
|
||||
// FIXME
|
||||
if (ctx->workers[i] == -1) break;
|
||||
pthread_kill(ctx->workers[i], SIGUSR1);
|
||||
//pthread_join(ctx->workers[i], NULL);
|
||||
pthread_cancel(ctx->workers[i]);
|
||||
pthread_join(ctx->workers[i], NULL);
|
||||
}
|
||||
|
||||
sem_destroy(&ctx->free);
|
||||
|
@@ -13,9 +13,10 @@ typedef struct {
|
||||
void **buffer;
|
||||
pthread_t *workers;
|
||||
void (*consumer)(void *obj);
|
||||
const char* name;
|
||||
} mpmc_t;
|
||||
|
||||
int mpmc_init(mpmc_t *ctx, int n_workers, int buf_size, void (*consumer)(void *obj), const char *prefix);
|
||||
int mpmc_init(mpmc_t *ctx, int n_workers, int buf_size, void (*consumer)(void *obj), const char *name);
|
||||
|
||||
int mpmc_queue(mpmc_t *ctx, void *obj);
|
||||
|
||||
|
270
src/lib/proxy.c
270
src/lib/proxy.c
@@ -12,25 +12,138 @@
|
||||
#include "proxy.h"
|
||||
#include "utils.h"
|
||||
#include "compress.h"
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/err.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
#include <netdb.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
|
||||
sock proxy;
|
||||
char *proxy_host = NULL;
|
||||
struct timeval server_timeout = {.tv_sec = SERVER_TIMEOUT, .tv_usec = 0};
|
||||
static SSL_CTX *proxy_ctx = NULL;
|
||||
static proxy_ctx_t *proxies = NULL;
|
||||
static sem_t *available = NULL;
|
||||
static sem_t lock;
|
||||
static int num_proxy_hosts = -1;
|
||||
|
||||
int proxy_preload(void) {
|
||||
proxy.ctx = SSL_CTX_new(TLS_client_method());
|
||||
int n = 0;
|
||||
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
|
||||
host_config_t *hc = &config.hosts[i];
|
||||
if (hc->type == CONFIG_TYPE_UNSET) break;
|
||||
if (hc->type != CONFIG_TYPE_REVERSE_PROXY) continue;
|
||||
n++;
|
||||
}
|
||||
|
||||
proxy_ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (proxy_ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
proxies = malloc(n * MAX_PROXY_CNX_PER_HOST * sizeof(proxy_ctx_t));
|
||||
if (proxies == NULL) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
memset(proxies, 0, n * MAX_PROXY_CNX_PER_HOST * sizeof(proxy_ctx_t));
|
||||
|
||||
available = malloc(n * sizeof(*available));
|
||||
if (available == NULL) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (sem_init(&available[i], 0, MAX_PROXY_CNX_PER_HOST) != 0) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (sem_init(&lock, 0, 1) != 0) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_proxy_hosts = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proxy_request_header(http_req *req, int enc, client_ctx_t *ctx) {
|
||||
void proxy_unload(void) {
|
||||
int e = errno;
|
||||
SSL_CTX_free(proxy_ctx);
|
||||
sem_destroy(&lock);
|
||||
if (num_proxy_hosts != -1) {
|
||||
for (int i = 0; i < num_proxy_hosts; i++) {
|
||||
sem_destroy(&available[i]);
|
||||
}
|
||||
}
|
||||
free(available);
|
||||
free(proxies);
|
||||
errno = e;
|
||||
}
|
||||
|
||||
void proxy_close_all(void) {
|
||||
proxy_ctx_t *ptr = proxies;
|
||||
for (int i = 0; i < MAX_PROXY_CNX_PER_HOST * num_proxy_hosts; i++, ptr++) {
|
||||
if (ptr->initialized)
|
||||
proxy_close(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
proxy_ctx_t *proxy_get_by_conf(host_config_t *conf) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
|
||||
host_config_t *hc = &config.hosts[i];
|
||||
if (hc->type == CONFIG_TYPE_UNSET) break;
|
||||
if (hc->type != CONFIG_TYPE_REVERSE_PROXY) continue;
|
||||
if (hc == conf) break;
|
||||
n++;
|
||||
}
|
||||
|
||||
try_again_1:
|
||||
if (sem_wait(&available[n]) != 0) {
|
||||
if (errno == EINTR) {
|
||||
goto try_again_1;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
try_again_2:
|
||||
if (sem_wait(&lock) != 0) {
|
||||
if (errno == EINTR) {
|
||||
goto try_again_2;
|
||||
} else {
|
||||
sem_post(&available[n]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
proxy_ctx_t *ptr = proxies + n * MAX_PROXY_CNX_PER_HOST;
|
||||
for (int i = 0; i < MAX_PROXY_CNX_PER_HOST; i++, ptr++) {
|
||||
if (!ptr->in_use) {
|
||||
ptr->in_use = 1;
|
||||
sem_post(&lock);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
sem_post(&lock);
|
||||
sem_post(&available[n]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void proxy_unlock_ctx(proxy_ctx_t *ctx) {
|
||||
int n = (int) ((ctx - proxies) / MAX_PROXY_CNX_PER_HOST);
|
||||
ctx->in_use = 0;
|
||||
sem_post(&available[n]);
|
||||
}
|
||||
|
||||
int proxy_request_header(http_req *req, sock *sock) {
|
||||
char buf1[256], buf2[256];
|
||||
int p_len;
|
||||
|
||||
@@ -50,13 +163,13 @@ int proxy_request_header(http_req *req, int enc, client_ctx_t *ctx) {
|
||||
|
||||
const char *host = http_get_header_field(&req->hdr, "Host");
|
||||
const char *forwarded = http_get_header_field(&req->hdr, "Forwarded");
|
||||
int client_ipv6 = strchr(ctx->addr, ':') != NULL;
|
||||
int server_ipv6 = strchr(ctx->s_addr, ':') != NULL;
|
||||
int client_ipv6 = strchr(sock->addr, ':') != NULL;
|
||||
int server_ipv6 = strchr(sock->s_addr, ':') != NULL;
|
||||
|
||||
p_len = snprintf(buf1, sizeof(buf1), "by=%s%s%s;for=%s%s%s;host=%s;proto=%s",
|
||||
server_ipv6 ? "\"[" : "", ctx->s_addr, server_ipv6 ? "]\"" : "",
|
||||
client_ipv6 ? "\"[" : "", ctx->addr, client_ipv6 ? "]\"" : "",
|
||||
host, enc ? "https" : "http");
|
||||
server_ipv6 ? "\"[" : "", sock->s_addr, server_ipv6 ? "]\"" : "",
|
||||
client_ipv6 ? "\"[" : "", sock->addr, client_ipv6 ? "]\"" : "",
|
||||
host, sock->enc ? "https" : "http");
|
||||
if (p_len < 0 || p_len >= sizeof(buf1)) {
|
||||
error("Appended part of header field 'Forwarded' too long");
|
||||
return -1;
|
||||
@@ -76,9 +189,9 @@ int proxy_request_header(http_req *req, int enc, client_ctx_t *ctx) {
|
||||
|
||||
const char *xff = http_get_header_field(&req->hdr, "X-Forwarded-For");
|
||||
if (xff == NULL) {
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-For", ctx->addr);
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-For", sock->addr);
|
||||
} else {
|
||||
sprintf(buf1, "%s, %s", xff, ctx->addr);
|
||||
sprintf(buf1, "%s, %s", xff, sock->addr);
|
||||
http_remove_header_field(&req->hdr, "X-Forwarded-For", HTTP_REMOVE_ALL);
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-For", buf1);
|
||||
}
|
||||
@@ -107,7 +220,7 @@ int proxy_request_header(http_req *req, int enc, client_ctx_t *ctx) {
|
||||
const char *xfp = http_get_header_field(&req->hdr, "X-Forwarded-Proto");
|
||||
if (xfp == NULL) {
|
||||
if (forwarded == NULL) {
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-Proto", enc ? "https" : "http");
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-Proto", sock->enc ? "https" : "http");
|
||||
} else {
|
||||
char *ptr = strchr(forwarded, ',');
|
||||
unsigned long len;
|
||||
@@ -180,43 +293,44 @@ int proxy_response_header(http_req *req, http_res *res, host_config_t *conf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, client_ctx_t *cctx, http_status *custom_status, char *err_msg) {
|
||||
char buffer[CHUNK_SIZE];
|
||||
int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg) {
|
||||
char buffer[CHUNK_SIZE], err_buf[256];
|
||||
const char *connection, *upgrade, *ws_version;
|
||||
long ret;
|
||||
int tries = 0, retry = 0;
|
||||
|
||||
if (proxy.socket != 0 && strcmp(proxy_host, conf->name) == 0 && sock_check(&proxy) == 0)
|
||||
*proxy_ptr = proxy_get_by_conf(conf);
|
||||
proxy_ctx_t *proxy = *proxy_ptr;
|
||||
proxy->client = NULL;
|
||||
|
||||
if (proxy->initialized && sock_has_pending(&proxy->proxy) == 0)
|
||||
goto proxy;
|
||||
|
||||
retry:
|
||||
if (proxy.socket != 0) {
|
||||
if (proxy->initialized) {
|
||||
info(BLUE_STR "Closing proxy connection");
|
||||
sock_close(&proxy);
|
||||
sock_close(&proxy->proxy);
|
||||
proxy->initialized = 0;
|
||||
}
|
||||
retry = 0;
|
||||
tries++;
|
||||
|
||||
proxy.socket = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (proxy.socket < 0) {
|
||||
proxy->proxy.socket = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (proxy->proxy.socket < 0) {
|
||||
error("Unable to create socket");
|
||||
res->status = http_get_status(500);
|
||||
ctx->origin = INTERNAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
server_timeout.tv_sec = SERVER_TIMEOUT_INIT;
|
||||
server_timeout.tv_usec = 0;
|
||||
if (setsockopt(proxy.socket, SOL_SOCKET, SO_RCVTIMEO, &server_timeout, sizeof(server_timeout)) < 0)
|
||||
goto proxy_timeout_err;
|
||||
if (setsockopt(proxy.socket, SOL_SOCKET, SO_SNDTIMEO, &server_timeout, sizeof(server_timeout)) < 0)
|
||||
if (sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT_INIT) != 0)
|
||||
goto proxy_timeout_err;
|
||||
|
||||
struct hostent *host_ent = gethostbyname2(conf->proxy.hostname, AF_INET6);
|
||||
if (host_ent == NULL) {
|
||||
host_ent = gethostbyname2(conf->proxy.hostname, AF_INET);
|
||||
if (host_ent == NULL) {
|
||||
res->status = http_get_status(503);
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to connect to server: Name or service not known");
|
||||
sprintf(err_msg, "Unable to connect to server: Name or service not known.");
|
||||
@@ -236,55 +350,50 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
inet_ntop(address.sin6_family, (void *) &address.sin6_addr, buffer, sizeof(buffer));
|
||||
|
||||
info(BLUE_STR "Connecting to " BLD_STR "[%s]:%i" CLR_STR BLUE_STR "...", buffer, conf->proxy.port);
|
||||
if (connect(proxy.socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
|
||||
if (connect(proxy->proxy.socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
|
||||
if (errno == ETIMEDOUT || errno == EINPROGRESS) {
|
||||
res->status = http_get_status(504);
|
||||
ctx->origin = SERVER_REQ;
|
||||
} else if (errno == ECONNREFUSED) {
|
||||
res->status = http_get_status(503);
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
} else {
|
||||
res->status = http_get_status(500);
|
||||
ctx->origin = INTERNAL;
|
||||
}
|
||||
error("Unable to connect to [%s]:%i: %s", buffer, conf->proxy.port, strerror(errno));
|
||||
sprintf(err_msg, "Unable to connect to server: %s.", strerror(errno));
|
||||
error("Unable to connect to [%s]:%i", buffer, conf->proxy.port);
|
||||
sprintf(err_msg, "Unable to connect to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
goto proxy_err;
|
||||
}
|
||||
|
||||
server_timeout.tv_sec = SERVER_TIMEOUT;
|
||||
server_timeout.tv_usec = 0;
|
||||
if (setsockopt(proxy.socket, SOL_SOCKET, SO_RCVTIMEO, &server_timeout, sizeof(server_timeout)) < 0)
|
||||
goto proxy_timeout_err;
|
||||
if (setsockopt(proxy.socket, SOL_SOCKET, SO_SNDTIMEO, &server_timeout, sizeof(server_timeout)) < 0) {
|
||||
if (sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT) != 0) {
|
||||
proxy_timeout_err:
|
||||
res->status = http_get_status(500);
|
||||
ctx->origin = INTERNAL;
|
||||
error("Unable to set timeout for reverse proxy socket");
|
||||
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", strerror(errno));
|
||||
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
goto proxy_err;
|
||||
}
|
||||
|
||||
if (conf->proxy.enc) {
|
||||
proxy.ssl = SSL_new(proxy.ctx);
|
||||
SSL_set_fd(proxy.ssl, proxy.socket);
|
||||
SSL_set_connect_state(proxy.ssl);
|
||||
proxy->proxy.ssl = SSL_new(proxy_ctx);
|
||||
SSL_set_fd(proxy->proxy.ssl, proxy->proxy.socket);
|
||||
SSL_set_connect_state(proxy->proxy.ssl);
|
||||
|
||||
ret = SSL_do_handshake(proxy.ssl);
|
||||
proxy._last_ret = ret;
|
||||
proxy._errno = errno;
|
||||
proxy._ssl_error = ERR_get_error();
|
||||
proxy.enc = 1;
|
||||
if (ret < 0) {
|
||||
ret = SSL_do_handshake(proxy->proxy.ssl);
|
||||
if (ret != 1) {
|
||||
error_ssl(SSL_get_error(proxy->proxy.ssl, (int) ret));
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to perform handshake: %s", sock_strerror(&proxy));
|
||||
sprintf(err_msg, "Unable to perform handshake: %s.", sock_strerror(&proxy));
|
||||
error("Unable to perform handshake");
|
||||
sprintf(err_msg, "Unable to perform handshake: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
goto proxy_err;
|
||||
}
|
||||
proxy->proxy.enc = 1;
|
||||
}
|
||||
|
||||
proxy_host = conf->name;
|
||||
proxy->initialized = 1;
|
||||
proxy->host = conf->name;
|
||||
info(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i", buffer, conf->proxy.port);
|
||||
|
||||
proxy:
|
||||
@@ -304,19 +413,19 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
http_add_header_field(&req->hdr, "Connection", "keep-alive");
|
||||
}
|
||||
|
||||
ret = proxy_request_header(req, (int) client->enc, cctx);
|
||||
ret = proxy_request_header(req, client);
|
||||
if (ret != 0) {
|
||||
res->status = http_get_status(500);
|
||||
ctx->origin = INTERNAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = http_send_request(&proxy, req);
|
||||
ret = http_send_request(&proxy->proxy, req);
|
||||
if (ret < 0) {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to send request to server (1): %s", sock_strerror(&proxy));
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&proxy));
|
||||
error("Unable to send request to server (1)");
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
retry = tries < 4;
|
||||
goto proxy_err;
|
||||
}
|
||||
@@ -327,24 +436,24 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
|
||||
ret = 0;
|
||||
if (content_len > 0) {
|
||||
ret = sock_splice(&proxy, client, buffer, sizeof(buffer), content_len);
|
||||
ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len);
|
||||
} else if (transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL) {
|
||||
ret = sock_splice_chunked(&proxy, client, buffer, sizeof(buffer));
|
||||
ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
if (ret < 0 || (content_len != 0 && ret != content_len)) {
|
||||
if (ret == -1) {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to send request to server (2): %s", sock_strerror(&proxy));
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&proxy));
|
||||
error("Unable to send request to server (2)");
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
retry = tries < 4;
|
||||
goto proxy_err;
|
||||
} else if (ret == -2) {
|
||||
res->status = http_get_status(400);
|
||||
ctx->origin = CLIENT_REQ;
|
||||
error("Unable to receive request from client: %s", sock_strerror(client));
|
||||
sprintf(err_msg, "Unable to receive request from client: %s.", sock_strerror(client));
|
||||
error("Unable to receive request from client");
|
||||
sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
return -1;
|
||||
}
|
||||
res->status = http_get_status(500);
|
||||
@@ -353,20 +462,18 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sock_recv(&proxy, buffer, sizeof(buffer), MSG_PEEK);
|
||||
ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer), MSG_PEEK);
|
||||
if (ret <= 0) {
|
||||
int enc_err = sock_enc_error(&proxy);
|
||||
if (errno == EAGAIN || errno == EINPROGRESS || enc_err == SSL_ERROR_WANT_READ ||
|
||||
enc_err == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
int e_sys = error_get_sys(), e_ssl = error_get_ssl();
|
||||
if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) {
|
||||
res->status = http_get_status(504);
|
||||
ctx->origin = SERVER_RES;
|
||||
} else {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_RES;
|
||||
}
|
||||
error("Unable to receive response from server: %s", sock_strerror(&proxy));
|
||||
sprintf(err_msg, "Unable to receive response from server: %s.", sock_strerror(&proxy));
|
||||
error("Unable to receive response from server");
|
||||
sprintf(err_msg, "Unable to receive response from server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
retry = tries < 4;
|
||||
goto proxy_err;
|
||||
}
|
||||
@@ -414,7 +521,7 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
res->status = http_get_status(status_code);
|
||||
if (res->status == NULL && status_code >= 100 && status_code <= 999) {
|
||||
custom_status->code = status_code;
|
||||
strcpy(custom_status->type, "");
|
||||
custom_status->type = 0;
|
||||
snprintf(custom_status->msg, sizeof(custom_status->msg), "%.*s",
|
||||
(int) (strchr(ptr, '\r') - ptr - 13), ptr + 13);
|
||||
res->status = custom_status;
|
||||
@@ -426,8 +533,7 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
goto proxy_err;
|
||||
}
|
||||
} else {
|
||||
ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
|
||||
if (ret != 0) {
|
||||
if (http_parse_header_field(&res->hdr, ptr, pos0, 0) != 0) {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_RES;
|
||||
error("Unable to parse header");
|
||||
@@ -440,7 +546,7 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
}
|
||||
ptr = pos0 + 2;
|
||||
}
|
||||
sock_recv(&proxy, buffer, header_len, 0);
|
||||
sock_recv(&proxy->proxy, buffer, header_len, 0);
|
||||
|
||||
ret = proxy_response_header(req, res, conf);
|
||||
if (ret != 0) {
|
||||
@@ -452,11 +558,12 @@ int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t
|
||||
return 0;
|
||||
|
||||
proxy_err:
|
||||
errno = 0;
|
||||
if (retry) goto retry;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags) {
|
||||
char buffer[CHUNK_SIZE], comp_out[CHUNK_SIZE], buf[256], *ptr;
|
||||
long ret = 0, len, snd_len;
|
||||
int finish_comp = 0;
|
||||
@@ -479,12 +586,12 @@ int proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
do {
|
||||
snd_len = 0;
|
||||
if (flags & PROXY_CHUNKED) {
|
||||
ret = sock_get_chunk_header(&proxy);
|
||||
ret = sock_get_chunk_header(&proxy->proxy);
|
||||
if (ret < 0) {
|
||||
if (ret == -1) {
|
||||
error("Unable to receive from server: Malformed chunk header");
|
||||
} else {
|
||||
error("Unable to receive from server: %s", sock_strerror(&proxy));
|
||||
error("Unable to receive from server");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -502,9 +609,9 @@ int proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
}
|
||||
while (snd_len < len_to_send) {
|
||||
unsigned long avail_in, avail_out;
|
||||
ret = sock_recv(&proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0);
|
||||
ret = sock_recv(&proxy->proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to receive from server: %s", sock_strerror(&proxy));
|
||||
error("Unable to receive from server");
|
||||
break;
|
||||
}
|
||||
len = ret;
|
||||
@@ -535,7 +642,7 @@ int proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
if (flags & PROXY_CHUNKED) ret = sock_send(client, "\r\n", 2, 0);
|
||||
if (ret <= 0) {
|
||||
err:
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
error("Unable to send");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -544,7 +651,7 @@ int proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
if (finish_comp) goto finish;
|
||||
}
|
||||
if (ret <= 0) break;
|
||||
if (flags & PROXY_CHUNKED) sock_recv(&proxy, buffer, 2, 0);
|
||||
if (flags & PROXY_CHUNKED) sock_recv(&proxy->proxy, buffer, 2, 0);
|
||||
} while ((flags & PROXY_CHUNKED) && len_to_send > 0);
|
||||
|
||||
if (ret <= 0) return -1;
|
||||
@@ -552,7 +659,7 @@ int proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
if (flags & PROXY_CHUNKED) {
|
||||
ret = sock_send(client, "0\r\n\r\n", 5, 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
error("Unable to send");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -560,8 +667,7 @@ int proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proxy_dump(char *buf, long len) {
|
||||
sock_recv(&proxy, buf, len, 0);
|
||||
sock_close(&proxy);
|
||||
int proxy_dump(proxy_ctx_t *proxy, char *buf, long len) {
|
||||
sock_recv(&proxy->proxy, buf, len, 0);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -20,20 +20,32 @@
|
||||
|
||||
#include "http.h"
|
||||
#include "config.h"
|
||||
#include "../client.h"
|
||||
|
||||
extern sock proxy;
|
||||
typedef struct {
|
||||
unsigned char initialized:1, in_use:1;
|
||||
sock proxy;
|
||||
char *host;
|
||||
void *client;
|
||||
} proxy_ctx_t;
|
||||
|
||||
int proxy_preload(void);
|
||||
|
||||
int proxy_request_header(http_req *req, int enc, client_ctx_t *ctx);
|
||||
void proxy_unload(void);
|
||||
|
||||
void proxy_close_all(void);
|
||||
|
||||
proxy_ctx_t *proxy_get_by_conf(host_config_t *conf);
|
||||
|
||||
void proxy_unlock_ctx(proxy_ctx_t *ctx);
|
||||
|
||||
int proxy_request_header(http_req *req, sock *sock);
|
||||
|
||||
int proxy_response_header(http_req *req, http_res *res, host_config_t *conf);
|
||||
|
||||
int proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, client_ctx_t *cctx, http_status *custom_status, char *err_msg);
|
||||
int proxy_init(proxy_ctx_t **proxy, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg);
|
||||
|
||||
int proxy_send(sock *client, unsigned long len_to_send, int flags);
|
||||
int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags);
|
||||
|
||||
int proxy_dump(char *buf, long len);
|
||||
int proxy_dump(proxy_ctx_t *proxy, char *buf, long len);
|
||||
|
||||
#endif //SESIMOS_PROXY_H
|
||||
|
62
src/lib/res.h
Normal file
62
src/lib/res.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief HTTP resources (header file)
|
||||
* @file src/lib/res.h
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-31
|
||||
*/
|
||||
|
||||
#ifndef SESIMOS_RES_H
|
||||
#define SESIMOS_RES_H
|
||||
|
||||
#define http_default_doc _binary_bin_res_default_txt_start
|
||||
#define http_default_doc_size ((unsigned int) (_binary_bin_res_default_txt_end - _binary_bin_res_default_txt_start) - 1)
|
||||
#define http_proxy_doc _binary_bin_res_proxy_txt_start
|
||||
#define http_proxy_doc_size ((unsigned int) (_binary_bin_res_proxy_txt_end - _binary_bin_res_proxy_txt_start) - 1)
|
||||
#define http_style_doc _binary_bin_res_style_txt_start
|
||||
#define http_style_doc_size ((unsigned int) (_binary_bin_res_style_txt_end - _binary_bin_res_style_txt_start) - 1)
|
||||
|
||||
#define http_icon_error _binary_bin_res_icon_error_txt_start
|
||||
#define http_icon_error_size ((unsigned int) (_binary_bin_res_icon_error_txt_end - _binary_bin_res_icon_error_txt_start) - 1)
|
||||
#define http_icon_info _binary_bin_res_icon_info_txt_start
|
||||
#define http_icon_info_size ((unsigned int) (_binary_bin_res_icon_info_txt_end - _binary_bin_res_icon_info_txt_start) - 1)
|
||||
#define http_icon_success _binary_bin_res_icon_success_txt_start
|
||||
#define http_icon_success_size ((unsigned int) (_binary_bin_res_icon_success_txt_end - _binary_bin_res_icon_success_txt_start) - 1)
|
||||
#define http_icon_warning _binary_bin_res_icon_warning_txt_start
|
||||
#define http_icon_warning_size ((unsigned int) (_binary_bin_res_icon_warning_txt_end - _binary_bin_res_icon_warning_txt_start) - 1)
|
||||
|
||||
#define http_icon_globe _binary_bin_res_globe_txt_start
|
||||
#define http_icon_globe_size ((unsigned int) (_binary_bin_res_globe_txt_end - _binary_bin_res_globe_txt_start) - 1)
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *type;
|
||||
const char *content;
|
||||
const unsigned int size;
|
||||
} res_t;
|
||||
|
||||
extern const char _binary_bin_res_default_txt_start[];
|
||||
extern const char _binary_bin_res_default_txt_end[];
|
||||
|
||||
extern const char _binary_bin_res_proxy_txt_start[];
|
||||
extern const char _binary_bin_res_proxy_txt_end[];
|
||||
|
||||
extern const char _binary_bin_res_style_txt_start[];
|
||||
extern const char _binary_bin_res_style_txt_end[];
|
||||
|
||||
extern const char _binary_bin_res_icon_error_txt_start[];
|
||||
extern const char _binary_bin_res_icon_error_txt_end[];
|
||||
|
||||
extern const char _binary_bin_res_icon_info_txt_start[];
|
||||
extern const char _binary_bin_res_icon_info_txt_end[];
|
||||
|
||||
extern const char _binary_bin_res_icon_success_txt_start[];
|
||||
extern const char _binary_bin_res_icon_success_txt_end[];
|
||||
|
||||
extern const char _binary_bin_res_icon_warning_txt_start[];
|
||||
extern const char _binary_bin_res_icon_warning_txt_end[];
|
||||
|
||||
extern const char _binary_bin_res_globe_txt_start[];
|
||||
extern const char _binary_bin_res_globe_txt_end[];
|
||||
|
||||
#endif //SESIMOS_RES_H
|
133
src/lib/sock.c
133
src/lib/sock.c
@@ -8,66 +8,58 @@
|
||||
|
||||
#include "sock.h"
|
||||
#include "utils.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
|
||||
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros) {
|
||||
struct timeval recv_to = {.tv_sec = recv_micros / 1000000, .tv_usec = recv_micros % 1000000},
|
||||
send_to = {.tv_sec = send_micros / 1000000, .tv_usec = send_micros % 1000000};
|
||||
|
||||
int sock_enc_error(sock *s) {
|
||||
return (int) s->enc ? SSL_get_error(s->ssl, (int) s->_last_ret) : 0;
|
||||
if (setsockopt(s->socket, SOL_SOCKET, SO_RCVTIMEO, &recv_to, sizeof(recv_to)) != 0)
|
||||
return -1;
|
||||
|
||||
if (setsockopt(s->socket, SOL_SOCKET, SO_SNDTIMEO, &send_to, sizeof(send_to)) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *sock_strerror(sock *s) {
|
||||
if (s->_last_ret == 0) {
|
||||
return "closed";
|
||||
} else if (s->enc) {
|
||||
if (s->_last_ret > 0) {
|
||||
return NULL;
|
||||
}
|
||||
const char *err1 = ERR_reason_error_string(s->_ssl_error);
|
||||
const char *err2 = strerror(errno);
|
||||
switch (sock_enc_error(s)) {
|
||||
case SSL_ERROR_NONE:
|
||||
return NULL;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return "closed";
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return "want read";
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return "want write";
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
return "want connect";
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
return "want accept";
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
return "want x509 lookup";
|
||||
case SSL_ERROR_SYSCALL:
|
||||
return ((s->_ssl_error == 0) ? ((s->_last_ret == 0) ? "protocol violation" : err2) : err1);
|
||||
case SSL_ERROR_SSL:
|
||||
return err1;
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
} else {
|
||||
return strerror(s->_errno);
|
||||
}
|
||||
int sock_set_socket_timeout(sock *s, double sec) {
|
||||
return sock_set_socket_timeout_micros(s, (long) (sec * 1000000L), (long) (sec * 1000000L));
|
||||
}
|
||||
|
||||
int sock_set_timeout_micros(sock *s, long micros) {
|
||||
if (micros < 0)
|
||||
return -1;
|
||||
|
||||
s->timeout_us = micros;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sock_set_timeout(sock *s, double sec) {
|
||||
return sock_set_timeout_micros(s, (long) (sec * 1000000));
|
||||
}
|
||||
|
||||
long sock_send(sock *s, void *buf, unsigned long len, int flags) {
|
||||
long ret;
|
||||
if (s->enc) {
|
||||
ret = SSL_write(s->ssl, buf, (int) len);
|
||||
s->_ssl_error = ERR_get_error();
|
||||
if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret));
|
||||
} else {
|
||||
ret = send(s->socket, buf, len, flags);
|
||||
}
|
||||
s->_last_ret = ret;
|
||||
s->_errno = errno;
|
||||
return ret >= 0 ? ret : -1;
|
||||
|
||||
if (ret >= 0) {
|
||||
s->ts_last = clock_micros();
|
||||
return ret;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
|
||||
@@ -75,13 +67,17 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
|
||||
if (s->enc) {
|
||||
int (*func)(SSL*, void*, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read;
|
||||
ret = func(s->ssl, buf, (int) len);
|
||||
s->_ssl_error = ERR_get_error();
|
||||
if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret));
|
||||
} else {
|
||||
ret = recv(s->socket, buf, len, flags);
|
||||
}
|
||||
s->_last_ret = ret;
|
||||
s->_errno = errno;
|
||||
return ret >= 0 ? ret : -1;
|
||||
|
||||
if (ret >= 0) {
|
||||
s->ts_last = clock_micros();
|
||||
return ret;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len) {
|
||||
@@ -121,50 +117,25 @@ long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len)
|
||||
}
|
||||
|
||||
int sock_close(sock *s) {
|
||||
if ((int) s->enc && s->ssl != NULL) {
|
||||
if (s->_last_ret >= 0) SSL_shutdown(s->ssl);
|
||||
int e = errno;
|
||||
if (s->enc && s->ssl != NULL) {
|
||||
SSL_shutdown(s->ssl);
|
||||
SSL_free(s->ssl);
|
||||
s->ssl = NULL;
|
||||
}
|
||||
shutdown(s->socket, SHUT_RDWR);
|
||||
close(s->socket);
|
||||
s->socket = 0;
|
||||
s->enc = 0;
|
||||
errno = e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sock_check(sock *s) {
|
||||
char buf;
|
||||
return recv(s->socket, &buf, 1, MSG_PEEK | MSG_DONTWAIT) == 1;
|
||||
}
|
||||
|
||||
int sock_poll(sock *sockets[], sock *ready[], sock *error[], int n_sock, int *n_ready, int *n_error, short events, int timeout_ms) {
|
||||
struct pollfd fds[n_sock];
|
||||
for (int i = 0; i < n_sock; i++) {
|
||||
fds[i].fd = sockets[i]->socket;
|
||||
fds[i].events = events;
|
||||
}
|
||||
|
||||
int ret = poll(fds, n_sock, timeout_ms);
|
||||
if (ret < 0 || ready == NULL || error == NULL) return ret;
|
||||
|
||||
*n_ready = 0, *n_error = 0;
|
||||
for (int i = 0; i < n_sock; i++) {
|
||||
if (fds[i].revents & events)
|
||||
ready[(*n_ready)++] = sockets[i];
|
||||
if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL))
|
||||
error[(*n_error)++] = sockets[i];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sock_poll_read(sock *sockets[], sock *readable[], sock *error[], int n_sock, int *n_readable, int *n_error, int timeout_ms) {
|
||||
return sock_poll(sockets, readable, error, n_sock, n_readable, n_error, POLLIN, timeout_ms);
|
||||
}
|
||||
|
||||
int sock_poll_write(sock *sockets[], sock *writable[], sock *error[], int n_sock, int *n_writable, int *n_error, int timeout_ms) {
|
||||
return sock_poll(sockets, writable, error, n_sock, n_writable, n_error, POLLOUT, timeout_ms);
|
||||
int sock_has_pending(sock *s) {
|
||||
char buf[1];
|
||||
int e = errno;
|
||||
long ret = sock_recv(s, &buf, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
errno = e;
|
||||
return ret == 1;
|
||||
}
|
||||
|
||||
long sock_parse_chunk_header(const char *buf, long len, long *ret_len) {
|
||||
|
@@ -19,17 +19,20 @@ typedef struct {
|
||||
union {
|
||||
struct sockaddr sock;
|
||||
struct sockaddr_in6 ipv6;
|
||||
} addr;
|
||||
} _addr;
|
||||
char *addr, *s_addr;
|
||||
SSL_CTX *ctx;
|
||||
SSL *ssl;
|
||||
long _last_ret;
|
||||
int _errno;
|
||||
unsigned long _ssl_error;
|
||||
long ts_start, ts_last, timeout_us;
|
||||
} sock;
|
||||
|
||||
int sock_enc_error(sock *s);
|
||||
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros);
|
||||
|
||||
const char *sock_strerror(sock *s);
|
||||
int sock_set_socket_timeout(sock *s, double sec);
|
||||
|
||||
int sock_set_timeout_micros(sock *s, long micros);
|
||||
|
||||
int sock_set_timeout(sock *s, double sec);
|
||||
|
||||
long sock_send(sock *s, void *buf, unsigned long len, int flags);
|
||||
|
||||
@@ -41,13 +44,7 @@ long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len)
|
||||
|
||||
int sock_close(sock *s);
|
||||
|
||||
int sock_check(sock *s);
|
||||
|
||||
int sock_poll(sock *sockets[], sock *ready[], sock *error[], int n_sock, int *n_ready, int *n_error, short events, int timeout_ms);
|
||||
|
||||
int sock_poll_read(sock *sockets[], sock *readable[], sock *error[], int n_sock, int *n_readable, int *n_error, int timeout_ms);
|
||||
|
||||
int sock_poll_write(sock *sockets[], sock *writable[], sock *error[], int n_sock, int *n_writable, int *n_error, int timeout_ms);
|
||||
int sock_has_pending(sock *s);
|
||||
|
||||
long sock_parse_chunk_header(const char *buf, long len, long *ret_len);
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
@@ -25,7 +26,7 @@ char *format_duration(unsigned long micros, char *buf) {
|
||||
} else if (micros < 60000000 - 1000000) {
|
||||
sprintf(buf, "%.1f s", (double) micros / 1000000);
|
||||
} else if (micros < 6000000000) {
|
||||
sprintf(buf, "%.1f min", (double) micros / 1000000 / 60);
|
||||
sprintf(buf, "%li:%02li min", micros / 1000000 / 60, micros / 1000000 % 60);
|
||||
} else {
|
||||
sprintf(buf, "%.0f min", (double) micros / 1000000 / 60);
|
||||
}
|
||||
@@ -203,3 +204,15 @@ int base64_encode(void *data, unsigned long data_len, char *output, unsigned lon
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long clock_micros(void) {
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||
return time.tv_sec * 1000000 + time.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
long clock_cpu(void) {
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time);
|
||||
return time.tv_sec * 1000000000 + time.tv_nsec;
|
||||
}
|
||||
|
@@ -37,4 +37,8 @@ int str_trim_lws(char **start, char **end);
|
||||
|
||||
int base64_encode(void *data, unsigned long data_len, char *output, unsigned long *output_len);
|
||||
|
||||
long clock_micros(void);
|
||||
|
||||
long clock_cpu(void);
|
||||
|
||||
#endif //SESIMOS_UTILS_H
|
||||
|
@@ -6,22 +6,14 @@
|
||||
* @date 2022-08-16
|
||||
*/
|
||||
|
||||
#include "../defs.h"
|
||||
#include "../logger.h"
|
||||
#include "websocket.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
static const char ws_key_uuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
static volatile sig_atomic_t terminate = 0;
|
||||
|
||||
void ws_terminate(int _) {
|
||||
terminate = 1;
|
||||
}
|
||||
|
||||
int ws_calc_accept_key(const char *key, char *accept_key) {
|
||||
if (key == NULL || accept_key == NULL)
|
||||
@@ -144,65 +136,3 @@ int ws_send_frame_header(sock *s, ws_frame *frame) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ws_handle_connection(sock *s1, sock *s2) {
|
||||
sock *poll_socks[2] = {s1, s2};
|
||||
sock *readable[2], *error[2];
|
||||
int n_sock = 2, n_readable = 0, n_error = 0;
|
||||
ws_frame frame;
|
||||
char buf[CHUNK_SIZE];
|
||||
int closes = 0;
|
||||
long ret;
|
||||
|
||||
signal(SIGINT, ws_terminate);
|
||||
signal(SIGTERM, ws_terminate);
|
||||
|
||||
while (!terminate && closes != 3) {
|
||||
ret = sock_poll_read(poll_socks, readable, error, n_sock, &n_readable, &n_error, WS_TIMEOUT * 1000);
|
||||
if (terminate) {
|
||||
break;
|
||||
} else if (ret < 0) {
|
||||
error("Unable to poll sockets");
|
||||
return -1;
|
||||
} else if (n_readable == 0) {
|
||||
error("Connection timed out");
|
||||
return -2;
|
||||
} else if (n_error > 0) {
|
||||
error("Peer closed connection");
|
||||
return -3;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_readable; i++) {
|
||||
sock *s = readable[i];
|
||||
sock *o = (s == s1) ? s2 : s1;
|
||||
if (ws_recv_frame_header(s, &frame) != 0) return -3;
|
||||
|
||||
// debug("WebSocket: Peer %s, Opcode=0x%X, Len=%li", (s == s1) ? "1" : "2", frame.opcode, frame.len);
|
||||
|
||||
if (frame.opcode == 0x8) {
|
||||
n_sock--;
|
||||
if (s == s1) {
|
||||
poll_socks[0] = s2;
|
||||
closes |= 1;
|
||||
} else {
|
||||
closes |= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (ws_send_frame_header(o, &frame) != 0) return -3;
|
||||
|
||||
if (frame.len > 0) {
|
||||
ret = sock_splice(o, s, buf, sizeof(buf), frame.len);
|
||||
if (ret < 0) {
|
||||
error("Unable to forward data in WebSocket");
|
||||
return -4;
|
||||
} else if (ret != frame.len) {
|
||||
error("Unable to forward correct number of bytes in WebSocket");
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -30,6 +30,4 @@ int ws_recv_frame_header(sock *s, ws_frame *frame);
|
||||
|
||||
int ws_send_frame_header(sock *s, ws_frame *frame);
|
||||
|
||||
int ws_handle_connection(sock *s1, sock *s2);
|
||||
|
||||
#endif //SESIMOS_WEBSOCKET_H
|
||||
|
35
src/logger.c
35
src/logger.c
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "logger.h"
|
||||
#include "lib/utils.h"
|
||||
#include "lib/error.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
@@ -59,8 +60,8 @@ static const char *level_keywords[] = {
|
||||
|
||||
static void err(const char *restrict msg) {
|
||||
char err_buf[64];
|
||||
strerror_r(errno, err_buf, sizeof(err_buf));
|
||||
fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", "logger", level_keywords[LOG_CRITICAL], msg, err_buf);
|
||||
fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", "logger",
|
||||
level_keywords[LOG_CRITICAL], msg, error_str(errno, err_buf, sizeof(err_buf)));
|
||||
}
|
||||
|
||||
void logmsgf(log_lvl_t level, const char *restrict format, ...) {
|
||||
@@ -71,8 +72,7 @@ void logmsgf(log_lvl_t level, const char *restrict format, ...) {
|
||||
|
||||
const char *color = (level <= LOG_ERROR) ? ERR_STR : ((level <= LOG_WARNING) ? WRN_STR : "");
|
||||
if (errno != 0) {
|
||||
strerror_r(errno, err_buf, sizeof(err_buf));
|
||||
snprintf(buf, sizeof(buf), "%s%s: %s" CLR_STR, color, format, err_buf);
|
||||
snprintf(buf, sizeof(buf), "%s%s: %s" CLR_STR, color, format, error_str(errno, err_buf, sizeof(err_buf)));
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s%s" CLR_STR, color, format);
|
||||
}
|
||||
@@ -162,10 +162,13 @@ static int logger_remaining(void) {
|
||||
return val;
|
||||
}
|
||||
|
||||
void logger_set_name(const char *restrict name) {
|
||||
void logger_set_name(const char *restrict format, ...) {
|
||||
va_list args;
|
||||
|
||||
if (key_name == -1) {
|
||||
// not initialized
|
||||
strncpy(global_name, name, sizeof(global_name));
|
||||
va_start(args, format);
|
||||
vsnprintf(global_name, sizeof(global_name), format, args);
|
||||
} else {
|
||||
int ret;
|
||||
void *ptr = pthread_getspecific(key_name);
|
||||
@@ -177,14 +180,22 @@ void logger_set_name(const char *restrict name) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
strncpy(ptr, name, LOG_NAME_LEN);
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(ptr, LOG_NAME_LEN, format, args);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void logger_set_prefix(const char *restrict prefix) {
|
||||
void logger_set_prefix(const char *restrict format, ...) {
|
||||
va_list args;
|
||||
|
||||
if (key_prefix == -1) {
|
||||
// not initialized
|
||||
strncpy(global_prefix, prefix, sizeof(global_prefix));
|
||||
va_start(args, format);
|
||||
vsnprintf(global_prefix, sizeof(global_prefix), format, args);
|
||||
} else {
|
||||
int ret;
|
||||
void *ptr = pthread_getspecific(key_prefix);
|
||||
@@ -196,8 +207,12 @@ void logger_set_prefix(const char *restrict prefix) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
strncpy(ptr, prefix, LOG_PREFIX_LEN);
|
||||
va_start(args, format);
|
||||
vsnprintf(ptr, LOG_PREFIX_LEN, format, args);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void *logger_thread(void *arg) {
|
||||
|
@@ -29,9 +29,9 @@ typedef unsigned char log_lvl_t;
|
||||
|
||||
void logmsgf(log_lvl_t level, const char *restrict format, ...);
|
||||
|
||||
void logger_set_name(const char *restrict name);
|
||||
void logger_set_name(const char *restrict format, ...);
|
||||
|
||||
void logger_set_prefix(const char *restrict prefix);
|
||||
void logger_set_prefix(const char *restrict format, ...);
|
||||
|
||||
int logger_init(void);
|
||||
|
||||
|
125
src/server.c
125
src/server.c
@@ -8,26 +8,24 @@
|
||||
|
||||
#include "defs.h"
|
||||
#include "server.h"
|
||||
#include "client.h"
|
||||
#include "logger.h"
|
||||
#include "async.h"
|
||||
#include "worker/tcp_acceptor.h"
|
||||
|
||||
#include "cache_handler.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/proxy.h"
|
||||
#include "lib/geoip.h"
|
||||
#include "worker/tcp_closer.h"
|
||||
#include "worker/request_handler.h"
|
||||
#include "workers.h"
|
||||
#include "worker/func.h"
|
||||
#include "lib/list.h"
|
||||
#include "lib/utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/socket.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <openssl/err.h>
|
||||
@@ -36,13 +34,14 @@
|
||||
#include <openssl/conf.h>
|
||||
|
||||
|
||||
volatile sig_atomic_t server_alive = 1;
|
||||
const char *config_file;
|
||||
|
||||
static int sockets[NUM_SOCKETS];
|
||||
static SSL_CTX *contexts[CONFIG_MAX_CERT_CONFIG];
|
||||
|
||||
static client_ctx_t clients[MAX_CLIENTS]; // TODO dynamic
|
||||
static client_ctx_t **clients;
|
||||
|
||||
static const char *color_table[] = {"\x1B[31m", "\x1B[32m", "\x1B[33m", "\x1B[34m", "\x1B[35m", "\x1B[36m"};
|
||||
|
||||
static int clean() {
|
||||
remove("/var/sesimos/server/cache");
|
||||
@@ -59,21 +58,40 @@ static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) {
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
void server_free_client(client_ctx_t *ctx) {
|
||||
for (int i = 0; i < list_size(clients); i++) {
|
||||
if (clients[i] == ctx) {
|
||||
clients = list_remove(clients, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void accept_cb(void *arg) {
|
||||
int i = (int) (((int *) arg) - sockets);
|
||||
int fd = sockets[i];
|
||||
|
||||
int j;
|
||||
for (j = 0; j < MAX_CLIENTS; j++) {
|
||||
if (clients[j].in_use == 0) break;
|
||||
client_ctx_t *client_ctx = malloc(sizeof(client_ctx_t));
|
||||
if (client_ctx == NULL) {
|
||||
critical("Unable to allocate memory for client context");
|
||||
errno = 0;
|
||||
return;
|
||||
}
|
||||
client_ctx_t *client_ctx = &clients[j];
|
||||
|
||||
clients = list_append(clients, &client_ctx);
|
||||
if (clients == NULL) {
|
||||
critical("Unable to add client context to list");
|
||||
errno = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
client_ctx->in_use = 1;
|
||||
sock *client = &client_ctx->socket;
|
||||
|
||||
client->ctx = contexts[0];
|
||||
socklen_t addr_len = sizeof(client->addr);
|
||||
int client_fd = accept(fd, &client->addr.sock, &addr_len);
|
||||
socklen_t addr_len = sizeof(client->_addr);
|
||||
int client_fd = accept(fd, &client->_addr.sock, &addr_len);
|
||||
if (client_fd < 0) {
|
||||
critical("Unable to accept connection");
|
||||
return;
|
||||
@@ -81,6 +99,10 @@ static void accept_cb(void *arg) {
|
||||
|
||||
client->socket = client_fd;
|
||||
client->enc = (i == 1);
|
||||
client->ts_start = clock_micros();
|
||||
client->ts_last = client->ts_start;
|
||||
client_ctx->cnx_s = client->ts_start;
|
||||
client_ctx->cnx_e = -1, client_ctx->req_s = -1, client_ctx->req_e = -1, client_ctx->res_ts = -1;
|
||||
|
||||
tcp_accept(client_ctx);
|
||||
}
|
||||
@@ -105,27 +127,26 @@ static void terminate_gracefully(int sig) {
|
||||
fprintf(stderr, "\n");
|
||||
notice("Terminating gracefully...");
|
||||
|
||||
server_alive = 0;
|
||||
signal(SIGINT, terminate_forcefully);
|
||||
signal(SIGTERM, terminate_forcefully);
|
||||
|
||||
tcp_acceptor_stop();
|
||||
request_handler_stop();
|
||||
tcp_closer_stop();
|
||||
|
||||
tcp_acceptor_destroy();
|
||||
request_handler_destroy();
|
||||
tcp_closer_destroy();
|
||||
|
||||
struct sigaction act = {0};
|
||||
act.sa_handler = terminate_forcefully;
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
sigaction(SIGTERM, &act, NULL);
|
||||
|
||||
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||
shutdown(sockets[i], SHUT_RDWR);
|
||||
close(sockets[i]);
|
||||
}
|
||||
|
||||
notice("Goodbye");
|
||||
geoip_free();
|
||||
exit(0);
|
||||
cache_stop();
|
||||
workers_stop();
|
||||
workers_destroy();
|
||||
|
||||
for (int i = 0; i < list_size(clients); i++) {
|
||||
tcp_close(clients[i]);
|
||||
}
|
||||
proxy_close_all();
|
||||
logger_set_prefix("");
|
||||
|
||||
async_stop();
|
||||
}
|
||||
|
||||
static void nothing(int sig) {}
|
||||
@@ -135,7 +156,11 @@ int main(int argc, char *const argv[]) {
|
||||
int ret;
|
||||
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
memset(clients, 0, sizeof(clients));
|
||||
clients = list_create(sizeof(void *), 64);
|
||||
if (clients == NULL) {
|
||||
critical("Unable to initialize client list");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct sockaddr_in6 addresses[2] = {
|
||||
{.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(80)},
|
||||
@@ -150,7 +175,7 @@ int main(int argc, char *const argv[]) {
|
||||
critical("Unable to set stdout/stderr to line-buffered mode");
|
||||
return 1;
|
||||
}
|
||||
printf("Sesimos web server " SERVER_VERSION "\n");
|
||||
printf("sesimos web server " SERVER_VERSION "\n");
|
||||
|
||||
static const struct option long_opts[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
@@ -209,9 +234,13 @@ int main(int argc, char *const argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
signal(SIGINT, terminate_gracefully);
|
||||
signal(SIGTERM, terminate_gracefully);
|
||||
signal(SIGUSR1, nothing);
|
||||
struct sigaction act = {0};
|
||||
act.sa_handler = terminate_gracefully;
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
sigaction(SIGTERM, &act, NULL);
|
||||
act.sa_handler = nothing;
|
||||
sigaction(SIGUSR1, &act, NULL);
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
if ((ret = geoip_init(config.geoip_dir)) != 0) {
|
||||
if (ret == -1) {
|
||||
@@ -252,29 +281,45 @@ int main(int argc, char *const argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
proxy_preload();
|
||||
if (async_init() != 0) {
|
||||
critical("Unable to initialize async thread");
|
||||
geoip_free();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (proxy_preload() != 0) {
|
||||
critical("Unable to initialize proxy");
|
||||
geoip_free();
|
||||
async_free();
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||
if (listen(sockets[i], LISTEN_BACKLOG) < 0) {
|
||||
critical("Unable to listen on socket %i", i);
|
||||
geoip_free();
|
||||
proxy_unload();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
tcp_acceptor_init(CNX_HANDLER_WORKERS, 64);
|
||||
tcp_closer_init(CNX_HANDLER_WORKERS, 64);
|
||||
request_handler_init(REQ_HANDLER_WORKERS, 64);
|
||||
workers_init();
|
||||
|
||||
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||
async(sockets[i], POLLIN, ASYNC_KEEP, accept_cb, &sockets[i], accept_err_cb, &sockets[i]);
|
||||
async_fd(sockets[i], ASYNC_WAIT_READ, ASYNC_KEEP, &sockets[i], accept_cb, accept_err_cb, accept_err_cb);
|
||||
}
|
||||
|
||||
notice("Ready to accept connections");
|
||||
|
||||
async_thread();
|
||||
|
||||
notice("Goodbye!");
|
||||
|
||||
// cleanup
|
||||
list_free(clients);
|
||||
geoip_free();
|
||||
proxy_unload();
|
||||
cache_join();
|
||||
async_free();
|
||||
return 0;
|
||||
}
|
||||
|
@@ -9,9 +9,7 @@
|
||||
#ifndef SESIMOS_SERVER_H
|
||||
#define SESIMOS_SERVER_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <maxminddb.h>
|
||||
#include <signal.h>
|
||||
#include "worker/func.h"
|
||||
|
||||
#define NUM_SOCKETS 2
|
||||
#define LISTEN_BACKLOG 16
|
||||
@@ -19,11 +17,10 @@
|
||||
#define CLIENT_TIMEOUT 3600
|
||||
#define SERVER_TIMEOUT_INIT 4
|
||||
#define SERVER_TIMEOUT 3600
|
||||
#define MAX_CLIENTS 4096
|
||||
|
||||
#define CNX_HANDLER_WORKERS 8
|
||||
#define REQ_HANDLER_WORKERS 16
|
||||
|
||||
extern volatile sig_atomic_t server_alive;
|
||||
void server_free_client(client_ctx_t *ctx);
|
||||
|
||||
#endif //SESIMOS_SERVER_H
|
||||
|
175
src/worker/fastcgi_handler.c
Normal file
175
src/worker/fastcgi_handler.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief FastCGI handler
|
||||
* @file src/worker/fastcgi_handler.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-28
|
||||
*/
|
||||
|
||||
#include "func.h"
|
||||
#include "../logger.h"
|
||||
#include "../lib/utils.h"
|
||||
#include "../lib/compress.h"
|
||||
#include "../workers.h"
|
||||
#include "../lib/fastcgi.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx);
|
||||
static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx);
|
||||
|
||||
void fastcgi_handler_func(client_ctx_t *ctx) {
|
||||
logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix);
|
||||
|
||||
fastcgi_cnx_t fcgi_cnx;
|
||||
int ret = fastcgi_handler_1(ctx, &fcgi_cnx);
|
||||
respond(ctx);
|
||||
if (ret == 0) fastcgi_handler_2(ctx, &fcgi_cnx);
|
||||
request_complete(ctx);
|
||||
|
||||
handle_request(ctx);
|
||||
}
|
||||
|
||||
static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) {
|
||||
http_res *res = &ctx->res;
|
||||
http_req *req = &ctx->req;
|
||||
http_uri *uri = &ctx->uri;
|
||||
sock *client = &ctx->socket;
|
||||
char *err_msg = ctx->err_msg;
|
||||
|
||||
fcgi_cnx->socket = 0;
|
||||
fcgi_cnx->req_id = 0;
|
||||
fcgi_cnx->r_addr = ctx->socket.addr;
|
||||
fcgi_cnx->r_host = (ctx->host[0] != 0) ? ctx->host : NULL;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
int mode, ret;
|
||||
if (strcmp(uri->filename + strlen(uri->filename) - 4, ".ncr") == 0) {
|
||||
mode = FASTCGI_SESIMOS;
|
||||
} else if (strcmp(uri->filename + strlen(uri->filename) - 4, ".php") == 0) {
|
||||
mode = FASTCGI_PHP;
|
||||
} else {
|
||||
res->status = http_get_status(500);
|
||||
error("Invalid FastCGI extension: %s", uri->filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
stat(uri->filename, &statbuf);
|
||||
char *last_modified = http_format_date(statbuf.st_mtime, buf, sizeof(buf));
|
||||
http_add_header_field(&res->hdr, "Last-Modified", last_modified);
|
||||
|
||||
res->status = http_get_status(200);
|
||||
if (fastcgi_init(fcgi_cnx, mode, ctx->req_num, client, req, uri) != 0) {
|
||||
res->status = http_get_status(503);
|
||||
sprintf(err_msg, "Unable to communicate with FastCGI socket.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const char *client_content_length = http_get_header_field(&req->hdr, "Content-Length");
|
||||
const char *client_transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding");
|
||||
if (client_content_length != NULL) {
|
||||
unsigned long client_content_len = strtoul(client_content_length, NULL, 10);
|
||||
ret = fastcgi_receive(fcgi_cnx, client, client_content_len);
|
||||
} else if (client_transfer_encoding != NULL && strstr(client_transfer_encoding, "chunked") != NULL) {
|
||||
ret = fastcgi_receive_chunked(fcgi_cnx, client);
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
if (ret != 0) {
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
sprintf(err_msg, "Unable to communicate with FastCGI socket.");
|
||||
}
|
||||
res->status = http_get_status(502);
|
||||
return 2;
|
||||
}
|
||||
fastcgi_close_stdin(fcgi_cnx);
|
||||
|
||||
ret = fastcgi_header(fcgi_cnx, res, err_msg);
|
||||
if (ret != 0) {
|
||||
return (ret < 0) ? -1 : 1;
|
||||
}
|
||||
|
||||
const char *status_hdr = http_get_header_field(&res->hdr, "Status");
|
||||
if (status_hdr != NULL) {
|
||||
int status_code = (int) strtoul(status_hdr, NULL, 10);
|
||||
res->status = http_get_status(status_code);
|
||||
http_remove_header_field(&res->hdr, "Status", HTTP_REMOVE_ALL);
|
||||
if (res->status == NULL && status_code >= 100 && status_code <= 999) {
|
||||
ctx->custom_status.code = status_code;
|
||||
ctx->custom_status.type = 0;
|
||||
strcpy(ctx->custom_status.msg, status_hdr + 4);
|
||||
res->status = &ctx->custom_status;
|
||||
} else if (res->status == NULL) {
|
||||
res->status = http_get_status(500);
|
||||
sprintf(err_msg, "The status_hdr code was set to an invalid or unknown value.");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const char *content_length_f = http_get_header_field(&res->hdr, "Content-Length");
|
||||
ctx->content_length = (content_length_f == NULL) ? -1 : strtol(content_length_f, NULL, 10);
|
||||
|
||||
const char *content_type = http_get_header_field(&res->hdr, "Content-Type");
|
||||
const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||
if (content_encoding == NULL &&
|
||||
content_type != NULL &&
|
||||
strncmp(content_type, "text/html", 9) == 0 &&
|
||||
ctx->content_length != -1 &&
|
||||
ctx->content_length <= sizeof(ctx->msg_content) - 1)
|
||||
{
|
||||
fastcgi_dump(fcgi_cnx, ctx->msg_content, sizeof(ctx->msg_content));
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx->use_fastcgi = 1;
|
||||
|
||||
if (ctx->content_length != -1 && ctx->content_length < 1024000) {
|
||||
ctx->use_fastcgi |= FASTCGI_COMPRESS_HOLD;
|
||||
}
|
||||
|
||||
ctx->content_length = -1;
|
||||
|
||||
int http_comp = http_get_compression(req, res);
|
||||
if (http_comp & COMPRESS) {
|
||||
if (http_comp & COMPRESS_BR) {
|
||||
ctx->use_fastcgi |= FASTCGI_COMPRESS_BR;
|
||||
sprintf(buf, "br");
|
||||
} else if (http_comp & COMPRESS_GZ) {
|
||||
ctx->use_fastcgi |= FASTCGI_COMPRESS_GZ;
|
||||
sprintf(buf, "gzip");
|
||||
}
|
||||
http_add_header_field(&res->hdr, "Vary", "Accept-Encoding");
|
||||
http_add_header_field(&res->hdr, "Content-Encoding", buf);
|
||||
http_remove_header_field(&res->hdr, "Content-Length", HTTP_REMOVE_ALL);
|
||||
}
|
||||
|
||||
if (http_get_header_field(&res->hdr, "Content-Length") == NULL) {
|
||||
http_add_header_field(&res->hdr, "Transfer-Encoding", "chunked");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) {
|
||||
const char *transfer_encoding = http_get_header_field(&ctx->res.hdr, "Transfer-Encoding");
|
||||
int chunked = (transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL);
|
||||
|
||||
int flags = (chunked ? FASTCGI_CHUNKED : 0) | (ctx->use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD));
|
||||
int ret = fastcgi_send(fcgi_cnx, &ctx->socket, flags);
|
||||
if (ret < 0) {
|
||||
ctx->c_keep_alive = 0;
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
if (fcgi_cnx->socket != 0) {
|
||||
close(fcgi_cnx->socket);
|
||||
fcgi_cnx->socket = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
71
src/worker/func.h
Normal file
71
src/worker/func.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Worker function header file
|
||||
* @file src/worker/func.h
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-29
|
||||
*/
|
||||
|
||||
#ifndef SESIMOS_FUNC_H
|
||||
#define SESIMOS_FUNC_H
|
||||
|
||||
#include "../lib/sock.h"
|
||||
#include "../lib/http.h"
|
||||
#include "../lib/uri.h"
|
||||
#include "../lib/config.h"
|
||||
#include "../lib/proxy.h"
|
||||
|
||||
typedef struct {
|
||||
sock socket;
|
||||
int req_num;
|
||||
unsigned char in_use: 1, s_keep_alive:1, c_keep_alive:1, use_fastcgi:4, use_proxy:2, ws_close:2;
|
||||
char cc[3], host[256];
|
||||
char req_host[256], err_msg[256];
|
||||
char log_prefix[128];
|
||||
char _c_addr[INET6_ADDRSTRLEN + 1], _s_addr[INET6_ADDRSTRLEN + 1];
|
||||
long cnx_s, cnx_e, req_s, res_ts, req_e;
|
||||
http_req req;
|
||||
http_res res;
|
||||
http_uri uri;
|
||||
http_status_ctx status;
|
||||
http_status custom_status;
|
||||
host_config_t *conf;
|
||||
FILE *file;
|
||||
long content_length;
|
||||
char *msg_buf, *msg_buf_ptr, msg_content[1024];
|
||||
proxy_ctx_t *proxy;
|
||||
} client_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
client_ctx_t *client;
|
||||
sock *socket;
|
||||
void *other;
|
||||
} ws_ctx_t;
|
||||
|
||||
void tcp_acceptor_func(client_ctx_t *ctx);
|
||||
|
||||
void request_handler_func(client_ctx_t *ctx);
|
||||
|
||||
void local_handler_func(client_ctx_t *ctx);
|
||||
|
||||
void fastcgi_handler_func(client_ctx_t *ctx);
|
||||
|
||||
void proxy_handler_func(client_ctx_t *ctx);
|
||||
|
||||
void ws_frame_handler_func(ws_ctx_t *ctx);
|
||||
|
||||
int respond(client_ctx_t *ctx);
|
||||
|
||||
void request_complete(client_ctx_t *ctx);
|
||||
|
||||
void timeout_request(client_ctx_t *ctx);
|
||||
|
||||
void tcp_close(client_ctx_t *ctx);
|
||||
|
||||
void proxy_close(proxy_ctx_t *ctx);
|
||||
|
||||
int ws_handle_connection(client_ctx_t *ctx);
|
||||
|
||||
void ws_close(ws_ctx_t *ctx);
|
||||
|
||||
#endif //SESIMOS_FUNC_H
|
217
src/worker/local_handler.c
Normal file
217
src/worker/local_handler.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Local filesystem handler
|
||||
* @file src/worker/local_handler.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-29
|
||||
*/
|
||||
|
||||
#include "func.h"
|
||||
#include "../logger.h"
|
||||
#include "../lib/utils.h"
|
||||
#include "../lib/compress.h"
|
||||
#include "../workers.h"
|
||||
#include "../lib/list.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int local_handler(client_ctx_t *ctx);
|
||||
|
||||
void local_handler_func(client_ctx_t *ctx) {
|
||||
logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix);
|
||||
|
||||
switch (local_handler(ctx)) {
|
||||
case 0:
|
||||
respond(ctx);
|
||||
request_complete(ctx);
|
||||
handle_request(ctx);
|
||||
break;
|
||||
case 1:
|
||||
fastcgi_handle(ctx);
|
||||
break;
|
||||
default:
|
||||
tcp_close(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int local_handler(client_ctx_t *ctx) {
|
||||
http_res *res = &ctx->res;
|
||||
http_req *req = &ctx->req;
|
||||
http_uri *uri = &ctx->uri;
|
||||
char *err_msg = ctx->err_msg;
|
||||
|
||||
char buf1[1024], buf2[1024];
|
||||
int accept_if_modified_since = 0;
|
||||
|
||||
if (strcmp(req->method, "TRACE") == 0) {
|
||||
res->status = http_get_status(200);
|
||||
http_add_header_field(&res->hdr, "Content-Type", "message/http");
|
||||
|
||||
ctx->msg_buf_ptr = malloc(4096);
|
||||
ctx->msg_buf = ctx->msg_buf_ptr;
|
||||
ctx->content_length = snprintf(ctx->msg_buf, 4096 - ctx->content_length, "%s %s HTTP/%s\r\n", req->method, req->uri, req->version);
|
||||
for (int i = 0; i < list_size(&req->hdr); i++) {
|
||||
const http_field *f = &req->hdr.fields[i];
|
||||
ctx->content_length += snprintf(ctx->msg_buf + ctx->content_length, 4096 - ctx->content_length, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strncmp(uri->req_path, "/.well-known/", 13) == 0) {
|
||||
http_add_header_field(&res->hdr, "Access-Control-Allow-Origin", "*");
|
||||
}
|
||||
|
||||
if (strncmp(uri->req_path, "/.well-known/", 13) != 0 && strstr(uri->path, "/.") != NULL) {
|
||||
res->status = http_get_status(403);
|
||||
sprintf(err_msg, "Parts of this URI are hidden.");
|
||||
return 0;
|
||||
} else if (uri->filename == NULL && (int) uri->is_static && (int) uri->is_dir && strlen(uri->pathinfo) == 0) {
|
||||
res->status = http_get_status(403);
|
||||
sprintf(err_msg, "It is not allowed to list the contents of this directory.");
|
||||
return 0;
|
||||
} else if (uri->filename == NULL && (int) !uri->is_static && (int) uri->is_dir && strlen(uri->pathinfo) == 0) {
|
||||
// TODO list directory contents
|
||||
res->status = http_get_status(501);
|
||||
sprintf(err_msg, "Listing contents of an directory is currently not implemented.");
|
||||
return 0;
|
||||
} else if (uri->filename == NULL || (strlen(uri->pathinfo) > 0 && (int) uri->is_static)) {
|
||||
res->status = http_get_status(404);
|
||||
return 0;
|
||||
} else if (strlen(uri->pathinfo) != 0 && ctx->conf->local.dir_mode != URI_DIR_MODE_INFO) {
|
||||
res->status = http_get_status(404);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (uri->is_static) {
|
||||
res->status = http_get_status(200);
|
||||
http_add_header_field(&res->hdr, "Accept-Ranges", "bytes");
|
||||
if (strcmp(req->method, "GET") != 0 && strcmp(req->method, "HEAD") != 0) {
|
||||
res->status = http_get_status(405);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (http_get_header_field(&req->hdr, "Content-Length") != NULL || http_get_header_field(&req->hdr, "Transfer-Encoding") != NULL) {
|
||||
res->status = http_get_status(400);
|
||||
sprintf(err_msg, "A GET request must not contain a payload");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache_init_uri(ctx->conf->cache, uri);
|
||||
|
||||
const char *last_modified = http_format_date(uri->meta->stat.st_mtime, buf1, sizeof(buf1));
|
||||
http_add_header_field(&res->hdr, "Last-Modified", last_modified);
|
||||
sprintf(buf2, "%s; charset=%s", uri->meta->type, uri->meta->charset);
|
||||
http_add_header_field(&res->hdr, "Content-Type", buf2);
|
||||
|
||||
|
||||
const char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding");
|
||||
int enc = 0;
|
||||
if (accept_encoding != NULL) {
|
||||
if (uri->meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != NULL) {
|
||||
ctx->file = fopen(uri->meta->filename_comp_br, "rb");
|
||||
if (ctx->file == NULL) {
|
||||
cache_mark_dirty(ctx->conf->cache, uri->filename);
|
||||
errno = 0;
|
||||
} else {
|
||||
http_add_header_field(&res->hdr, "Content-Encoding", "br");
|
||||
enc = COMPRESS_BR;
|
||||
}
|
||||
} else if (uri->meta->filename_comp_gz[0] != 0 && strstr(accept_encoding, "gzip") != NULL) {
|
||||
ctx->file = fopen(uri->meta->filename_comp_gz, "rb");
|
||||
if (ctx->file == NULL) {
|
||||
cache_mark_dirty(ctx->conf->cache, uri->filename);
|
||||
errno = 0;
|
||||
} else {
|
||||
http_add_header_field(&res->hdr, "Content-Encoding", "gzip");
|
||||
enc = COMPRESS_GZ;
|
||||
}
|
||||
}
|
||||
if (enc != 0) {
|
||||
http_add_header_field(&res->hdr, "Vary", "Accept-Encoding");
|
||||
}
|
||||
}
|
||||
|
||||
if (uri->meta->etag[0] != 0) {
|
||||
if (enc) {
|
||||
sprintf(buf1, "%s-%s", uri->meta->etag, (enc & COMPRESS_BR) ? "br" : (enc & COMPRESS_GZ) ? "gzip" : "");
|
||||
http_add_header_field(&res->hdr, "ETag", buf1);
|
||||
} else {
|
||||
http_add_header_field(&res->hdr, "ETag", uri->meta->etag);
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(uri->meta->type, "text/", 5) == 0) {
|
||||
http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=3600");
|
||||
} else {
|
||||
http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=86400");
|
||||
}
|
||||
|
||||
const char *if_modified_since = http_get_header_field(&req->hdr, "If-Modified-Since");
|
||||
const char *if_none_match = http_get_header_field(&req->hdr, "If-None-Match");
|
||||
if ((if_none_match != NULL && strstr(if_none_match, uri->meta->etag) == NULL) ||
|
||||
(accept_if_modified_since && if_modified_since != NULL && strcmp(if_modified_since, last_modified) == 0))
|
||||
{
|
||||
res->status = http_get_status(304);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *range = http_get_header_field(&req->hdr, "Range");
|
||||
if (range != NULL) {
|
||||
if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) {
|
||||
res->status = http_get_status(416);
|
||||
http_remove_header_field(&res->hdr, "Content-Type", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Last-Modified", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "ETag", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Cache-Control", HTTP_REMOVE_ALL);
|
||||
return 0;
|
||||
}
|
||||
range += 6;
|
||||
char *ptr = strchr(range, '-');
|
||||
if (ptr == NULL) {
|
||||
res->status = http_get_status(416);
|
||||
return 0;
|
||||
}
|
||||
ctx->file = fopen(uri->filename, "rb");
|
||||
fseek(ctx->file, 0, SEEK_END);
|
||||
unsigned long file_len = ftell(ctx->file);
|
||||
fseek(ctx->file, 0, SEEK_SET);
|
||||
if (file_len == 0) {
|
||||
ctx->content_length = 0;
|
||||
return 0;
|
||||
}
|
||||
long num1 = 0;
|
||||
long num2 = (long) file_len - 1;
|
||||
|
||||
if (ptr != range) num1 = (long) strtoul(range, NULL, 10);
|
||||
if (ptr[1] != 0) num2 = (long) strtoul(ptr + 1, NULL, 10);
|
||||
|
||||
if (num1 >= file_len || num2 >= file_len || num1 > num2) {
|
||||
res->status = http_get_status(416);
|
||||
return 0;
|
||||
}
|
||||
sprintf(buf1, "bytes %li-%li/%li", num1, num2, file_len);
|
||||
http_add_header_field(&res->hdr, "Content-Range", buf1);
|
||||
|
||||
res->status = http_get_status(206);
|
||||
fseek(ctx->file, num1, SEEK_SET);
|
||||
ctx->content_length = num2 - num1 + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx->file == NULL) {
|
||||
ctx->file = fopen(uri->filename, "rb");
|
||||
}
|
||||
|
||||
fseek(ctx->file, 0, SEEK_END);
|
||||
ctx->content_length = ftell(ctx->file);
|
||||
fseek(ctx->file, 0, SEEK_SET);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
170
src/worker/proxy_handler.c
Normal file
170
src/worker/proxy_handler.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Proxy handler
|
||||
* @file src/worker/proxy_handler_1.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-29
|
||||
*/
|
||||
|
||||
#include "func.h"
|
||||
#include "../logger.h"
|
||||
#include "../lib/utils.h"
|
||||
#include "../lib/proxy.h"
|
||||
#include "../lib/websocket.h"
|
||||
#include "../workers.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int proxy_handler_1(client_ctx_t *ctx);
|
||||
static int proxy_handler_2(client_ctx_t *ctx);
|
||||
|
||||
void proxy_handler_func(client_ctx_t *ctx) {
|
||||
logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix);
|
||||
|
||||
// TODO handle 1xx responses
|
||||
|
||||
int ret = proxy_handler_1(ctx);
|
||||
respond(ctx);
|
||||
|
||||
if (ret == 1) {
|
||||
proxy_unlock_ctx(ctx->proxy);
|
||||
ctx->proxy = NULL;
|
||||
} else if (ctx->use_proxy == 0) {
|
||||
proxy_close(ctx->proxy);
|
||||
} else if (ctx->use_proxy == 1) {
|
||||
proxy_handler_2(ctx);
|
||||
proxy_unlock_ctx(ctx->proxy);
|
||||
ctx->proxy = NULL;
|
||||
} else if (ctx->use_proxy == 2) {
|
||||
// WebSocket
|
||||
ws_handle_connection(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
request_complete(ctx);
|
||||
handle_request(ctx);
|
||||
}
|
||||
|
||||
static int proxy_handler_1(client_ctx_t *ctx) {
|
||||
http_res *res = &ctx->res;
|
||||
http_status_ctx *status = &ctx->status;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
info("Reverse proxy for " BLD_STR "%s:%i" CLR_STR, ctx->conf->proxy.hostname, ctx->conf->proxy.port);
|
||||
http_remove_header_field(&res->hdr, "Date", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Server", HTTP_REMOVE_ALL);
|
||||
|
||||
ctx->use_proxy = proxy_init(&ctx->proxy, &ctx->req, res, status, ctx->conf, &ctx->socket, &ctx->custom_status, ctx->err_msg) == 0;
|
||||
ctx->proxy->client = ctx;
|
||||
|
||||
if (res->status->code == 101) {
|
||||
const char *connection = http_get_header_field(&res->hdr, "Connection");
|
||||
const char *upgrade = http_get_header_field(&res->hdr, "Upgrade");
|
||||
if (connection != NULL && upgrade != NULL &&
|
||||
(strstr(connection, "upgrade") != NULL || strstr(connection, "Upgrade") != NULL) &&
|
||||
strcmp(upgrade, "websocket") == 0)
|
||||
{
|
||||
const char *ws_accept = http_get_header_field(&res->hdr, "Sec-WebSocket-Accept");
|
||||
if (ws_calc_accept_key(status->ws_key, buf) == 0) {
|
||||
ctx->use_proxy = (strcmp(buf, ws_accept) == 0) ? 2 : 1;
|
||||
}
|
||||
} else {
|
||||
status->status = 101;
|
||||
status->origin = INTERNAL;
|
||||
res->status = http_get_status(501);
|
||||
}
|
||||
}
|
||||
|
||||
// Let 300 be formatted by origin server
|
||||
if (ctx->use_proxy && res->status->code >= 301 && res->status->code < 600) {
|
||||
const char *content_type = http_get_header_field(&res->hdr, "Content-Type");
|
||||
const char *content_length_f = http_get_header_field(&res->hdr, "Content-Length");
|
||||
const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||
if (content_encoding == NULL && (
|
||||
content_length_f == NULL ||
|
||||
(content_length_f != NULL && strcmp(content_length_f, "0") == 0) ||
|
||||
(content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0)))
|
||||
{
|
||||
long content_len = (strcmp(ctx->req.method, "HEAD") != 0 && content_length_f != NULL) ? strtol(content_length_f, NULL, 10) : 0;
|
||||
if (content_len <= sizeof(ctx->msg_content) - 1) {
|
||||
if (status->status != 101) {
|
||||
status->status = res->status->code;
|
||||
status->origin = res->status->code >= 400 ? SERVER : NONE;
|
||||
}
|
||||
ctx->use_proxy = 0;
|
||||
|
||||
if (content_len > 0)
|
||||
proxy_dump(ctx->proxy, ctx->msg_content, content_len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(ctx->req.method, "HEAD") == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||
if (use_proxy && content_encoding == NULL) {
|
||||
int http_comp = http_get_compression(&req, &res);
|
||||
if (http_comp & COMPRESS_BR) {
|
||||
use_proxy |= PROXY_COMPRESS_BR;
|
||||
} else if (http_comp & COMPRESS_GZ) {
|
||||
use_proxy |= PROXY_COMPRESS_GZ;
|
||||
}
|
||||
}
|
||||
|
||||
char *transfer_encoding = http_get_header_field(&res->hdr, "Transfer-Encoding");
|
||||
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
|
||||
http_remove_header_field(&res->hdr, "Transfer-Encoding", HTTP_REMOVE_ALL);
|
||||
ret = sprintf(buf, "%s%s%s",
|
||||
(use_proxy & PROXY_COMPRESS_BR) ? "br" :
|
||||
((use_proxy & PROXY_COMPRESS_GZ) ? "gzip" : ""),
|
||||
((use_proxy & PROXY_COMPRESS) && chunked) ? ", " : "",
|
||||
chunked ? "chunked" : "");
|
||||
if (ret > 0) {
|
||||
http_add_header_field(&res->hdr, "Transfer-Encoding", buf);
|
||||
}
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proxy_handler_2(client_ctx_t *ctx) {
|
||||
const char *transfer_encoding = http_get_header_field(&ctx->res.hdr, "Transfer-Encoding");
|
||||
int chunked = transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL;
|
||||
|
||||
const char *content_len = http_get_header_field(&ctx->res.hdr, "Content-Length");
|
||||
unsigned long len_to_send = 0;
|
||||
if (content_len != NULL) {
|
||||
len_to_send = strtol(content_len, NULL, 10);
|
||||
}
|
||||
|
||||
int flags = (chunked ? PROXY_CHUNKED : 0) | (ctx->use_proxy & PROXY_COMPRESS);
|
||||
int ret = proxy_send(ctx->proxy, &ctx->socket, len_to_send, flags);
|
||||
|
||||
if (ret < 0) {
|
||||
ctx->c_keep_alive = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void proxy_close(proxy_ctx_t *ctx) {
|
||||
client_ctx_t *cctx = ctx->client;
|
||||
if (cctx) {
|
||||
logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, cctx->req_host, CLR_STR, cctx->log_prefix);
|
||||
} else {
|
||||
logger_set_prefix("");
|
||||
}
|
||||
|
||||
info(BLUE_STR "Closing proxy connection");
|
||||
sock_close(&ctx->proxy);
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
errno = 0;
|
||||
}
|
@@ -1,31 +1,396 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Client request handler
|
||||
* @file src/worker/request_handler.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-28
|
||||
*/
|
||||
|
||||
#include "request_handler.h"
|
||||
#include "../logger.h"
|
||||
#include "../defs.h"
|
||||
#include "func.h"
|
||||
#include "../workers.h"
|
||||
#include "../lib/mpmc.h"
|
||||
#include "../logger.h"
|
||||
#include "../lib/utils.h"
|
||||
#include "tcp_closer.h"
|
||||
#include "../server.h"
|
||||
#include "../lib/res.h"
|
||||
#include "../lib/error.h"
|
||||
|
||||
static mpmc_t mpmc_ctx;
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
||||
static void request_handler_func(client_ctx_t *ctx);
|
||||
static int request_handler(client_ctx_t *ctx);
|
||||
|
||||
int request_handler_init(int n_workers, int buf_size) {
|
||||
return mpmc_init(&mpmc_ctx, n_workers, buf_size, (void (*)(void *)) request_handler_func, "req");
|
||||
void request_handler_func(client_ctx_t *ctx) {
|
||||
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix);
|
||||
|
||||
switch (request_handler(ctx)) {
|
||||
case 0:
|
||||
respond(ctx);
|
||||
request_complete(ctx);
|
||||
handle_request(ctx);
|
||||
break;
|
||||
case 1:
|
||||
local_handle(ctx);
|
||||
break;
|
||||
case 2:
|
||||
proxy_handle(ctx);
|
||||
break;
|
||||
default:
|
||||
tcp_close(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int handle_request(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&mpmc_ctx, ctx);
|
||||
static void init_ctx(client_ctx_t *ctx) {
|
||||
ctx->conf = NULL;
|
||||
ctx->file = NULL;
|
||||
ctx->proxy = NULL;
|
||||
ctx->use_fastcgi = 0;
|
||||
ctx->use_proxy = 0;
|
||||
ctx->ws_close = 0;
|
||||
ctx->proxy = NULL;
|
||||
ctx->msg_content[0] = 0;
|
||||
ctx->msg_buf = NULL;
|
||||
ctx->msg_buf_ptr = NULL;
|
||||
ctx->req_host[0] = 0;
|
||||
ctx->err_msg[0] = 0;
|
||||
ctx->req_s = ctx->socket.ts_last;
|
||||
|
||||
memset(&ctx->uri, 0, sizeof(ctx->uri));
|
||||
memset(&ctx->req, 0, sizeof(ctx->req));
|
||||
memset(&ctx->res, 0, sizeof(ctx->res));
|
||||
|
||||
|
||||
ctx->res.status = http_get_status(501);
|
||||
http_init_hdr(&ctx->res.hdr);
|
||||
ctx->res.hdr.last_field_num = -1;
|
||||
sprintf(ctx->res.version, "1.1");
|
||||
|
||||
ctx->status.status = 0;
|
||||
ctx->status.origin = NONE;
|
||||
ctx->status.ws_key = NULL;
|
||||
}
|
||||
|
||||
void request_handler_stop(void) {
|
||||
mpmc_stop(&mpmc_ctx);
|
||||
static int request_handler(client_ctx_t *ctx) {
|
||||
sock *client = &ctx->socket;
|
||||
char *err_msg = ctx->err_msg;
|
||||
http_res *res = &ctx->res;
|
||||
|
||||
long ret;
|
||||
char buf0[1024], buf1[1024];
|
||||
|
||||
ctx->req_s = clock_micros();
|
||||
|
||||
init_ctx(ctx);
|
||||
|
||||
http_add_header_field(&res->hdr, "Date", http_get_date(buf0, sizeof(buf0)));
|
||||
http_add_header_field(&res->hdr, "Server", SERVER_STR);
|
||||
/*if (ret <= 0) {
|
||||
if (errno != 0) return 0;
|
||||
|
||||
ctx->c_keep_alive = 0;
|
||||
res->status = http_get_status(408);
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
http_req *req = &ctx->req;
|
||||
ret = http_receive_request(client, req);
|
||||
if (ret != 0) {
|
||||
ctx->c_keep_alive = 0;
|
||||
error("Unable to receive http header");
|
||||
sprintf(err_msg, "Unable to receive http header: %s.", error_str(errno, buf0, sizeof(buf0)));
|
||||
int err = error_get_http();
|
||||
res->status = http_get_status(err == HTTP_ERROR_URI_TOO_LONG ? 414 : (err == HTTP_ERROR_TOO_MANY_HEADER_FIELDS ? 431 : 400));
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *hdr_connection = http_get_header_field(&req->hdr, "Connection");
|
||||
ctx->c_keep_alive = (hdr_connection != NULL && (strstr(hdr_connection, "keep-alive") != NULL || strstr(hdr_connection, "Keep-Alive") != NULL));
|
||||
const char *host_ptr = http_get_header_field(&req->hdr, "Host");
|
||||
if (host_ptr != NULL && strlen(host_ptr) > 255) {
|
||||
ctx->req_host[0] = 0;
|
||||
res->status = http_get_status(400);
|
||||
sprintf(err_msg, "Host header field is too long.");
|
||||
return 0;
|
||||
} else if (host_ptr == NULL || strchr(host_ptr, '/') != NULL) {
|
||||
if (strchr(ctx->socket.addr, ':') == NULL) {
|
||||
strcpy(ctx->req_host, ctx->socket.addr);
|
||||
} else {
|
||||
sprintf(ctx->req_host, "[%s]", ctx->socket.addr);
|
||||
}
|
||||
res->status = http_get_status(400);
|
||||
sprintf(err_msg, "The client provided no or an invalid Host header field.");
|
||||
return 0;
|
||||
} else {
|
||||
strcpy(ctx->req_host, host_ptr);
|
||||
}
|
||||
|
||||
logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix);
|
||||
info(BLD_STR "%s %s", req->method, req->uri);
|
||||
|
||||
if (strncmp(req->uri, "/.sesimos/res/", 14) == 0) {
|
||||
if (strcmp(req->method, "GET") != 0 && strcmp(req->method, "HEAD") != 0) {
|
||||
res->status = http_get_status(405);
|
||||
http_add_header_field(&res->hdr, "Allow", "GET, HEAD");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const res_t resources[] = {
|
||||
{"style.css", "text/css; charset=UTF-8", http_style_doc, http_style_doc_size},
|
||||
{"icon-error.svg", "image/svg+xml; charset=UTF-8", http_icon_error, http_icon_error_size},
|
||||
{"icon-info.svg", "image/svg+xml; charset=UTF-8", http_icon_info, http_icon_info_size},
|
||||
{"icon-success.svg", "image/svg+xml; charset=UTF-8", http_icon_success, http_icon_success_size},
|
||||
{"icon-warning.svg", "image/svg+xml; charset=UTF-8", http_icon_warning, http_icon_warning_size},
|
||||
{"globe.svg", "image/svg+xml; charset=UTF-8", http_icon_globe, http_icon_globe_size},
|
||||
};
|
||||
|
||||
res->status = http_get_status(404);
|
||||
for (int i = 0; i < sizeof(resources) / sizeof(res_t); i++) {
|
||||
const res_t *r = &resources[i];
|
||||
if (strcmp(req->uri + 14, r->name) == 0) {
|
||||
res->status = http_get_status(203);
|
||||
http_add_header_field(&res->hdr, "Content-Type", r->type);
|
||||
http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=86400");
|
||||
ctx->msg_buf = (char *) r->content;
|
||||
ctx->content_length = r->size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->conf = get_host_config(ctx->req_host);
|
||||
if (ctx->conf == NULL) {
|
||||
res->status = http_get_status(421);
|
||||
strcpy(ctx->err_msg, "The requested host name is not configured on the server.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_uri *uri = &ctx->uri;
|
||||
unsigned char dir_mode = (ctx->conf->type == CONFIG_TYPE_LOCAL ? ctx->conf->local.dir_mode : URI_DIR_MODE_NO_VALIDATION);
|
||||
ret = uri_init(uri, ctx->conf->local.webroot, req->uri, dir_mode);
|
||||
if (ret != 0) {
|
||||
if (ret == 1) {
|
||||
sprintf(err_msg, "Invalid URI: has to start with slash.");
|
||||
res->status = http_get_status(400);
|
||||
} else if (ret == 2) {
|
||||
sprintf(err_msg, "Invalid URI: contains relative path change (/../).");
|
||||
res->status = http_get_status(400);
|
||||
} else if (ret == 3) {
|
||||
sprintf(err_msg, "The specified webroot directory does not exist.");
|
||||
res->status = http_get_status(404);
|
||||
} else {
|
||||
res->status = http_get_status(500);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx->conf->type == CONFIG_TYPE_LOCAL && strcmp(req->method, "TRACE") == 0) {
|
||||
return 1;
|
||||
} else if (dir_mode != URI_DIR_MODE_NO_VALIDATION) {
|
||||
ssize_t size = sizeof(buf0);
|
||||
url_decode(req->uri, buf0, &size);
|
||||
int change_proto = (!client->enc && strncmp(uri->uri, "/.well-known/", 13) != 0);
|
||||
if (strcmp(uri->uri, buf0) != 0 || change_proto) {
|
||||
res->status = http_get_status(308);
|
||||
size = url_encode(uri->uri, strlen(uri->uri), buf0, sizeof(buf0));
|
||||
if (change_proto) {
|
||||
int p_len = snprintf(buf1, sizeof(buf1), "https://%s%s", ctx->req_host, buf0);
|
||||
if (p_len < 0 || p_len >= sizeof(buf1)) {
|
||||
res->status = http_get_status(500);
|
||||
error("Header field 'Location' too long");
|
||||
return 0;
|
||||
}
|
||||
http_add_header_field(&res->hdr, "Location", buf1);
|
||||
} else {
|
||||
http_add_header_field(&res->hdr, "Location", buf0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} else if (!client->enc) {
|
||||
res->status = http_get_status(308);
|
||||
sprintf(buf0, "https://%s%s", ctx->req_host, req->uri);
|
||||
http_add_header_field(&res->hdr, "Location", buf0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx->conf->type == CONFIG_TYPE_LOCAL) {
|
||||
return 1;
|
||||
} else if (ctx->conf->type == CONFIG_TYPE_REVERSE_PROXY) {
|
||||
return 2;
|
||||
} else {
|
||||
error("Unknown host type: %i", ctx->conf->type);
|
||||
res->status = http_get_status(501);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void request_handler_destroy(void) {
|
||||
mpmc_destroy(&mpmc_ctx);
|
||||
int respond(client_ctx_t *ctx) {
|
||||
http_req *req = &ctx->req;
|
||||
http_res *res = &ctx->res;
|
||||
sock *client = &ctx->socket;
|
||||
http_status_ctx *status = &ctx->status;
|
||||
char *err_msg = ctx->err_msg;
|
||||
|
||||
long ret = 0;
|
||||
char buf0[1024];
|
||||
char msg_pre_buf_1[4096], msg_pre_buf_2[4096];
|
||||
char buffer[CHUNK_SIZE];
|
||||
|
||||
if (!ctx->use_proxy) {
|
||||
if (ctx->conf != NULL && ctx->conf->type == CONFIG_TYPE_LOCAL && ctx->uri.is_static && res->status->code == 405) {
|
||||
http_add_header_field(&res->hdr, "Allow", "GET, HEAD, TRACE");
|
||||
}
|
||||
if (http_get_header_field(&res->hdr, "Accept-Ranges") == NULL) {
|
||||
http_add_header_field(&res->hdr, "Accept-Ranges", "none");
|
||||
}
|
||||
if (!ctx->use_fastcgi && ctx->file == NULL && ctx->msg_buf == NULL) {
|
||||
http_remove_header_field(&res->hdr, "Date", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Server", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Cache-Control", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Content-Type", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Content-Encoding", HTTP_REMOVE_ALL);
|
||||
http_add_header_field(&res->hdr, "Date", http_get_date(buf0, sizeof(buf0)));
|
||||
http_add_header_field(&res->hdr, "Server", SERVER_STR);
|
||||
http_add_header_field(&res->hdr, "Cache-Control", "no-cache");
|
||||
http_add_header_field(&res->hdr, "Content-Type", "text/html; charset=UTF-8");
|
||||
|
||||
// TODO list Locations on 3xx Redirects
|
||||
const http_doc_info *http_info = http_get_status_info(res->status);
|
||||
const http_status_msg *http_msg = http_get_error_msg(res->status);
|
||||
|
||||
if (ctx->msg_content[0] == 0) {
|
||||
if (res->status->code >= 300 && res->status->code < 400) {
|
||||
const char *location = http_get_header_field(&res->hdr, "Location");
|
||||
if (location != NULL) {
|
||||
snprintf(ctx->msg_content, sizeof(ctx->msg_content), " <ul>\n <li><a href=\"%s\">%s</a></li>\n </ul>\n", location, location);
|
||||
}
|
||||
}
|
||||
} else if (strncmp(ctx->msg_content, "<!DOCTYPE html>", 15) == 0 || strncmp(ctx->msg_content, "<html", 5) == 0) {
|
||||
ctx->msg_content[0] = 0;
|
||||
// TODO let relevant information pass?
|
||||
}
|
||||
|
||||
char *proxy_doc = "";
|
||||
if (ctx->conf != NULL && ctx->conf->type == CONFIG_TYPE_REVERSE_PROXY) {
|
||||
const http_status *status_hdr = http_get_status(status->status);
|
||||
char stat_str[8];
|
||||
sprintf(stat_str, "%03i", status->status);
|
||||
snprintf(msg_pre_buf_2, sizeof(msg_pre_buf_2), http_proxy_doc,
|
||||
" success",
|
||||
(status->origin == CLIENT_REQ) ? " error" : " success",
|
||||
(status->origin == INTERNAL) ? " error" : " success",
|
||||
(status->origin == SERVER_REQ) ? " error" : (status->status == 0 ? "" : " success"),
|
||||
(status->origin == CLIENT_RES) ? " error" : " success",
|
||||
(status->origin == SERVER) ? " error" : (status->status == 0 ? "" : " success"),
|
||||
(status->origin == SERVER_RES) ? " error" : (status->status == 0 ? "" : " success"),
|
||||
(status->origin == INTERNAL) ? " error" : " success",
|
||||
(status->origin == INTERNAL || status->origin == SERVER) ? " error" : " success",
|
||||
res->status->code,
|
||||
res->status->msg,
|
||||
(status->status == 0) ? "???" : stat_str,
|
||||
(status_hdr != NULL) ? status_hdr->msg : "",
|
||||
ctx->req_host, SERVER_NAME);
|
||||
proxy_doc = msg_pre_buf_2;
|
||||
}
|
||||
|
||||
ctx->msg_buf_ptr = malloc(4096);
|
||||
ctx->msg_buf = ctx->msg_buf_ptr;
|
||||
snprintf(msg_pre_buf_1, sizeof(msg_pre_buf_1), http_info->doc,
|
||||
res->status->code, res->status->msg, http_msg != NULL ? http_msg->msg : "", err_msg[0] != 0 ? err_msg : "");
|
||||
ctx->content_length = snprintf(ctx->msg_buf, 4096, http_default_doc, res->status->code,
|
||||
res->status->msg, msg_pre_buf_1, http_info->mode, http_info->icon, http_info->color, ctx->req_host,
|
||||
proxy_doc, ctx->msg_content[0] != 0 ? ctx->msg_content : "", SERVER_STR_HTML);
|
||||
}
|
||||
if (ctx->content_length >= 0) {
|
||||
sprintf(buf0, "%li", ctx->content_length);
|
||||
http_remove_header_field(&res->hdr, "Content-Length", HTTP_REMOVE_ALL);
|
||||
http_add_header_field(&res->hdr, "Content-Length", buf0);
|
||||
} else if (http_get_header_field(&res->hdr, "Transfer-Encoding") == NULL) {
|
||||
ctx->s_keep_alive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->use_proxy != 2) {
|
||||
http_remove_header_field(&res->hdr, "Connection", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res->hdr, "Keep-Alive", HTTP_REMOVE_ALL);
|
||||
if (ctx->s_keep_alive && ctx->c_keep_alive) {
|
||||
http_add_header_field(&res->hdr, "Connection", "keep-alive");
|
||||
sprintf(buf0, "timeout=%i, max=%i", CLIENT_TIMEOUT, REQ_PER_CONNECTION);
|
||||
http_add_header_field(&res->hdr, "Keep-Alive", buf0);
|
||||
} else {
|
||||
http_add_header_field(&res->hdr, "Connection", "close");
|
||||
}
|
||||
}
|
||||
|
||||
http_send_response(client, res);
|
||||
ctx->res_ts = clock_micros();
|
||||
const char *location = http_get_header_field(&res->hdr, "Location");
|
||||
info("%s%s%03i %s%s%s (%s)%s", http_get_status_color(res->status), ctx->use_proxy ? "-> " : "", res->status->code,
|
||||
res->status->msg, location != NULL ? " -> " : "", location != NULL ? location : "",
|
||||
format_duration(ctx->res_ts - ctx->req_s, buf0), CLR_STR);
|
||||
|
||||
// TODO access/error log file
|
||||
|
||||
if (ctx->use_proxy) {
|
||||
// reverse proxy
|
||||
return 3;
|
||||
} else if (strcmp(req->method, "HEAD") != 0) {
|
||||
// default response
|
||||
if (ctx->msg_buf != NULL) {
|
||||
ret = sock_send(client, ctx->msg_buf, ctx->content_length, 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send");
|
||||
}
|
||||
} else if (ctx->file != NULL) {
|
||||
unsigned long len, snd_len = 0;
|
||||
while (snd_len < ctx->content_length) {
|
||||
len = fread(buffer, 1, CHUNK_SIZE, ctx->file);
|
||||
if (snd_len + len > ctx->content_length) {
|
||||
len = ctx->content_length - snd_len;
|
||||
}
|
||||
ret = sock_send(client, buffer, len, feof(ctx->file) ? 0 : MSG_MORE);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send");
|
||||
break;
|
||||
}
|
||||
snd_len += ret;
|
||||
}
|
||||
} else if (ctx->use_fastcgi) {
|
||||
// FastCGI
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (ret < 0) ctx->c_keep_alive = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void request_handler_func(client_ctx_t *ctx) {
|
||||
// TODO
|
||||
void request_complete(client_ctx_t *ctx) {
|
||||
char buf[32];
|
||||
ctx->req_e = clock_micros();
|
||||
info("Transfer complete: %s", format_duration(ctx->req_e - ctx->req_s, buf));
|
||||
|
||||
if (ctx->file) fclose(ctx->file);
|
||||
free(ctx->msg_buf_ptr);
|
||||
uri_free(&ctx->uri);
|
||||
http_free_req(&ctx->req);
|
||||
http_free_res(&ctx->res);
|
||||
}
|
||||
|
||||
void timeout_request(client_ctx_t *ctx) {
|
||||
init_ctx(ctx);
|
||||
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix);
|
||||
|
||||
ctx->s_keep_alive = 0;
|
||||
ctx->res.status = http_get_status(408);
|
||||
|
||||
respond(ctx);
|
||||
tcp_close(ctx);
|
||||
}
|
||||
|
@@ -1,15 +0,0 @@
|
||||
|
||||
#ifndef SESIMOS_REQUEST_HANDLER_H
|
||||
#define SESIMOS_REQUEST_HANDLER_H
|
||||
|
||||
#include "../client.h"
|
||||
|
||||
int request_handler_init(int n_workers, int buf_size);
|
||||
|
||||
int handle_request(client_ctx_t *ctx);
|
||||
|
||||
void request_handler_stop(void);
|
||||
|
||||
void request_handler_destroy(void);
|
||||
|
||||
#endif //SESIMOS_REQUEST_HANDLER_H
|
@@ -1,77 +1,70 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief TCP acceptor
|
||||
* @file src/worker/tcp_acceptor.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-28
|
||||
*/
|
||||
|
||||
#include "tcp_acceptor.h"
|
||||
#include "func.h"
|
||||
#include "../logger.h"
|
||||
#include "../lib/mpmc.h"
|
||||
#include "../lib/utils.h"
|
||||
#include "../server.h"
|
||||
#include "../lib/geoip.h"
|
||||
#include "../async.h"
|
||||
#include "tcp_closer.h"
|
||||
#include "request_handler.h"
|
||||
#include "../workers.h"
|
||||
#include "../server.h"
|
||||
#include "../lib/error.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
static mpmc_t mpmc_ctx;
|
||||
static int tcp_acceptor(client_ctx_t *ctx);
|
||||
|
||||
static void tcp_acceptor_func(client_ctx_t *ctx);
|
||||
|
||||
int tcp_acceptor_init(int n_workers, int buf_size) {
|
||||
return mpmc_init(&mpmc_ctx, n_workers, buf_size, (void (*)(void *)) tcp_acceptor_func, "tcp/a");
|
||||
}
|
||||
|
||||
int tcp_accept(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&mpmc_ctx, ctx);
|
||||
}
|
||||
|
||||
void tcp_acceptor_stop(void) {
|
||||
mpmc_stop(&mpmc_ctx);
|
||||
}
|
||||
|
||||
void tcp_acceptor_destroy(void) {
|
||||
mpmc_destroy(&mpmc_ctx);
|
||||
}
|
||||
|
||||
static void tcp_acceptor_func(client_ctx_t *ctx) {
|
||||
struct sockaddr_in6 server_addr;
|
||||
char log_client_prefix[256];
|
||||
|
||||
inet_ntop(ctx->socket.addr.ipv6.sin6_family, &ctx->socket.addr.ipv6.sin6_addr, ctx->_c_addr, sizeof(ctx->_c_addr));
|
||||
if (strncmp(ctx->_c_addr, "::ffff:", 7) == 0) {
|
||||
ctx->addr = ctx->_c_addr + 7;
|
||||
void tcp_acceptor_func(client_ctx_t *ctx) {
|
||||
if (tcp_acceptor(ctx) == 0) {
|
||||
handle_request(ctx);
|
||||
} else {
|
||||
ctx->addr = ctx->_c_addr;
|
||||
tcp_close(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int tcp_acceptor(client_ctx_t *ctx) {
|
||||
struct sockaddr_in6 server_addr;
|
||||
|
||||
inet_ntop(ctx->socket._addr.ipv6.sin6_family, &ctx->socket._addr.ipv6.sin6_addr, ctx->_c_addr, sizeof(ctx->_c_addr));
|
||||
if (strncmp(ctx->_c_addr, "::ffff:", 7) == 0) {
|
||||
ctx->socket.addr = ctx->_c_addr + 7;
|
||||
} else {
|
||||
ctx->socket.addr = ctx->_c_addr;
|
||||
}
|
||||
|
||||
socklen_t len = sizeof(server_addr);
|
||||
getsockname(ctx->socket.socket, (struct sockaddr *) &server_addr, &len);
|
||||
inet_ntop(server_addr.sin6_family, (void *) &server_addr.sin6_addr, ctx->_s_addr, sizeof(ctx->_s_addr));
|
||||
if (strncmp(ctx->_s_addr, "::ffff:", 7) == 0) {
|
||||
ctx->s_addr = ctx->_s_addr + 7;
|
||||
ctx->socket.s_addr = ctx->_s_addr + 7;
|
||||
} else {
|
||||
ctx->s_addr = ctx->_s_addr;
|
||||
ctx->socket.s_addr = ctx->_s_addr;
|
||||
}
|
||||
|
||||
sprintf(log_client_prefix, "[%s%4i%s]%s[%*s][%5i]%s", (int) ctx->socket.enc ? HTTPS_STR : HTTP_STR,
|
||||
ntohs(server_addr.sin6_port), CLR_STR, /*color_table[0]*/ "", INET6_ADDRSTRLEN, ctx->addr,
|
||||
ntohs(ctx->socket.addr.ipv6.sin6_port), CLR_STR);
|
||||
sprintf(ctx->log_prefix, "[%s%4i%s]%s[%*s][%5i]%s", (int) ctx->socket.enc ? HTTPS_STR : HTTP_STR,
|
||||
ntohs(server_addr.sin6_port), CLR_STR, /*color_table[0]*/ "", INET6_ADDRSTRLEN, ctx->socket.addr,
|
||||
ntohs(ctx->socket._addr.ipv6.sin6_port), CLR_STR);
|
||||
|
||||
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix);
|
||||
|
||||
sprintf(ctx->log_prefix, "[%*s]%s", INET6_ADDRSTRLEN, ctx->s_addr, log_client_prefix);
|
||||
logger_set_prefix(ctx->log_prefix);
|
||||
|
||||
int ret;
|
||||
char buf[1024];
|
||||
sock *client = &ctx->socket;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ctx->begin);
|
||||
ctx->cnx_s = clock_micros();
|
||||
|
||||
if (config.dns_server[0] != 0) {
|
||||
sprintf(buf, "dig @%s +short +time=1 -x %s", config.dns_server, ctx->addr);
|
||||
sprintf(buf, "dig @%s +short +time=1 -x %s", config.dns_server, ctx->socket.addr);
|
||||
FILE *dig = popen(buf, "r");
|
||||
if (dig == NULL) {
|
||||
error("Unable to start dig: %s", strerror(errno));
|
||||
error("Unable to start dig: %s");
|
||||
goto dig_err;
|
||||
}
|
||||
unsigned long read = fread(buf, 1, sizeof(buf), dig);
|
||||
@@ -92,19 +85,15 @@ static void tcp_acceptor_func(client_ctx_t *ctx) {
|
||||
}
|
||||
|
||||
ctx->cc[0] = 0;
|
||||
geoip_lookup_country(&client->addr.sock, ctx->cc);
|
||||
geoip_lookup_country(&client->_addr.sock, ctx->cc);
|
||||
|
||||
info("Connection accepted from %s %s%s%s[%s]", ctx->addr, ctx->host[0] != 0 ? "(" : "",
|
||||
info("Connection accepted from %s %s%s%s[%s]", ctx->socket.addr, ctx->host[0] != 0 ? "(" : "",
|
||||
ctx->host[0] != 0 ? ctx->host : "", ctx->host[0] != 0 ? ") " : "",
|
||||
ctx->cc[0] != 0 ? ctx->cc : "N/A");
|
||||
|
||||
struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0};
|
||||
if (setsockopt(client->socket, SOL_SOCKET, SO_RCVTIMEO, &client_timeout, sizeof(client_timeout)) == -1 ||
|
||||
setsockopt(client->socket, SOL_SOCKET, SO_SNDTIMEO, &client_timeout, sizeof(client_timeout)) == -1)
|
||||
{
|
||||
if (sock_set_socket_timeout(client, 1) != 0 || sock_set_timeout(client, CLIENT_TIMEOUT) != 0) {
|
||||
error("Unable to set timeout for socket");
|
||||
tcp_close(ctx);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (client->enc) {
|
||||
@@ -113,19 +102,29 @@ static void tcp_acceptor_func(client_ctx_t *ctx) {
|
||||
SSL_set_accept_state(client->ssl);
|
||||
|
||||
ret = SSL_accept(client->ssl);
|
||||
client->_last_ret = ret;
|
||||
client->_errno = errno;
|
||||
client->_ssl_error = ERR_get_error();
|
||||
if (ret <= 0) {
|
||||
error("Unable to perform handshake: %s", sock_strerror(client));
|
||||
tcp_close(ctx);
|
||||
return;
|
||||
if (ret != 1) {
|
||||
error_ssl(SSL_get_error(client->ssl, ret));
|
||||
info("Unable to perform handshake");
|
||||
return - 1;
|
||||
}
|
||||
client->ts_last = clock_micros();
|
||||
}
|
||||
|
||||
ctx->req_num = 0;
|
||||
ctx->s_keep_alive = 1;
|
||||
ctx->c_keep_alive = 1;
|
||||
|
||||
async(ctx->socket.socket, POLLIN, 0, (void (*)(void *)) handle_request, ctx, (void (*)(void *)) tcp_close, ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tcp_close(client_ctx_t *ctx) {
|
||||
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix);
|
||||
|
||||
sock_close(&ctx->socket);
|
||||
|
||||
ctx->cnx_e = clock_micros();
|
||||
char buf[32];
|
||||
info("Connection closed (%s)", format_duration(ctx->cnx_e - ctx->cnx_s, buf));
|
||||
|
||||
server_free_client(ctx);
|
||||
}
|
||||
|
@@ -1,15 +0,0 @@
|
||||
|
||||
#ifndef SESIMOS_TCP_ACCEPTOR_H
|
||||
#define SESIMOS_TCP_ACCEPTOR_H
|
||||
|
||||
#include "../client.h"
|
||||
|
||||
int tcp_acceptor_init(int n_workers, int buf_size);
|
||||
|
||||
int tcp_accept(client_ctx_t *ctx);
|
||||
|
||||
void tcp_acceptor_stop(void);
|
||||
|
||||
void tcp_acceptor_destroy(void);
|
||||
|
||||
#endif //SESIMOS_TCP_ACCEPTOR_H
|
@@ -1,40 +0,0 @@
|
||||
|
||||
#include "tcp_closer.h"
|
||||
#include "../logger.h"
|
||||
#include "../lib/mpmc.h"
|
||||
#include "../lib/utils.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
static mpmc_t mpmc_ctx;
|
||||
|
||||
static void tcp_closer_func(client_ctx_t *ctx);
|
||||
|
||||
int tcp_closer_init(int n_workers, int buf_size) {
|
||||
return mpmc_init(&mpmc_ctx, n_workers, buf_size, (void (*)(void *)) tcp_closer_func, "tcp/c");
|
||||
}
|
||||
|
||||
int tcp_close(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&mpmc_ctx, ctx);
|
||||
}
|
||||
|
||||
void tcp_closer_stop(void) {
|
||||
mpmc_stop(&mpmc_ctx);
|
||||
}
|
||||
|
||||
void tcp_closer_destroy(void) {
|
||||
mpmc_destroy(&mpmc_ctx);
|
||||
}
|
||||
|
||||
static void tcp_closer_func(client_ctx_t *ctx) {
|
||||
logger_set_prefix(ctx->log_prefix);
|
||||
|
||||
sock_close(&ctx->socket);
|
||||
|
||||
char buf[32];
|
||||
clock_gettime(CLOCK_MONOTONIC, &ctx->end);
|
||||
unsigned long micros = (ctx->end.tv_nsec - ctx->begin.tv_nsec) / 1000 + (ctx->end.tv_sec - ctx->begin.tv_sec) * 1000000;
|
||||
info("Connection closed (%s)", format_duration(micros, buf));
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
|
||||
#ifndef SESIMOS_TCP_CLOSER_H
|
||||
#define SESIMOS_TCP_CLOSER_H
|
||||
|
||||
#include "../client.h"
|
||||
|
||||
int tcp_closer_init(int n_workers, int buf_size);
|
||||
|
||||
int tcp_close(client_ctx_t *ctx);
|
||||
|
||||
void tcp_closer_stop(void);
|
||||
|
||||
void tcp_closer_destroy(void);
|
||||
|
||||
#endif //SESIMOS_TCP_CLOSER_H
|
94
src/worker/ws_frame_handler.c
Normal file
94
src/worker/ws_frame_handler.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief WebSocket frame handler
|
||||
* @file src/worker/ws_frame_handler.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-30
|
||||
*/
|
||||
|
||||
#include "../defs.h"
|
||||
#include "func.h"
|
||||
#include "../logger.h"
|
||||
#include "../lib/websocket.h"
|
||||
#include "../workers.h"
|
||||
#include <errno.h>
|
||||
|
||||
static int ws_frame_handler(ws_ctx_t *ctx);
|
||||
|
||||
void ws_frame_handler_func(ws_ctx_t *ctx) {
|
||||
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix);
|
||||
|
||||
if (ws_frame_handler(ctx) == 0) {
|
||||
if (ctx->client->ws_close == 3) {
|
||||
ws_close(ctx);
|
||||
} else {
|
||||
ws_handle_frame(ctx);
|
||||
}
|
||||
} else {
|
||||
ws_close(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int ws_handle_connection(client_ctx_t *ctx) {
|
||||
info("Upgrading to WebSocket connection");
|
||||
sock_set_timeout(&ctx->socket, WS_TIMEOUT);
|
||||
sock_set_timeout(&ctx->proxy->proxy, WS_TIMEOUT);
|
||||
|
||||
ws_ctx_t *a = malloc(sizeof(ws_ctx_t));
|
||||
ws_ctx_t *b = malloc(sizeof(ws_ctx_t));
|
||||
|
||||
a->other = b, b->other = a;
|
||||
a->client = ctx, b->client = ctx;
|
||||
a->socket = &ctx->socket, b->socket = &ctx->proxy->proxy;
|
||||
|
||||
ws_handle_frame(a);
|
||||
ws_handle_frame(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ws_frame_handler(ws_ctx_t *ctx) {
|
||||
ws_frame frame;
|
||||
char buf[CHUNK_SIZE];
|
||||
|
||||
sock *socket = ctx->socket;
|
||||
sock *other = (ctx->socket == &ctx->client->socket) ? &ctx->client->proxy->proxy : &ctx->client->socket;
|
||||
|
||||
if (ws_recv_frame_header(socket, &frame) != 0)
|
||||
return -1;
|
||||
|
||||
debug("WebSocket: Peer %s, Opcode=0x%X, Len=%li", (ctx->socket == &ctx->client->socket) ? "client" : "server", frame.opcode, frame.len);
|
||||
|
||||
if (frame.opcode == 0x8) {
|
||||
ctx->client->ws_close |= (ctx->socket == &ctx->client->socket) ? 1 : 2;
|
||||
}
|
||||
|
||||
if (ws_send_frame_header(other, &frame) != 0)
|
||||
return -1;
|
||||
|
||||
if (frame.len > 0) {
|
||||
long ret = sock_splice(other, socket, buf, sizeof(buf), frame.len);
|
||||
if (ret < 0) {
|
||||
error("Unable to forward data in WebSocket");
|
||||
return -1;
|
||||
} else if (ret != frame.len) {
|
||||
error("Unable to forward correct number of bytes in WebSocket");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ws_close(ws_ctx_t *ctx) {
|
||||
ws_ctx_t *other = ctx->other;
|
||||
if (other) {
|
||||
other->other = NULL;
|
||||
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix);
|
||||
info("Closing WebSocket connection");
|
||||
proxy_close(ctx->client->proxy);
|
||||
tcp_close(ctx->client);
|
||||
}
|
||||
free(ctx);
|
||||
errno = 0;
|
||||
}
|
87
src/workers.c
Normal file
87
src/workers.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Worker interface
|
||||
* @file src/workers.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-29
|
||||
*/
|
||||
|
||||
#include "workers.h"
|
||||
#include "lib/mpmc.h"
|
||||
|
||||
#include "worker/func.h"
|
||||
#include "async.h"
|
||||
|
||||
static mpmc_t tcp_acceptor_ctx, request_handler_ctx, local_handler_ctx, fastcgi_handler_cxt, proxy_handler_ctx,
|
||||
ws_frame_handler_ctx;
|
||||
|
||||
int workers_init(void) {
|
||||
mpmc_init(&tcp_acceptor_ctx, 8, 64, (void (*)(void *)) tcp_acceptor_func, "tcp");
|
||||
mpmc_init(&request_handler_ctx, 16, 64, (void (*)(void *)) request_handler_func, "req");
|
||||
mpmc_init(&local_handler_ctx, 16, 64, (void (*)(void *)) local_handler_func, "local");
|
||||
mpmc_init(&fastcgi_handler_cxt, 16, 64, (void (*)(void *)) fastcgi_handler_func, "fcgi");
|
||||
mpmc_init(&proxy_handler_ctx, 16, 64, (void (*)(void *)) proxy_handler_func, "proxy");
|
||||
mpmc_init(&ws_frame_handler_ctx, 16, 64, (void (*)(void *)) ws_frame_handler_func, "ws");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void workers_stop(void) {
|
||||
mpmc_stop(&tcp_acceptor_ctx);
|
||||
mpmc_stop(&local_handler_ctx);
|
||||
mpmc_stop(&fastcgi_handler_cxt);
|
||||
mpmc_stop(&proxy_handler_ctx);
|
||||
mpmc_stop(&request_handler_ctx);
|
||||
mpmc_stop(&ws_frame_handler_ctx);
|
||||
}
|
||||
|
||||
void workers_destroy(void) {
|
||||
mpmc_destroy(&tcp_acceptor_ctx);
|
||||
mpmc_destroy(&local_handler_ctx);
|
||||
mpmc_destroy(&fastcgi_handler_cxt);
|
||||
mpmc_destroy(&proxy_handler_ctx);
|
||||
mpmc_destroy(&request_handler_ctx);
|
||||
mpmc_destroy(&ws_frame_handler_ctx);
|
||||
}
|
||||
|
||||
int tcp_accept(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&tcp_acceptor_ctx, ctx);
|
||||
}
|
||||
|
||||
static int handle_request_cb(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&request_handler_ctx, ctx);
|
||||
}
|
||||
|
||||
int handle_request(client_ctx_t *ctx) {
|
||||
if (ctx->c_keep_alive && ctx->s_keep_alive) {
|
||||
return async(&ctx->socket, ASYNC_WAIT_READ, 0, ctx,
|
||||
(void (*)(void *)) handle_request_cb,
|
||||
(void (*)(void *)) timeout_request,
|
||||
(void (*)(void *)) tcp_close);
|
||||
} else {
|
||||
tcp_close(ctx);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int local_handle(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&local_handler_ctx, ctx);
|
||||
}
|
||||
|
||||
int fastcgi_handle(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&fastcgi_handler_cxt, ctx);
|
||||
}
|
||||
|
||||
int proxy_handle(client_ctx_t *ctx) {
|
||||
return mpmc_queue(&proxy_handler_ctx, ctx);
|
||||
}
|
||||
|
||||
static int ws_handle_frame_cb(ws_ctx_t *ctx) {
|
||||
return mpmc_queue(&ws_frame_handler_ctx, ctx);
|
||||
}
|
||||
|
||||
int ws_handle_frame(ws_ctx_t *ctx) {
|
||||
return async(ctx->socket, ASYNC_WAIT_READ, 0, ctx,
|
||||
(void (*)(void *)) ws_handle_frame_cb,
|
||||
(void (*)(void *)) ws_close,
|
||||
(void (*)(void *)) ws_close);
|
||||
}
|
32
src/workers.h
Normal file
32
src/workers.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Worker interface (header file)
|
||||
* @file src/workers.h
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2022-12-29
|
||||
*/
|
||||
|
||||
#ifndef SESIMOS_WORKERS_H
|
||||
#define SESIMOS_WORKERS_H
|
||||
|
||||
#include "worker/func.h"
|
||||
|
||||
int workers_init(void);
|
||||
|
||||
void workers_stop(void);
|
||||
|
||||
void workers_destroy(void);
|
||||
|
||||
int tcp_accept(client_ctx_t *ctx);
|
||||
|
||||
int handle_request(client_ctx_t *ctx);
|
||||
|
||||
int local_handle(client_ctx_t *ctx);
|
||||
|
||||
int fastcgi_handle(client_ctx_t *ctx);
|
||||
|
||||
int proxy_handle(client_ctx_t *ctx);
|
||||
|
||||
int ws_handle_frame(ws_ctx_t *ctx);
|
||||
|
||||
#endif //SESIMOS_WORKERS_H
|
92
test/test_list.c
Normal file
92
test/test_list.c
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
#include <criterion/parameterized.h>
|
||||
|
||||
#include "../src/lib/list.h"
|
||||
|
||||
Test(list, simple) {
|
||||
int v;
|
||||
int *list = list_create(sizeof(int), 16);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 0);
|
||||
|
||||
v = 1;
|
||||
list = list_append(list, &v);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 1);
|
||||
cr_assert_eq(list[0], 1);
|
||||
|
||||
v = 3;
|
||||
list = list_append(list, &v);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 2);
|
||||
cr_assert_eq(list[0], 1);
|
||||
cr_assert_eq(list[1], 3);
|
||||
|
||||
v = 2;
|
||||
list = list_insert(list, &v, 1);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 3);
|
||||
cr_assert_eq(list[0], 1);
|
||||
cr_assert_eq(list[1], 2);
|
||||
cr_assert_eq(list[2], 3);
|
||||
|
||||
list = list_remove(list, 0);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 2);
|
||||
cr_assert_eq(list[0], 2);
|
||||
cr_assert_eq(list[1], 3);
|
||||
|
||||
list = list_remove(list, 1);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 1);
|
||||
cr_assert_eq(list[0], 2);
|
||||
|
||||
list = list_remove(list, 0);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 0);
|
||||
|
||||
list_free(list);
|
||||
}
|
||||
|
||||
Test(list, resize) {
|
||||
int v;
|
||||
int *list = list_create(sizeof(int), 4);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 0);
|
||||
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
v = 9182 - i;
|
||||
list = list_append(list, &v);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), i + 1);
|
||||
for (int j = 0; j <= i; j++) {
|
||||
cr_assert_eq(list[j], 9182 - j);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
list = list_remove(list, -1);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 4096 - i - 1);
|
||||
for (int j = 0; j < 4096 - i; j++) {
|
||||
cr_assert_eq(list[j], 9182 - j);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
v = 47391 - i;
|
||||
list = list_append(list, &v);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), i + 1);
|
||||
for (int j = 0; j <= i; j++) {
|
||||
cr_assert_eq(list[j], 47391 - j);
|
||||
}
|
||||
}
|
||||
|
||||
list = list_clear(list);
|
||||
cr_assert_not_null(list);
|
||||
cr_assert_eq(list_size(list), 0);
|
||||
|
||||
list_free(list);
|
||||
}
|
@@ -72,9 +72,9 @@ ParameterizedTestParameters(utils, format_duration) {
|
||||
{999999, "1.0 s"},
|
||||
{1000000, "1.0 s"},
|
||||
{3000000, "3.0 s"},
|
||||
{1000000 * 60, "1.0 min"},
|
||||
{1000000 * 60 * 30L - 30000000, "29.5 min"},
|
||||
{1000000 * 60 * 60L, "60.0 min"},
|
||||
{1000000 * 60, "1:00 min"},
|
||||
{1000000 * 60 * 30L - 30000000, "29:30 min"},
|
||||
{1000000 * 60 * 60L, "60:00 min"},
|
||||
{1000000 * 60 * 120L, "120 min"},
|
||||
};
|
||||
return cr_make_param_array(struct format_duration_t, params, sizeof(params) / sizeof(struct format_duration_t));
|
||||
|
Reference in New Issue
Block a user