Release first beta version server

Version 0.1.0 has been released:
- Added a feature for easily sending an http response
- Minor bugs have been fixed
- Updated API for server modules
- Added a readme file
This commit is contained in:
Doczom
2023-12-10 18:21:37 +05:00
committed by GitHub
parent bb92182caf
commit b32a38a071
13 changed files with 774 additions and 99 deletions

View File

@@ -21,6 +21,7 @@ mime_file=/usbhd0/3/mime_types.bin
; list units
; path = path to lib in units_dir
test=test_unit.obj
rasp=test_unit_2.obj
;database/sqlite3=sqlite3_serv.obj
;database/cvs=cvs_table_server.obj

View File

@@ -1,10 +1,15 @@
format MS COFF
public @EXPORT as 'EXPORTS'
NO_DEBUG_INPUT = 1
API_VERSION = 0x05
FLAG_KEEP_ALIVE = 0x01
NO_DEBUG_INPUT = 0
include 'D:\kos\programs\macros.inc'
struct EXPORT_DATA
struct IMPORT_DATA
version rd 1 ; dword for check api
sizeof rd 1 ; size struct
netfunc_socket rd 1
netfunc_close rd 1
netfunc_bind rd 1
@@ -16,6 +21,26 @@ struct EXPORT_DATA
FileRead rd 1
Alloc rd 1
Free rd 1
parse_http_query rd 1
;send_resp(RESPD* ptr, char* content, uint32_t length)
;create_resp(CONNECT_DATA* session, uint32_t flags)
; FLAG_KEEP_ALIVE = 0x01
; FLAG_ADD_DATE = 0x02 ;(not supported)
; FLAG_NO_SET_CACHE = 0x04 ;(not supported)
; FLAG_NO_CONTENT_ENCODING = 0x08 ;(not supported)
;
;destruct_resp(RESPD* ptr)
;set_http_status(RESPD* ptr, uint32_t status) ; status in '200' format,
;add_http_header(RESPD* ptr, char* ptr_header)
;del_http_header(RESPD* ptr, char* ptr_header) ; no del std header
;set_http_ver(RESPD* ptr, char* version) ; example: RTSP/1.1
send_resp rd 1
create_resp rd 1
destruct_resp rd 1
set_http_status rd 1
add_http_header rd 1
del_http_header rd 1
set_http_ver rd 1
base_response rd 1
GLOBAL_DATA rd 1
@@ -64,13 +89,25 @@ end if
section '.flat' code readable align 16
unit_init:
mov eax, -1
push esi edi
mov esi, [esp + 4*2 + 4]
mov [import_httpd], esi
mov eax, [esp + 4]
mov [import_httpd], eax
cmp dword[esi + IMPORT_DATA.version], API_VERSION
jne .exit
mov edi, IMPORT
mov ecx, [esi + IMPORT_DATA.sizeof]
shr ecx, 2 ; div 4
rep movsd
xor eax, eax
.exit:
pop edi esi
ret 4
server_entry:
push esi edi
mov esi, [esp + 4*2 + 4]
@@ -106,6 +143,11 @@ server_entry:
cmp dword[ecx], 'txt'
jne .no_args
mov dword[text_message], ' '
mov dword[text_message + 4], ' '
mov dword[text_message + 8], ' '
mov dword[text_message + 12], ' '
push esi edi
mov esi, [eax + 12]
mov edi, text_message
@@ -123,8 +165,7 @@ server_entry:
board_input 'create message'
; create http message
push dword 8*1024
mov eax, [import_httpd]
call [eax + EXPORT_DATA.Alloc]
call [IMPORT + IMPORT_DATA.Alloc]
test eax, eax
jz .exit
@@ -168,13 +209,12 @@ server_entry:
; set httpcode
mov dword[edi + sceleton_resp.code], '200 '
; send http message
mov ecx, [import_httpd]
push dword 0 ; flags
push sceleton_resp.size
push edi
push dword[esi + CONNECT_DATA.socket]
call [ecx + EXPORT_DATA.netfunc_send]
call [IMPORT + IMPORT_DATA.netfunc_send]
board_input 'send'
.exit:
@@ -224,3 +264,6 @@ text_message:
export \
unit_init, 'httpd_init', \
server_entry, 'httpd_serv'
IMPORT IMPORT_DATA ;

Binary file not shown.

264
example/test_unit_2.asm Normal file
View File

