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 ; list units
; path = path to lib in units_dir ; path = path to lib in units_dir
test=test_unit.obj test=test_unit.obj
rasp=test_unit_2.obj
;database/sqlite3=sqlite3_serv.obj ;database/sqlite3=sqlite3_serv.obj
;database/cvs=cvs_table_server.obj ;database/cvs=cvs_table_server.obj

View File

@@ -1,10 +1,15 @@
format MS COFF format MS COFF
public @EXPORT as 'EXPORTS' 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' 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_socket rd 1
netfunc_close rd 1 netfunc_close rd 1
netfunc_bind rd 1 netfunc_bind rd 1
@@ -16,6 +21,26 @@ struct EXPORT_DATA
FileRead rd 1 FileRead rd 1
Alloc rd 1 Alloc rd 1
Free 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 base_response rd 1
GLOBAL_DATA rd 1 GLOBAL_DATA rd 1
@@ -64,13 +89,25 @@ end if
section '.flat' code readable align 16 section '.flat' code readable align 16
unit_init: unit_init:
mov eax, -1
push esi edi
mov esi, [esp + 4*2 + 4]
mov [import_httpd], esi
mov eax, [esp + 4] cmp dword[esi + IMPORT_DATA.version], API_VERSION
mov [import_httpd], eax jne .exit
mov edi, IMPORT
mov ecx, [esi + IMPORT_DATA.sizeof]
shr ecx, 2 ; div 4
rep movsd
xor eax, eax xor eax, eax
.exit:
pop edi esi
ret 4 ret 4
server_entry: server_entry:
push esi edi push esi edi
mov esi, [esp + 4*2 + 4] mov esi, [esp + 4*2 + 4]
@@ -106,6 +143,11 @@ server_entry:
cmp dword[ecx], 'txt' cmp dword[ecx], 'txt'
jne .no_args 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 push esi edi
mov esi, [eax + 12] mov esi, [eax + 12]
mov edi, text_message mov edi, text_message
@@ -123,8 +165,7 @@ server_entry:
board_input 'create message' board_input 'create message'
; create http message ; create http message
push dword 8*1024 push dword 8*1024
mov eax, [import_httpd] call [IMPORT + IMPORT_DATA.Alloc]
call [eax + EXPORT_DATA.Alloc]
test eax, eax test eax, eax
jz .exit jz .exit
@@ -168,13 +209,12 @@ server_entry:
; set httpcode ; set httpcode
mov dword[edi + sceleton_resp.code], '200 ' mov dword[edi + sceleton_resp.code], '200 '
; send http message ; send http message
mov ecx, [import_httpd]
push dword 0 ; flags push dword 0 ; flags
push sceleton_resp.size push sceleton_resp.size
push edi push edi
push dword[esi + CONNECT_DATA.socket] push dword[esi + CONNECT_DATA.socket]
call [ecx + EXPORT_DATA.netfunc_send] call [IMPORT + IMPORT_DATA.netfunc_send]
board_input 'send' board_input 'send'
.exit: .exit:
@@ -224,3 +264,6 @@ text_message:
export \ export \
unit_init, 'httpd_init', \ unit_init, 'httpd_init', \
server_entry, 'httpd_serv' 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 ; This is a module for processing standard requests to get a file along
; a path that does not belong to another module. ; a path that does not belong to another module.
SIZE_FILE_BUFFER = 64*1024
file_server: file_server:
@@ -90,7 +91,7 @@ file_server:
.send_file: .send_file:
; create http response (set 200 code, MINE type and length of body) ; create http response (set 200 code, MINE type and length of body)
;; alloc 33 kib ;; alloc 33 kib
push dword 33*1024 push dword SIZE_FILE_BUFFER
call Alloc call Alloc
test eax, eax test eax, eax
jz .err_http_501 ; error memory jz .err_http_501 ; error memory
@@ -107,6 +108,11 @@ file_server:
mov edi, [esp] mov edi, [esp]
mov dword[edi + response.code], '200 ' 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 push dword[esp + 4] ; <-FILED
call Get_MIME_Type call Get_MIME_Type
@@ -173,7 +179,7 @@ file_server:
mov dword[ebx + FILED.offset], 0 mov dword[ebx + FILED.offset], 0
mov dword[ebx + FILED.offset + 4], 0 mov dword[ebx + FILED.offset + 4], 0
mov [ebx + FILED.buffer], edi mov [ebx + FILED.buffer], edi
mov [ebx + FILED.size], 32*1024 mov [ebx + FILED.size], SIZE_FILE_BUFFER
.send_response_body: .send_response_body:
; read 32 kib to file ; read 32 kib to file
push ebx push ebx
@@ -181,11 +187,13 @@ file_server:
test eax, eax test eax, eax
jz @f jz @f
cmp eax, 6 cmp eax, 6
mov [0], eax
jne .exit_free jne .exit_free
@@: @@:
; send this block data ; send this block data
push dword 0 ; flags push dword 0 ; flags
push 32*1024 push SIZE_FILE_BUFFER
push edi push edi
push dword[esi + CONNECT_DATA.socket] push dword[esi + CONNECT_DATA.socket]
call netfunc_send call netfunc_send
@@ -202,7 +210,7 @@ file_server:
jbe .exit_free jbe .exit_free
.add_offset: .add_offset:
add [ebx + FILED.offset], 32*1024 add [ebx + FILED.offset], SIZE_FILE_BUFFER
adc [ebx + FILED.offset + 4], 0 adc [ebx + FILED.offset + 4], 0
jmp @b jmp @b

