diff --git a/example/httpd.ini b/example/httpd.ini index 1715a62..0ab3f65 100644 --- a/example/httpd.ini +++ b/example/httpd.ini @@ -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 diff --git a/example/test_unit.asm b/example/test_unit.asm index d779424..e83f870 100644 --- a/example/test_unit.asm +++ b/example/test_unit.asm @@ -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 ; \ No newline at end of file diff --git a/example/test_unit.obj b/example/test_unit.obj index 0caa622..d270688 100644 Binary files a/example/test_unit.obj and b/example/test_unit.obj differ diff --git a/example/test_unit_2.asm b/example/test_unit_2.asm new file mode 100644 index 0000000..24fcb8a --- /dev/null +++ b/example/test_unit_2.asm @@ -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 '' + db 'Test Server 2' + db '' + db 'Название группы: ' +.name = $ - sceleton_resp + db '
Экзамены:' +.data = $ - sceleton_resp + db '
' +.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 ; \ No newline at end of file diff --git a/example/test_unit_2.obj b/example/test_unit_2.obj new file mode 100644 index 0000000..04c1771 Binary files /dev/null and b/example/test_unit_2.obj differ diff --git a/file_server.inc b/file_server.inc index 2b3c5e5..f32a846 100644 --- a/file_server.inc +++ b/file_server.inc @@ -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 diff --git a/httpd.asm b/httpd.asm index fd3a0b6..1ee4200 100644 --- a/httpd.asm +++ b/httpd.asm @@ -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 diff --git a/httpd.kex b/httpd.kex index 5fb6720..eda8712 100644 Binary files a/httpd.kex and b/httpd.kex differ diff --git a/httpd_lib.inc b/httpd_lib.inc index 06ea312..338a76b 100644 --- a/httpd_lib.inc +++ b/httpd_lib.inc @@ -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 '' @@ -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) -.connection = $ - response - db 'Connection: close', 13, 10 + db 'Connection: ' +.connection = $ - response + 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,15 +153,15 @@ min_http_size = 18 MIME_FILE_FORMAT: .html: db 5,'.html', 'text/html',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 +.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 .json: db 5,'.json', 'application/json',0 -.png: db 4,'.png', 'image/png',0 -.mp3: db 4,'.mp3', 'audio/mpeg',0 -.mp4: db 4,'.mp4', 'video/mp4',0 +.png: db 4,'.png', 'image/png',0 +.mp3: db 4,'.mp3', 'audio/mpeg',0 +.mp4: db 4,'.mp4', 'video/mp4',0 .other: dd 0 db 'application/octet-stream',0 ; for unknow file - all file :) diff --git a/parser.inc b/parser.inc index 3c1f93b..40d9585 100644 --- a/parser.inc +++ b/parser.inc @@ -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 @@ -44,7 +48,10 @@ parse_url: add ecx, 2 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 @@ -201,7 +235,7 @@ parse_headers: ; ecx - raw data query ; esi - ptr to CONNECT_DATA ; OUT: eax = 0 error -; eax = prt to struct CONNECT_DATA +; eax = prt to struct CONNECT_DATA parse_http_query: ;method scheme://host:port/abs_path HTTP/1.1 0x0d 0x0a ;header_1:value 0x0d 0x0a @@ -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] diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..bfdc664 --- /dev/null +++ b/readme.md @@ -0,0 +1,42 @@ + +# simple_http +Это небольшой http-сервер для Колибри ОС позволяющий хостить статичные сайты и создавать модули, для динамической генерации отправляемых клиенту данных. + +Сервер отправляет содержимое файлов без сжатия в соответствии с заданной таблицей ассоциации MIME типа и расширения файла. +Если запрос от клиента имеет uri путь который соответствует модулю сервера, то сервер передаёт управление коду в этом модуле с передачей всех необходимых для функционирования данных. + + +## Configuration +Для настройки сервера применяется файл конфигурации в формате ini, где указываются следующие параметры: + +В секции MAIN + - ip - ip адрес сервера + - port - порт для подключения (по умолчанию 80) + - conn - максимальное количество открытых соединений(по умолчанию 10) + - work_dir - директория для размещения файлов, отправляемых сервером + - mime_file - путь к файлу с таблицей сопоставлениея mime типов и расширений файлов (если не указан, то используется встроенная в сервер таблица сопоставления) + - unit_dir - директория расположения модулей сервера + + В секции UNITS может находиться множество параметров, имеющих вид uri_path=file_name где: + - uri_path - путь указываемый клиентом во время запроса + - file_name - название/путь до файла модуля относительно work_dir + + + +## API for units + +К серверу можно подключить дополнительные модули в виде библиотек со специальными экспортируемыми функциями: + + - uint32_t stdcall httpd_init(IMPORT_DATA* import) + +Эта функция необходима для передачи модулю необходимых данных, таких как функции работы с сетью, рабочие директории и тд. +Если инициализация модуля прошла успешно, функция возвращает 0. + + - void stdcall httpd_server(CONNECT_DATA* request_data) +Эта функция вызывается при получении запроса с uri путём указанном в файле конфигурации для этого модуля. Сервер передаёт в функцию структуру соединения, в которой находятся данные запроса(заголовки, параметры, http метод и версия). На основе этих данных функция может генерировать необходимый ответ. + + +## Bugs + + - В ходе тестов был обнаружена ошибка отправки "больших" файлов. Это баг сетевого стека. + - При длительной работе сервер может начать "подзависать" или перестать отвечать на сообщения. Это баг сетевого стека. diff --git a/settings.inc b/settings.inc index 05a59f8..745f742 100644 --- a/settings.inc +++ b/settings.inc @@ -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 @@ -76,7 +94,10 @@ load_settings: ; flags ; 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 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 diff --git a/sys_func.inc b/sys_func.inc index b01e78d..d4da36c 100644 --- a/sys_func.inc +++ b/sys_func.inc @@ -214,4 +214,241 @@ FileRead: mov dword[ebx], 0 ; read file mcall 70 pop ebx - ret 4 \ No newline at end of file + 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 +