@@ -0,0 +1,264 @@
format MS COFF
public @EXPORT as 'EXPORTS'
API_VERSION = 0x05
FLAG_KEEP_ALIVE = 0x01
NO_DEBUG_INPUT = 0
include 'D:\kos\programs\macros.inc'
purge mov,add,sub
include 'D:\kos\programs\proc32.inc'
struct IMPORT_DATA
version rd 1 ; dword for check api
sizeof rd 1 ; size struct
netfunc_socket rd 1
netfunc_close rd 1
netfunc_bind rd 1
netfunc_accept rd 1
netfunc_listen rd 1
netfunc_recv rd 1
netfunc_send rd 1
FileInfo rd 1
FileRead rd 1
Alloc rd 1
Free rd 1
parse_http_query rd 1
;send_resp(RESPD* ptr, char* content, uint32_t length)
;create_resp(CONNECT_DATA* session, uint32_t flags)
; FLAG_KEEP_ALIVE = 0x01
; FLAG_ADD_DATE = 0x02 ;(not supported)
; FLAG_NO_SET_CACHE = 0x04 ;(not supported)
; FLAG_NO_CONTENT_ENCODING = 0x08 ;(not supported)
;
;destruct_resp(RESPD* ptr)
;set_http_status(RESPD* ptr, uint32_t status) ; status in '200' format,
;add_http_header(RESPD* ptr, char* ptr_header)
;del_http_header(RESPD* ptr, char* ptr_header) ; no del std header
;set_http_ver(RESPD* ptr, char* version) ; example: RTSP/1.1
send_resp rd 1
create_resp rd 1
destruct_resp rd 1
set_http_status rd 1
add_http_header rd 1
del_http_header rd 1
set_http_ver rd 1
base_response rd 1
GLOBAL_DATA rd 1
ends
struct CONNECT_DATA ; 16*4 = 64 bytes
socket dd 0 ; номер сокета подключения
sockaddr dd 16/4 ; socaddr connection
buffer_request dd 0 ; pointer to buffer for geting message socket
request_size dd 0 ; size geted data from client
end_buffer_request dd 0 ; для парсера
buffer_response dd 0 ; pointer to buffwr for resp message
http_method dd 0 ; указатель на строку
http_verion dd 0 ; указатель на строку
num_headers dd 0 ; number items in REQUEST_DATA
http_headers dd 0 ; указатель на массив REQUEST_DATA
uri_scheme dd 0 ; указатель на схему
uri_authority dd 0 ; pointer to struct ?
uri_path dd 0 ; указатель на декодированный путь к ресурсу(без параметров)
num_uri_args dd 0 ;
uri_arg dd 0 ; pointer to array REQUEST_DATA аргументов uri строк
uri_fragment dd 0 ; указатель на строку
message_body dd 0 ; указатель на тело http запроса
ends
macro board_input message {
if NO_DEBUG_INPUT = 0
local ..str, ..end
push eax ebx ecx esi
mov esi, ..str
@@:
mov cl, [esi]
mcall 63, 1
inc esi
cmp cl, 10
jne @b
jmp ..end
..str:
db message,13, 10
..end:
pop esi ecx ebx eax
end if
}
section '.flat' code readable align 16
unit_init:
mov eax, -1
push esi edi
mov esi, [esp + 4*2 + 4]
cmp dword[esi + IMPORT_DATA.version], API_VERSION
jne .exit
mov edi, IMPORT
mov ecx, [esi + IMPORT_DATA.sizeof]
shr ecx, 2 ; div 4
rep movsd
xor eax, eax
.exit:
pop edi esi
ret 4
server_entry:
push esi edi
mov esi, [esp + 4*2 + 4]
; work
board_input 'first'
cmp [esi + CONNECT_DATA.num_uri_args], 1
jb .no_args
mov eax, [esi + CONNECT_DATA.uri_arg]
mov ecx, [eax]
cmp word[ecx], 'gr'
jne .no_args
cmp byte[ecx + 2], 0
jne .no_args
mov ecx, [eax + 4]
cmp dword[ecx], 'bpo'
jne .no_bpo
board_input 'bpo'
invoke IMPORT.Alloc, sceleton_resp.size
test eax, eax
jz .exit
push esi
mov edi, eax
mov esi, sceleton_resp
mov ecx, sceleton_resp.size
rep movsb
lea edi, [eax + sceleton_resp.name]
mov esi, bpo_name
mov ecx, bpo_name.size
rep movsb
lea edi, [eax + sceleton_resp.data]
mov esi, bpo_data
mov ecx, bpo_data.size
rep movsb
pop esi
jmp .send_data
.no_bpo:
cmp dword[ecx], 'btp'
jne .err_404
board_input 'btp'
invoke IMPORT.Alloc, sceleton_resp.size
test eax, eax
jz .exit
push esi
mov edi, eax
mov esi, sceleton_resp
mov ecx, sceleton_resp.size
rep movsb
lea edi, [eax + sceleton_resp.name]
mov esi, btp_name
mov ecx, btp_name.size
rep movsb
lea edi, [eax + sceleton_resp.data]
mov esi, btp_data
mov ecx, btp_data.size
rep movsb
pop esi
jmp .send_data
.no_args:
board_input 'no_arg'
invoke IMPORT.create_resp, esi, 0
test eax, eax
jz .exit
push eax
invoke IMPORT.send_resp, eax, sceleton_resp, sceleton_resp.size
invoke IMPORT.destruct_resp ; arg in stack
.exit:
pop edi esi
ret 4
.send_data: ; eax - ptr to buffer
mov edi, eax
board_input 'create_resp'
invoke IMPORT.create_resp, esi, 0
test eax, eax
jz .exit
board_input 'send_data'
push eax
invoke IMPORT.send_resp, eax, edi, sceleton_resp.size
invoke IMPORT.destruct_resp ; arg in stack
invoke IMPORT.Free, edi
jmp .exit
.err_404:
; send resp 404
board_input 'err404'
invoke IMPORT.create_resp, esi, 0
test eax, eax
jz .exit
mov edi, eax
invoke IMPORT.set_http_status, edi, dword '404'
invoke IMPORT.send_resp, edi, 0, 0
invoke IMPORT.destruct_resp, edi
jmp .exit
section '.data' data readable writable align 16
_10: dd 10
sceleton_resp:
db '<!DOCTYPE html>'
db '<html><head><meta charset="utf-8"><title>Test Server 2</title></head>'
db '<body><ul><il><a href="?gr=bpo">bpo</a></il><br><il><a href="?gr=btp">btp</a></il></ul>'
db '<b>Название группы: </b>'
.name = $ - sceleton_resp
db ' <br><b>Экзамены:</b>'
.data = $ - sceleton_resp
db ' <br></body></html>'
.size = $ - sceleton_resp
bpo_data:
db 'Дискретка'
.size = $ - bpo_data
bpo_name:
db 'БПО09-23-21'
.size = $ - bpo_name
btp_data:
db 'Эти лохи химию сдают'
.size = $ - btp_data
btp_name:
db 'БТП-23-21'
.size = $ - btp_name
@EXPORT:
export \
unit_init, 'httpd_init', \
server_entry, 'httpd_serv'
IMPORT IMPORT_DATA ;