View File

@@ -4,9 +4,11 @@
; ; ; ;
; httpd - Simple http server for Kolibri OS. ; ; 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" ;include "macros.inc"
use32 use32
org 0 org 0
@@ -43,7 +45,7 @@ START:
jnz .err_settings jnz .err_settings
;init server socket ;init server socket
push dword SO_NONBLOCK ; IPPROTO_TCP ? push dword SO_NONBLOCK ;IPPROTO_TCP
push dword SOCK_STREAM push dword SOCK_STREAM
push dword AF_INET4 push dword AF_INET4
call netfunc_socket; AF_INET4, SOCK_STREAM, SO_NONBLOCK ; we dont want to block on accept call netfunc_socket; AF_INET4, SOCK_STREAM, SO_NONBLOCK ; we dont want to block on accept
@@ -253,6 +255,8 @@ httpd_import:
dd 0 dd 0
EXPORT_DATA: EXPORT_DATA:
dd API_VERSION
dd .size
dd netfunc_socket dd netfunc_socket
dd netfunc_close dd netfunc_close
dd netfunc_bind dd netfunc_bind
@@ -264,15 +268,25 @@ EXPORT_DATA:
dd FileRead dd FileRead
dd Alloc dd Alloc
dd Free 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 base_response
dd GLOBAL_DATA dd GLOBAL_DATA
.size = $ - EXPORT_DATA ; (count func)*4 + size(api ver) + 4
dd 0 dd 0
; DATA ; DATA
;UDATA ;UDATA
srv_backlog: rd 1 ; максимум одновременных подключений подключений srv_backlog: rd 1 ; maximum number of simultaneous open connections
srv_socket: rd 1 srv_socket: rd 1
@@ -284,7 +298,7 @@ srv_sockaddr:
.length = $ - srv_sockaddr .length = $ - srv_sockaddr
GLOBAL_DATA: 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 ; next, prev, ptr of httpd_serv(), uri path
.work_dir rb 1024 ; max size path to work directory .work_dir rb 1024 ; max size path to work directory
.work_dir.size rd 1 ; length string .work_dir.size rd 1 ; length string

BIN
httpd.kex

Binary file not shown.

View File

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

View File

