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 '
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
+