BIN
example/test_unit_2.obj Normal file

Binary file not shown.

View File

@@ -1,6 +1,7 @@
; This is a module for processing standard requests to get a file along
; a path that does not belong to another module.
SIZE_FILE_BUFFER = 64*1024
file_server:
@@ -90,7 +91,7 @@ file_server:
.send_file:
; create http response (set 200 code, MINE type and length of body)
;; alloc 33 kib
push dword 33*1024
push dword SIZE_FILE_BUFFER
call Alloc
test eax, eax
jz .err_http_501 ; error memory
@@ -107,6 +108,11 @@ file_server:
mov edi, [esp]
mov dword[edi + response.code], '200 '
mov dword[edi + response.connection], 'keep'
mov dword[edi + response.connection + 4], '-ali'
mov word[edi + response.connection + 8], 've'
push dword[esp + 4] ; <-FILED
call Get_MIME_Type
@@ -173,7 +179,7 @@ file_server:
mov dword[ebx + FILED.offset], 0
mov dword[ebx + FILED.offset + 4], 0
mov [ebx + FILED.buffer], edi
mov [ebx + FILED.size], 32*1024
mov [ebx + FILED.size], SIZE_FILE_BUFFER
.send_response_body:
; read 32 kib to file
push ebx
@@ -181,11 +187,13 @@ file_server:
test eax, eax
jz @f
cmp eax, 6
mov [0], eax
jne .exit_free
@@:
; send this block data
push dword 0 ; flags
push 32*1024
push SIZE_FILE_BUFFER
push edi
push dword[esi + CONNECT_DATA.socket]
call netfunc_send
@@ -202,7 +210,7 @@ file_server:
jbe .exit_free
.add_offset:
add [ebx + FILED.offset], 32*1024
add [ebx + FILED.offset], SIZE_FILE_BUFFER
adc [ebx + FILED.offset + 4], 0
jmp @b