@@ -2,23 +2,24 @@
BASE_ARRAY_ARGS equ (esi - 1024) BASE_ARRAY_ARGS equ (esi - 1024)
BASE_ARRAY_HEADERS equ (esi - 2048) BASE_ARRAY_HEADERS equ (esi - 2048)
MAX_COUNT_ARG = 1024/(4+4)
MAX_COUNT_HEADER = 1024/(4+4)
;TODO: fix checking end http packed ;TODO: fix checking end http packed
; IN: ; IN:
; esi - struct ; esi - struct
; ecx = ptr to str URI ; ecx - ptr to str URI
; OUT: ; OUT:
; ecx - new base for reading data ('HTTP/1.1 ...') ; ecx = new base for reading data ('HTTP/1.1 ...')
; eax - ; eax = -1 - error
; NOTE: this function don`t check buffer size ; 0 - good
parse_url: parse_url:
; URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] ; URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
;
; hier-part = "//" authority path-abempty ; hier-part = "//" authority path-abempty
; / path-absolute ; / path-absolute
; / path-rootless ; / path-rootless
; / path-empty ; / path-empty
;
; foo://example.com:8042/over/there?name=ferret#nose ; foo://example.com:8042/over/there?name=ferret#nose
; \_/ \______________/\_________/ \_________/ \__/ ; \_/ \______________/\_________/ \_________/ \__/
; | | | | | ; | | | | |
@@ -33,6 +34,9 @@ parse_url:
;get scheme ;get scheme
mov [esi + CONNECT_DATA.uri_scheme], ecx mov [esi + CONNECT_DATA.uri_scheme], ecx
@@: @@:
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
inc ecx inc ecx
cmp byte[ecx - 1], ':' cmp byte[ecx - 1], ':'
jne @b jne @b
@@ -44,7 +48,10 @@ parse_url:
add ecx, 2 add ecx, 2
mov [esi + CONNECT_DATA.uri_authority], ecx mov [esi + CONNECT_DATA.uri_authority], ecx
;get authority ;get authority
@@: @@:
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
inc ecx inc ecx
;cmp byte[ecx - 1], ' ' ;check end, не нужно, так как в http всегда / абс путь ;cmp byte[ecx - 1], ' ' ;check end, не нужно, так как в http всегда / абс путь
@@ -52,10 +59,14 @@ parse_url:
jne @b jne @b
dec ecx dec ecx
.get_path: .get_path:
;path-absolute ;ecx = path-absolute
mov [esi + CONNECT_DATA.uri_path], ecx mov [esi + CONNECT_DATA.uri_path], ecx
@@: @@:
inc ecx inc ecx
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
cmp byte[ecx], '?' cmp byte[ecx], '?'
je .get_query je .get_query
@@ -77,11 +88,19 @@ parse_url:
; add new item ; add new item
.get_query_new_arg: .get_query_new_arg:
inc edx inc edx
cmp edx, MAX_COUNT_HEADER
jae .error_exit
mov dword[BASE_ARRAY_ARGS + (edx-1)*8], ecx mov dword[BASE_ARRAY_ARGS + (edx-1)*8], ecx
mov [esi + CONNECT_DATA.num_uri_args], edx mov [esi + CONNECT_DATA.num_uri_args], edx
dec ecx dec ecx
@@: @@:
inc ecx inc ecx
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
cmp byte[ecx], '=' cmp byte[ecx], '='
je .get_args je .get_args
@@ -102,6 +121,10 @@ parse_url:
dec ecx dec ecx
@@: @@:
inc ecx inc ecx
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
cmp byte[ecx], '#' cmp byte[ecx], '#'
je .get_fragment je .get_fragment
@@ -124,22 +147,29 @@ parse_url:
inc ecx inc ecx
mov [esi + CONNECT_DATA.uri_fragment], ecx mov [esi + CONNECT_DATA.uri_fragment], ecx
@@: @@:
cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
ja .error_exit
inc ecx inc ecx
cmp byte[ecx - 1], ' ' cmp byte[ecx - 1], ' '
jne @b jne @b
mov byte[ecx - 1], 0 mov byte[ecx - 1], 0
.exit: .exit:
xor eax, eax
ret ret
.error_exit:
; set return value
mov eax, -1
ret
; IN: ; IN:
; esi - struct ; esi - struct
; ecx - ptr to begin headers block ; ecx - ptr to begin headers block
; edx - free mem ptr
; OUT: ; OUT:
; ecx - new base for reading body message HTTP query ; ecx = new base for reading body message HTTP query
; eax - ; eax = -1 - error
; NOTE: this function don`t check buffer size ; 0 - good
parse_headers: parse_headers:
; init array ; init array
mov [esi + CONNECT_DATA.num_headers], 0 mov [esi + CONNECT_DATA.num_headers], 0
@@ -148,12 +178,10 @@ parse_headers:
xor edx, edx xor edx, edx
; for check size ; for check size
mov eax, [esi + CONNECT_DATA.request_size] mov eax, [esi + CONNECT_DATA.end_buffer_request]
add eax, [esi + CONNECT_DATA.buffer_request]
.new_str: .new_str:
cmp ecx, eax cmp ecx, eax
jae .exit jae .error_exit
cmp word[ecx], 0x0A0D ; \n cmp word[ecx], 0x0A0D ; \n
jnz .find_header jnz .find_header
@@ -161,12 +189,16 @@ parse_headers:
; end find heeaders ; end find heeaders
mov byte[ecx], 0 mov byte[ecx], 0
add ecx, 2 ; ecx = base for body message add ecx, 2 ; ecx = base for body message
xor eax, eax
ret ret
.error_exit:
mov eax, -1
ret
.find_header: .find_header:
; add new item in array headers ; add new item in array headers
cmp edx, 512 ; max count headers cmp edx, MAX_COUNT_HEADER
jae .exit jae .error_exit
inc edx inc edx
mov dword[esi + CONNECT_DATA.num_headers], edx mov dword[esi + CONNECT_DATA.num_headers], edx
@@ -175,9 +207,9 @@ parse_headers:
dec ecx dec ecx
@@: @@:
inc ecx inc ecx
; check size
cmp ecx, eax cmp ecx, eax
jae .exit jae .error_exit
cmp byte[ecx], ':' cmp byte[ecx], ':'
jnz @b jnz @b
@@ -187,12 +219,14 @@ parse_headers:
; save pointer to value ; save pointer to value
mov dword[BASE_ARRAY_HEADERS + (edx-1)*8 + 4], ecx mov dword[BASE_ARRAY_HEADERS + (edx-1)*8 + 4], ecx
@@: @@:
; check size
cmp ecx, eax cmp ecx, eax
jae .exit jae .error_exit
inc ecx inc ecx
cmp word[ecx - 1], 0x0A0D cmp word[ecx - 1], 0x0A0D
jnz @b jnz @b
mov byte[ecx - 1], 0 mov byte[ecx - 1], 0
inc ecx ; set offset on new string inc ecx ; set offset on new string
jmp .new_str jmp .new_str
@@ -201,7 +235,7 @@ parse_headers:
; ecx - raw data query ; ecx - raw data query
; esi - ptr to CONNECT_DATA ; esi - ptr to CONNECT_DATA
; OUT: eax = 0 error ; OUT: eax = 0 error
; eax = prt to struct CONNECT_DATA ; eax = prt to struct CONNECT_DATA
parse_http_query: parse_http_query:
;method scheme://host:port/abs_path HTTP/1.1 0x0d 0x0a ;method scheme://host:port/abs_path HTTP/1.1 0x0d 0x0a
;header_1:value 0x0d 0x0a ;header_1:value 0x0d 0x0a
@@ -230,15 +264,15 @@ parse_http_query:
mov byte[ecx - 1], 0 mov byte[ecx - 1], 0
; check size ; check size
mov edx, ecx cmp ecx, [esi + CONNECT_DATA.end_buffer_request]
sub edx, [esi + CONNECT_DATA.buffer_request] ja .error_exit
sub edx, 2 ; / 0x20
cmp dword[esi + CONNECT_DATA.request_size], edx
jle .error_exit
; ecx <- uri string ; 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 call parse_url
test eax, eax
jnz .error_exit
; get http version(HTTP/1.1) ; get http version(HTTP/1.1)
mov [esi + CONNECT_DATA.http_verion], ecx mov [esi + CONNECT_DATA.http_verion], ecx
@@ -261,7 +295,8 @@ parse_http_query:
; get headers request (key + value string) ; get headers request (key + value string)
call parse_headers call parse_headers
test eax, eax
jnz .error_exit
; check size ; check size
cmp ecx, [esi + CONNECT_DATA.end_buffer_request] 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 uri_path rb 4096-3*4
ends 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 struct REQUEST_DATA
ptr_name dd 0 ; ptr_name dd 0 ;
ptr_data dd 0 ; ptr_data dd 0 ;
@@ -41,6 +57,8 @@ load_settings:
sub esp, 16 sub esp, 16
mov esi, esp mov esi, esp
invoke ini.get_str, ebp, ini_section_main, ini_key_ip, esi, 16, 0 ; ip 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 ; xxx.xxx.xxx.xxx\n - 16 byte max
xor edx, edx xor edx, edx
xor eax, eax xor eax, eax
@@ -76,7 +94,10 @@ load_settings:
; flags ; flags
; work_dir ; work_dir
invoke ini.get_str, ebp, ini_section_main, ini_key_work_dir, GLOBAL_DATA.work_dir, 1024, 0 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 push edi
mov ecx, 1024 mov ecx, 1024
mov edi, GLOBAL_DATA.work_dir mov edi, GLOBAL_DATA.work_dir
@@ -87,12 +108,14 @@ load_settings:
mov [GLOBAL_DATA.work_dir.size], edi mov [GLOBAL_DATA.work_dir.size], edi
pop edi pop edi
; TODO: get mime file ; get mime file
;mov dword[GLOBAL_DATA.MIME_types_arr], STD_MIME_TYPE_ARR
call load_mime_file call load_mime_file
; units_dir ; units_dir
invoke ini.get_str, ebp, ini_section_main, ini_key_units_dir, GLOBAL_DATA.unit_dir, 1024, 0 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 push edi
mov ecx, 1024 mov ecx, 1024
mov edi, GLOBAL_DATA.unit_dir mov edi, GLOBAL_DATA.unit_dir
@@ -104,7 +127,7 @@ load_settings:
; get all units ; get all units
invoke ini.enum_keys, ebp, ini_section_units, .add_unit invoke ini.enum_keys, ebp, ini_section_units, .add_unit
.no_units:
xor eax, eax xor eax, eax
ret ret
@@ -176,7 +199,10 @@ load_settings:
.err: .error_exit2:
add esp, 16
.error_exit:
mov eax, -1
ret ret

View File

@@ -214,4 +214,241 @@ FileRead:
mov dword[ebx], 0 ; read file mov dword[ebx], 0 ; read file
mcall 70 mcall 70
pop ebx pop ebx
ret 4 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