View File

@@ -4,9 +4,11 @@
; ;
; httpd - Simple http server for Kolibri OS. ;
; ;
; Version 0.0.4, 12 November 2023 ;
; Version 0.1.0, 10 December 2023 ;
; ;
;*****************************************************************************;
API_VERSION = 0x05 ; 0.0.5
;include "macros.inc"
use32
org 0
@@ -43,7 +45,7 @@ START:
jnz .err_settings
;init server socket
push dword SO_NONBLOCK ; IPPROTO_TCP ?
push dword SO_NONBLOCK ;IPPROTO_TCP
push dword SOCK_STREAM
push dword AF_INET4
call netfunc_socket; AF_INET4, SOCK_STREAM, SO_NONBLOCK ; we dont want to block on accept
@@ -253,6 +255,8 @@ httpd_import:
dd 0
EXPORT_DATA:
dd API_VERSION
dd .size
dd netfunc_socket
dd netfunc_close
dd netfunc_bind
@@ -264,15 +268,25 @@ EXPORT_DATA:
dd FileRead
dd Alloc
dd Free
dd parse_http_query ; no stdcall
dd send_resp
dd create_resp
dd destruct_resp
dd set_http_status
dd add_http_header
dd del_http_header
dd set_http_ver
dd base_response
dd GLOBAL_DATA
.size = $ - EXPORT_DATA ; (count func)*4 + size(api ver) + 4
dd 0
; DATA
;UDATA
srv_backlog: rd 1 ; максимум одновременных подключений подключений
srv_backlog: rd 1 ; maximum number of simultaneous open connections
srv_socket: rd 1
@@ -284,7 +298,7 @@ srv_sockaddr:
.length = $ - srv_sockaddr
GLOBAL_DATA:
.units rd 1 ; указатель на двусвязный не кольцевой(null terminator) список
.units rd 1 ; pointer to a doubly connected non-cyclic list (null terminator)
; next, prev, ptr of httpd_serv(), uri path
.work_dir rb 1024 ; max size path to work directory
.work_dir.size rd 1 ; length string

BIN
httpd.kex

Binary file not shown.

View File

@@ -1,30 +1,30 @@
align 4
day:
dd 'Mon,'
dd 'Tue,'
dd 'Wed,'
dd 'Thu,'
dd 'Fri,'
dd 'Sat,'
dd 'Sun,'
.count = ($ - day) / 4
align 4
months:
dd 'Jan '
dd 'Feb '
dd 'Mar '
dd 'Apr '
dd 'May '
dd 'Jun '
dd 'Jul '
dd 'Aug '
dd 'Sep '
dd 'Oct '
dd 'Nov '
dd 'Dec '
.count = ($ - months) / 4 ; count item in this array
;align 4
;day:
; dd 'Mon,'
; dd 'Tue,'
; dd 'Wed,'
; dd 'Thu,'
; dd 'Fri,'
; dd 'Sat,'
; dd 'Sun,'
;.count = ($ - day) / 4
;
;align 4
;months:
; dd 'Jan '
; dd 'Feb '
; dd 'Mar '
; dd 'Apr '
; dd 'May '
; dd 'Jun '
; dd 'Jul '
; dd 'Aug '
; dd 'Sep '
; dd 'Oct '
; dd 'Nov '
; dd 'Dec '
;.count = ($ - months) / 4 ; count item in this array
; HTTP-date = rfc1123-date | rfc850-date | asctime-date
; rfc1123-date = wkday "," SP date1 SP time SP "GMT"
@@ -49,16 +49,16 @@ months:
serv_header:
.Accept_Ranges db 'Accept-Ranges: bytes',13,10
.connection db 'Connection: close',0
;serv_header:
; .Accept_Ranges db 'Accept-Ranges: bytes',13,10
; .connection db 'Connection: close',0
http_method:
.get: db 'GET '
.head: db 'HEAD'
.post: db 'POST'
.put: db 'PUT '
.patch db 'PATCH'
;http_method:
; .get: db 'GET '
; .head: db 'HEAD'
; .post: db 'POST'
; .put: db 'PUT '
; .patch db 'PATCH'
;error_404:
; db '<html>'
@@ -83,6 +83,9 @@ http_method:
; db 13, 10
;.size = $ - http_err_response
default_http_version: db 'HTTP/1.1 '
.length = $ - default_http_version
http_response_err_501:
db 'HTTP/1.1 '
db '501 ',13, 10
@@ -119,7 +122,7 @@ http_response_options:
base_response:
response:
db 'HTTP/1.0 '
db 'HTTP/1.1 '
.code = $ - response
db '000 ',13, 10
db 'Server: simple-httpd/0.0.1', 13, 10
@@ -134,11 +137,13 @@ response:
db '0000000000000000000000', 13, 10
db 'Content-type: '
.content_type = $ - response
db ' ', 13, 10;
db 'text/html ', 13, 10;
;'text/html; charset=utf-8'
.end_headers: ; for adding new headers, and save connection(keep-alive)
db 'Connection: '
.connection = $ - response
db 'Connection: close', 13, 10
db 'close ', 13, 10
; 'keep-alive'
.end_headers = $ - response ; for adding new headers
db 13, 10
.body = $ - response ; offset for add http body in simple response
@@ -148,7 +153,7 @@ min_http_size = 18
MIME_FILE_FORMAT:
.html: db 5,'.html', 'text/html',0
.css: db 4,'.css', 'text/css',0
.css: db 4,'.css', 'text/css ',0
.js: db 3,'.js', 'text/javascript',0
.txt: db 4,'.txt', 'text/plain',0
.pdf: db 4,'.pdf', 'application/pdf',0

View File

@@ -2,23 +2,24 @@
BASE_ARRAY_ARGS equ (esi - 1024)
BASE_ARRAY_HEADERS equ (esi - 2048)
MAX_COUNT_ARG = 1024/(4+4)
MAX_COUNT_HEADER = 1024/(4+4)
;TODO: fix checking end http packed
; IN:
; esi - struct
; ecx = ptr to str URI
; ecx - ptr to str URI
; OUT:
; ecx - new base for reading data ('HTTP/1.1 ...')
; eax -
; NOTE: this function don`t check buffer size
; ecx = new base for reading data ('HTTP/1.1 ...')
; eax = -1 - error
; 0 - good
parse_url:
; URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
;
; hier-part = "//" authority path-abempty
; / path-absolute
; / path-rootless
; / path-empty
;
; foo://example.com:8042/over/there?name=ferret#nose
; \_/ \______________/\_________/ \_________/ \__/
; | | | | |
@@ -33,6 +34,9 @@ parse_url:
;get scheme
mov [esi + CONNECT_DATA.uri_scheme], ecx
@@:
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
inc ecx
cmp byte[ecx - 1], ':'
jne @b
@@ -45,6 +49,9 @@ parse_url:
mov [esi + CONNECT_DATA.uri_authority], ecx
;get authority
@@:
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
inc ecx
;cmp byte[ecx - 1], ' ' ;check end, не нужно, так как в http всегда / абс путь
@@ -52,10 +59,14 @@ parse_url:
jne @b
dec ecx
.get_path:
;path-absolute
;ecx = path-absolute
mov [esi + CONNECT_DATA.uri_path], ecx
@@:
inc ecx
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
cmp byte[ecx], '?'
je .get_query
@@ -77,11 +88,19 @@ parse_url:
; add new item
.get_query_new_arg:
inc edx
cmp edx, MAX_COUNT_HEADER
jae .error_exit
mov dword[BASE_ARRAY_ARGS + (edx-1)*8], ecx
mov [esi + CONNECT_DATA.num_uri_args], edx
dec ecx
@@:
inc ecx
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
cmp byte[ecx], '='
je .get_args
@@ -102,6 +121,10 @@ parse_url:
dec ecx
@@:
inc ecx
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
cmp byte[ecx], '#'
je .get_fragment
@@ -124,22 +147,29 @@ parse_url:
inc ecx
mov [esi + CONNECT_DATA.uri_fragment], ecx
@@:
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
inc ecx
cmp byte[ecx - 1], ' '
jne @b
mov byte[ecx - 1], 0
.exit:
xor eax, eax
ret
.error_exit:
; set return value
mov eax, -1
ret
; IN:
; esi - struct
; ecx - ptr to begin headers block
; edx - free mem ptr
; OUT:
; ecx - new base for reading body message HTTP query
; eax -
; NOTE: this function don`t check buffer size
; ecx = new base for reading body message HTTP query
; eax = -1 - error
; 0 - good
parse_headers:
; init array
mov [esi + CONNECT_DATA.num_headers], 0
@@ -148,12 +178,10 @@ parse_headers:
xor edx, edx
; for check size
mov eax, [esi + CONNECT_DATA.request_size]
add eax, [esi + CONNECT_DATA.buffer_request]
mov eax, [esi + CONNECT_DATA.end_buffer_request]
.new_str:
cmp ecx, eax
jae .exit
jae .error_exit
cmp word[ecx], 0x0A0D ; \n
jnz .find_header
@@ -161,12 +189,16 @@ parse_headers:
; end find heeaders
mov byte[ecx], 0
add ecx, 2 ; ecx = base for body message
xor eax, eax
ret
.error_exit:
mov eax, -1
ret
.find_header:
; add new item in array headers
cmp edx, 512 ; max count headers
jae .exit
cmp edx, MAX_COUNT_HEADER
jae .error_exit
inc edx
mov dword[esi + CONNECT_DATA.num_headers], edx
@@ -175,9 +207,9 @@ parse_headers:
dec ecx
@@:
inc ecx
; check size
cmp ecx, eax
jae .exit
jae .error_exit
cmp byte[ecx], ':'
jnz @b
@@ -187,12 +219,14 @@ parse_headers:
; save pointer to value
mov dword[BASE_ARRAY_HEADERS + (edx-1)*8 + 4], ecx
@@:
; check size
cmp ecx, eax
jae .exit
jae .error_exit
inc ecx
cmp word[ecx - 1], 0x0A0D
jnz @b
mov byte[ecx - 1], 0
inc ecx ; set offset on new string
jmp .new_str
@@ -230,15 +264,15 @@ parse_http_query:
mov byte[ecx - 1], 0
; check size
mov edx, ecx
sub edx, [esi + CONNECT_DATA.buffer_request]
sub edx, 2 ; / 0x20
cmp dword[esi + CONNECT_DATA.request_size], edx
jle .error_exit
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
; ecx <- uri string
; парсинг uri строки в заголовке запроса(получение схемы, пути аргументов, фрагмента и тд)
; parsing the URI string in the start line of the query
; (getting the schema, path, arguments, fragment, etc.)
call parse_url
test eax, eax
jnz .error_exit
; get http version(HTTP/1.1)
mov [esi + CONNECT_DATA.http_verion], ecx
@@ -261,7 +295,8 @@ parse_http_query:
; get headers request (key + value string)
call parse_headers
test eax, eax
jnz .error_exit
; check size
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]

42
readme.md Normal file
View File

@@ -0,0 +1,42 @@
# simple_http
Это небольшой http-сервер для Колибри ОС позволяющий хостить статичные сайты и создавать модули, для динамической генерации отправляемых клиенту данных.
Сервер отправляет содержимое файлов без сжатия в соответствии с заданной таблицей ассоциации MIME типа и расширения файла.
Если запрос от клиента имеет uri путь который соответствует модулю сервера, то сервер передаёт управление коду в этом модуле с передачей всех необходимых для функционирования данных.
## Configuration
Для настройки сервера применяется файл конфигурации в формате ini, где указываются следующие параметры:
В секции <CODE>MAIN</CODE>
- <CODE>ip</CODE> - ip адрес сервера
- <CODE>port</CODE> - порт для подключения (по умолчанию 80)
- <CODE>conn</CODE> - максимальное количество открытых соединений(по умолчанию 10)
- <CODE>work_dir</CODE> - директория для размещения файлов, отправляемых сервером
- <CODE>mime_file</CODE> - путь к файлу с таблицей сопоставлениея mime типов и расширений файлов (если не указан, то используется встроенная в сервер таблица сопоставления)
- <CODE>unit_dir</CODE> - директория расположения модулей сервера
В секции <CODE>UNITS</CODE> может находиться множество параметров, имеющих вид <CODE>uri_path=file_name</CODE> где:
- <CODE>uri_path</CODE> - путь указываемый клиентом во время запроса
- <CODE>file_name</CODE> - название/путь до файла модуля относительно <CODE>work_dir</CODE>
## API for units
К серверу можно подключить дополнительные модули в виде библиотек со специальными экспортируемыми функциями:
- <CODE>uint32_t stdcall httpd_init(IMPORT_DATA* import)</CODE>
Эта функция необходима для передачи модулю необходимых данных, таких как функции работы с сетью, рабочие директории и тд.
Если инициализация модуля прошла успешно, функция возвращает 0.
- <CODE>void stdcall httpd_server(CONNECT_DATA* request_data)</CODE>
Эта функция вызывается при получении запроса с uri путём указанном в файле конфигурации для этого модуля. Сервер передаёт в функцию структуру соединения, в которой находятся данные запроса(заголовки, параметры, http метод и версия). На основе этих данных функция может генерировать необходимый ответ.
## Bugs
- В ходе тестов был обнаружена ошибка отправки "больших" файлов. Это баг сетевого стека.
- При длительной работе сервер может начать "подзависать" или перестать отвечать на сообщения. Это баг сетевого стека.

View File

@@ -27,6 +27,22 @@ struct HTTPD_UNIT
uri_path rb 4096-3*4
ends
struct RESPD
session rd 1
flags rd 1
http_status rd 1
http_ver_ptr rd 1
http_ver_len rd 1
buffer rd 1
buffer_size rd 1
http_body rd 1
count_header rd 1
header.ptr rd 1
header.len rd 1
rd 2*(64 - 1)
ends
struct REQUEST_DATA
ptr_name dd 0 ;
ptr_data dd 0 ;
@@ -41,6 +57,8 @@ load_settings:
sub esp, 16
mov esi, esp
invoke ini.get_str, ebp, ini_section_main, ini_key_ip, esi, 16, 0 ; ip
test eax, eax
jnz .error_exit2
; xxx.xxx.xxx.xxx\n - 16 byte max
xor edx, edx
xor eax, eax
@@ -77,6 +95,9 @@ load_settings:
; work_dir
invoke ini.get_str, ebp, ini_section_main, ini_key_work_dir, GLOBAL_DATA.work_dir, 1024, 0
test eax, eax
jnz .error_exit
push edi
mov ecx, 1024
mov edi, GLOBAL_DATA.work_dir
@@ -87,12 +108,14 @@ load_settings:
mov [GLOBAL_DATA.work_dir.size], edi
pop edi
; TODO: get mime file
;mov dword[GLOBAL_DATA.MIME_types_arr], STD_MIME_TYPE_ARR
; get mime file
call load_mime_file
; units_dir
invoke ini.get_str, ebp, ini_section_main, ini_key_units_dir, GLOBAL_DATA.unit_dir, 1024, 0
test eax, eax
jnz .no_units
push edi
mov ecx, 1024
mov edi, GLOBAL_DATA.unit_dir
@@ -104,7 +127,7 @@ load_settings:
; get all units
invoke ini.enum_keys, ebp, ini_section_units, .add_unit
.no_units:
xor eax, eax
ret
@@ -176,7 +199,10 @@ load_settings:
.err:
.error_exit2:
add esp, 16
.error_exit:
mov eax, -1
ret

View File

@@ -215,3 +215,240 @@ FileRead:
mcall 70
pop ebx
ret 4
FLAG_KEEP_ALIVE = 0x01
FLAG_ADD_DATE = 0x02 ;(not supported)
FLAG_NO_SET_CACHE = 0x04 ;(not supported)
FLAG_NO_CONTENT_ENCODING = 0x08 ;(not supported)
;RESPD* stdcall create_resp(CONNECT_DATA* session, uint32_t flags)
create_resp:
stdcall Alloc, sizeof.RESPD
test eax, eax
jz .exit
mov edx, [esp + 4]
mov ecx, [esp + 8]
mov [eax + RESPD.flags], ecx
mov [eax + RESPD.session], edx
; set default values
mov [eax + RESPD.http_status], '200 '
mov [eax + RESPD.http_ver_ptr], default_http_version
mov [eax + RESPD.http_ver_len], default_http_version.length
mov [eax + RESPD.count_header], 0
.exit:
ret 8
;void destruct_resp(RESPD* ptr)
destruct_resp:
stdcall Free, [esp + 4]
ret 4
;void set_http_status(RESPD* ptr, uint32_t status) // status in '200' format
set_http_status:
mov eax, [esp + 4]
mov ecx, [esp + 8]
and ecx, 0x00ffffff ; clear 3 byte register
add ecx, 0x20 shl 24 ; set 3 byte in ' '
mov [eax + RESPD.http_status], ecx
ret 8
;void set_http_ver(RESPD* ptr, char* version, uint32_t length) // example: 'RTSP/1.1 '
set_http_ver:
mov eax, [esp + 4]
mov ecx, [esp + 8]
mov edx, [esp + 12]
mov [eax + RESPD.http_ver_ptr], ecx
mov [eax + RESPD.http_ver_len], edx
ret 12
;uint32_t add_http_header(RESPD* ptr, char* ptr_header, uint32_t length)
add_http_header:
mov eax, [esp + 4]
mov ecx, [eax + RESPD.count_header]
cmp ecx, 64
jz .err
inc dword[eax + RESPD.count_header]
mov edx, [esp + 8]
mov [eax + ecx*8 + RESPD.header.ptr], edx
mov edx, [esp + 12]
mov [eax + ecx*8 + RESPD.header.len], edx
mov eax, ecx
ret 12
.err:
mov eax, -1
ret 12
;uint32_t del_http_header(RESPD* ptr, char* ptr_header) // no del std header
del_http_header:
mov eax, [esp + 4]
mov edx, [esp + 8]
mov ecx, [eax + RESPD.count_header]
add eax, RESPD.header.ptr
@@:
test ecx, ecx
jz .err
cmp [eax], edx
je .found
add eax, 8
dec ecx
jmp @b
.found:
dec ecx ; skiip one item
@@:
test ecx, ecx
jz .exit
mov edx, [eax + 8]
mov [eax], edx
mov edx, [eax + 8 + 4]
mov [eax + 4], edx
add eax, 8
jmp @b
.exit:
mov ecx, [esp + 4]
inc dword[ecx + RESPD.count_header]
ret 8
.err:
mov eax, -1
ret 8
;uint32_t send_resp(RESPD* ptr, char* content, uint32_t length)
send_resp:
push esi edi ebp
; get full size
mov ebp, [esp + 4*3 + 4]
mov ecx, [ebp + RESPD.count_header]
shl ecx, 1 ; *2
add ecx, 2 ; finish 0x0d 0x0a
add ecx, [ebp + RESPD.http_ver_len]
; add size all headers
xor edx, edx
@@:
cmp edx, [ebp + RESPD.count_header]
je @f
add ecx, [ebp + edx*8 + RESPD.header.len]
inc edx
jmp @b
@@:
add ecx, response.end_headers - response.code
; add size content
;add ecx, [esp + 4*3 + 12]
; alloc buffer
push ecx
stdcall Alloc, ecx
pop ecx
test eax, eax
jz .error_alloc
mov [ebp + RESPD.buffer], eax
mov [ebp + RESPD.buffer_size], ecx
mov edi, eax
; copy data
mov ecx, [ebp + RESPD.http_ver_len]
mov esi, [ebp + RESPD.http_ver_ptr]
rep movsb ; copy http ver
mov esi, base_response + response.code
mov ecx, response.end_headers - response.code
mov edx, edi
rep movsb ; copy default status code + headers
mov eax, [ebp + RESPD.http_status]
mov [edx], eax
test [ebp + RESPD.flags], FLAG_KEEP_ALIVE
jz @f
push edx
add edx, response.connection - response.code
mov dword[edx], 'keep'
mov dword[edx + 4], '-ali'
mov word[edx + 8], 've'
pop edx
@@:
add edx, response.content_len + 21 - response.code
mov ecx, edx
; set content length
mov eax, [esp + 4*3 + 12]
@@:
xor edx, edx
test eax, eax
jz @f
div dword[_DIV_10_]
add byte[ecx], dl
dec ecx
jmp @b
@@:
; copy addition headers
lea eax, [ebp + RESPD.header.ptr]
xor edx, edx
@@:
cmp edx, [ebp + RESPD.count_header]
je @f
mov esi, [eax]
mov ecx, [eax + 4]
rep movsb
mov word[edi], 0x0a0d
add edi, 2
add eax, 8
inc edx
jmp @b
@@:
mov ax, 0x0A0D
stosw
; send response status line and headers
mov eax, [ebp + RESPD.session]
push dword 0
push dword[ebp + RESPD.buffer_size]
push dword[ebp + RESPD.buffer]
push dword[eax + CONNECT_DATA.socket]
call netfunc_send
cmp eax, -1
jz .exit
; send content
mov ecx, [esp + 4*3 + 8] ; ptr
test ecx, ecx
jz .free
mov eax, [ebp + RESPD.session]
push dword 0
push dword[esp + 4*4 + 12] ; size
push ecx
push dword[eax + CONNECT_DATA.socket]
call netfunc_send
cmp eax, -1
jz .exit
.free:
xor eax, eax
; free buffer
push eax
stdcall Free, [ebp + RESPD.buffer]
pop eax
.exit:
pop ebp edi esi
ret 12
.error_alloc:
mov eax, -1
jmp .exit