diff --git a/bin/httpd b/bin/httpd index b1b9943..cb5d053 100644 Binary files a/bin/httpd and b/bin/httpd differ diff --git a/bin/httpd.ini b/bin/httpd.ini index f7ef16e..c43bb95 100644 --- a/bin/httpd.ini +++ b/bin/httpd.ini @@ -23,5 +23,7 @@ mime_file=/usbhd0/3/mime_types.bin ; not uning ' ' in unit_name;cmdline : "path = unit_name ; cmdline" is bad name test=test_unit.obj rasp=test_unit_2.obj +test5=test_unit5.obj +admin=srv_control.obj;123456 ;database/sqlite3=sqlite3_serv.obj ;database/cvs=cvs_table_server.obj \ No newline at end of file diff --git a/bin/modules/srv_control.obj b/bin/modules/srv_control.obj new file mode 100644 index 0000000..e853cda Binary files /dev/null and b/bin/modules/srv_control.obj differ diff --git a/bin/modules/test_unit.obj b/bin/modules/test_unit.obj index 44922db..ba5b189 100644 Binary files a/bin/modules/test_unit.obj and b/bin/modules/test_unit.obj differ diff --git a/bin/modules/test_unit4.obj b/bin/modules/test_unit4.obj index 190a0e3..7773ac7 100644 Binary files a/bin/modules/test_unit4.obj and b/bin/modules/test_unit4.obj differ diff --git a/bin/modules/test_unit5.obj b/bin/modules/test_unit5.obj new file mode 100644 index 0000000..5367c5d Binary files /dev/null and b/bin/modules/test_unit5.obj differ diff --git a/bin/modules/test_unit_2.obj b/bin/modules/test_unit_2.obj index 3bb0572..3c73304 100644 Binary files a/bin/modules/test_unit_2.obj and b/bin/modules/test_unit_2.obj differ diff --git a/example/srv_control.asm b/example/srv_control.asm new file mode 100644 index 0000000..e930ed0 --- /dev/null +++ b/example/srv_control.asm @@ -0,0 +1,148 @@ +; test api 0.1.0 - get cmd path and get context unit +format MS COFF ;<- this is lib format +public @EXPORT as 'EXPORTS' + +NO_DEBUG_INPUT = 0 +include "macros.inc" +purge mov,add,sub +include "proc32.inc" + +include '../module_api.inc' + +section '.flat' code readable align 16 + +unit_init: + xor eax, eax + 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 + + ; create unit context + invoke IMPORT.Alloc, 4096 ; for cmd path + test eax, eax + jz .exit + + mov esi, [esp + 4*2 + 8] + mov edi, eax + test esi, esi + jz .exit + pushfd + cld +@@: + movsb + cmp byte[esi - 1], 0 + jnz @b + + mov esi, [esp + 4*3 + 8] + mov edi, text_board.token + movsd + movsw + + popfd + ;unit init successful +.exit: + pop edi esi + ret 8 + + +server_entry: + push esi edi + mov esi, [esp + 4*2 + 4] ; request context + + mov edi, [esp + 4*2 + 8] ; unit context + cmp dword[edi], 0 + je .err_403 + + ; check arg "token" + + invoke IMPORT.find_uri_arg, esi, key_token + test eax, eax + jz .err_403 + mov ecx, esi + mov esi, eax + + cmpsd + jne .err_403_1 + cmpsw + jne .err_403_1 + cmpsb + jne .err_403_1 + cmp byte[edi - 1], 0 + jne .err_403_1 + mov esi, ecx + ; check command + + invoke IMPORT.find_uri_arg, esi, key_cmd + test eax, eax + jz .no_shutdown + + cmp word[eax], 'S' + jnz .no_shutdown + + invoke IMPORT.close_server +.no_shutdown: + + invoke IMPORT.create_resp, esi, 0 + test eax, eax + jz .exit + + push eax + invoke IMPORT.send_resp, eax, text_board, text_board.size + invoke IMPORT.destruct_resp +.exit: + pop edi esi + ret 8 +.err_403_1: + mov esi, ecx +.err_403: + invoke IMPORT.create_resp, esi, FLAG_NO_CACHE_CONTROL\ + + FLAG_NO_CONTENT_ENCODING + test eax, eax + jz .exit + + mov edi, eax + invoke IMPORT.set_http_status, edi, dword '403' + invoke IMPORT.send_resp, edi, text403, text403.length + invoke IMPORT.destruct_resp, edi + jmp .exit + + +server_close: + mov ecx, [esp + 4] + invoke IMPORT.Free, ecx + ret 4 + + +section '.data' data readable writable align 16 + +key_token: + db 'token', 0 +key_cmd: + db 'cmd',0 + +text403: + db 'Access to server management is prohibited.',\ + ' An unknown token has been entered' +.length = $ - text403 + +text_board: + db '

Control panel of simple-httpd

Stop Server' +.size = $ - text_board + +@EXPORT: +export \ + unit_init, 'httpd_init', \ + server_entry, 'httpd_serv',\ + server_close, 'httpd_close' + + +IMPORT IMPORT_DATA \ No newline at end of file diff --git a/example/test_unit4.asm b/example/test_unit4.asm index e55cb60..43b6112 100644 --- a/example/test_unit4.asm +++ b/example/test_unit4.asm @@ -88,7 +88,7 @@ section '.data' data readable writable align 16 text_no_cmd: db 'For this unit in config not set arguments' -.size = $ - $$ +.size = $ - text_no_cmd @EXPORT: export \ diff --git a/example/test_unit5.asm b/example/test_unit5.asm new file mode 100644 index 0000000..ba73eb0 --- /dev/null +++ b/example/test_unit5.asm @@ -0,0 +1,84 @@ +; test api 0.1.0 - get cmd path and get context unit +format MS COFF ;<- this is lib format +public @EXPORT as 'EXPORTS' + +NO_DEBUG_INPUT = 0 +include "macros.inc" +purge mov,add,sub +include "proc32.inc" + +include '../module_api.inc' + +section '.flat' code readable align 16 + +unit_init: + xor eax, eax + 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 + + mov eax, 1 ;no zero return - module init successful +.exit: + pop edi esi + ret 8 + + +server_entry: + push esi edi + mov esi, [esp + 4*2 + 4] ; request context + + invoke IMPORT.create_resp, esi, FLAG_TRANSFER_CHUNKED\ + + FLAG_NO_CONTENT_LENGTH\ + + FLAG_NO_SERVER_HEADER\ + + FLAG_NO_CONTENT_ENCODING\ + + FLAG_NO_CONNECTION\ + + FLAG_NO_CACHE_CONTROL + test eax, eax + jz .exit + + push eax + mov edi, eax + invoke IMPORT.begin_send_resp, edi, 0, 0 + + invoke IMPORT.send_resp, edi, text_no_cmd, text_no_cmd.size + invoke IMPORT.send_resp, edi, text_no_cmd2, text_no_cmd2.size + invoke IMPORT.send_resp, edi, text_no_cmd3, text_no_cmd3.size + + invoke IMPORT.finish_send_resp, edi + invoke IMPORT.destruct_resp +.exit: + pop edi esi + ret 8 + +server_close: + + ret 4 + + +section '.data' data readable writable align 16 + +text_no_cmd: + db 'chunk 1
' +.size = $ - text_no_cmd +text_no_cmd2: + db 'chunk 2 - new size chunk
' +.size = $ - text_no_cmd2 +text_no_cmd3: + db 'chunk 3 - end chunk' +.size = $ - text_no_cmd3 + +@EXPORT: +export \ + unit_init, 'httpd_init', \ + server_entry, 'httpd_serv',\ + server_close, 'httpd_close' + + +IMPORT IMPORT_DATA \ No newline at end of file diff --git a/example/test_unit_2.asm b/example/test_unit_2.asm index cc21380..372ae78 100644 --- a/example/test_unit_2.asm +++ b/example/test_unit_2.asm @@ -138,7 +138,7 @@ server_entry: .send_data: ; eax - ptr to buffer mov edi, eax board_input 'create_resp' - invoke IMPORT.create_resp, esi, 0 + invoke IMPORT.create_resp, esi, FLAG_KEEP_ALIVE test eax, eax jz .exit @@ -153,7 +153,8 @@ server_entry: .err_404: ; send resp 404 board_input 'err404' - invoke IMPORT.create_resp, esi, 0 + invoke IMPORT.create_resp, esi, FLAG_NO_CACHE_CONTROL\ + + FLAG_NO_CONTENT_ENCODING test eax, eax jz .exit diff --git a/httpd.asm b/httpd.asm index d5a0f3c..cdaa59e 100644 --- a/httpd.asm +++ b/httpd.asm @@ -1,10 +1,10 @@ ;*****************************************************************************; -; Copyright (C) 2023, Mikhail Frolov aka Doczom . All rights reserved. ; +; Copyright (C) 2023-2024, Mikhail Frolov aka Doczom . All rights reserved. ; ; Distributed under terms of the 3-Clause BSD License. ; ; ; ; httpd - Simple http server for Kolibri OS. ; ; ; -; Version 0.1.0, 10 December 2023 ; +; Version 0.2.2, 10 March 2024 ; ; ; ;*****************************************************************************; @@ -36,10 +36,12 @@ START: test eax, eax jnz .err_settings - mov ecx, PATH - cmp byte[ecx],0 - jnz @f mov ecx, default_ini_path + cmp byte[PATH],0 + jz @f + + mov ecx, PATH + @@: ; get settings call load_settings ; ecx -> string to config file @@ -70,14 +72,21 @@ START: jz .listen_err .mainloop: - mcall 23, 100 ; get event to network stack + cmp dword[srv_shutdown], 1 + jz .shutdown + + mcall 23, 100 ; get event to network stack test eax, eax jz .mainloop + cmp dword[srv_stop], 1 + jz .mainloop + push dword thread_connect call CreateThread ; not save PID jmp .mainloop +.shutdown: .listen_err: .bind_err: push dword[srv_socket] @@ -237,6 +246,7 @@ default_ini_path: db 'httpd.ini',0 ini_section_units: db 'MODULES',0 ini_section_main: db 'MAIN', 0 +ini_section_tls db 'TLS',0 ini_key_ip db 'ip',0 ini_key_port db 'port',0 @@ -285,6 +295,8 @@ EXPORT_DATA: ; in modules for this table using struct IMPORT_DATA dd find_uri_arg dd find_header dd close_server + dd begin_send_resp + dd finish_send_resp dd base_response dd GLOBAL_DATA @@ -293,7 +305,8 @@ EXPORT_DATA: ; in modules for this table using struct IMPORT_DATA ; DATA ;UDATA - +srv_stop: rd 1 ; set 1 for skip new connections +srv_shutdown: rd 1 ; set 1 for ending working server srv_backlog: rd 1 ; maximum number of simultaneous open connections srv_socket: rd 1 diff --git a/httpd_lib.inc b/httpd_lib.inc index e5f17d9..ad97635 100644 --- a/httpd_lib.inc +++ b/httpd_lib.inc @@ -82,16 +82,52 @@ ; db 13, 10 ;.size = $ - http_err_response -default_http_version: db 'HTTP/1.1 ' +default_http_version: + db 'HTTP/1.1 ' .length = $ - default_http_version +default_http_connection: + db 'Connection: ' +.value = $ - default_http_connection + db 'close ', 13, 10 ; or keep-alive +.length = $ - default_http_connection + +default_http_cont_encod: + db 'Content-Encoding: identity', 13, 10 +.length = $ - default_http_cont_encod + +default_http_cont_type: + db 'Content-type: text/html', 13, 10 +.length = $ - default_http_cont_type + +default_http_cache_ctl: + db 'Cache-Control: no-cache', 13, 10 +.length = $ - default_http_cache_ctl + +default_http_cont_len: + db 'Content-length: ' + db '0000000000000000000000', 13, 10 +.length = $ - default_http_cont_len + +default_http_header_server: + db 'Server: simple-httpd/0.3.0', 13, 10 +.length = $ - default_http_header_server + +http_header_transfer_chunked: + db 'Transfer-Encoding: chunked', 13, 10 +.length = $ - http_header_transfer_chunked + +;default_http_date_header: +; db 'Date: ' +;.date: db 'Sun, 30 Oct 2022 09:29:13 GMT',13, 10 +;.length = $ - default_http_date_header + + http_response_err_501: db 'HTTP/1.1 ' db '501 ',13, 10 db 'Error parsing your request message. The version is not supported or another error.' - db 'Server: simple-httpd/0.0.1', 13, 10 - ;db 'Content-length: 91', 13, 10 - ;db 'Content-type: text/plain', 13, 10; + db 'Server: simple-httpd/0.3.0', 13, 10 db 'Connection: close', 13, 10 db 13, 10 .size = $ - http_response_err_501 @@ -99,7 +135,7 @@ http_response_err_501: http_response_err_404: db 'HTTP/1.1 ' db '404 ',13, 10 - db 'Server: simple-httpd/0.0.1', 13, 10 + db 'Server: simple-httpd/0.3.0', 13, 10 db 'Content-length: 45', 13, 10 db 'Content-type: text/plain', 13, 10; db 'Connection: close', 13, 10 @@ -110,7 +146,7 @@ http_response_err_404: http_response_options: db 'HTTP/1.1 ' db '204 ',13, 10 - db 'Server: simple-httpd/0.0.1', 13, 10 + db 'Server: simple-httpd/0.3.0', 13, 10 db 'Allow: OPTIONS, GET, POST', 13, 10 db 'Connection: close', 13, 10 db 13, 10 @@ -124,7 +160,7 @@ response: db 'HTTP/1.1 ' .code = $ - response db '000 ',13, 10 - db 'Server: simple-httpd/0.0.1', 13, 10 + db 'Server: simple-httpd/0.3.0', 13, 10 db 'Cache-Control: no-cache', 13, 10 db 'Content-Encoding: ' .content_encod = $ - response @@ -179,3 +215,6 @@ STD_MIME_TYPE_ARR: _DIV_10_: dd 10 _DIV_100_: dd 100 _DIV_1000_: dd 1000 + +hex_chars: + db '0123456789ABCDEF' diff --git a/module_api.inc b/module_api.inc index 6b9096c..be27f10 100644 --- a/module_api.inc +++ b/module_api.inc @@ -6,11 +6,18 @@ API_VERSION = 0x100 ; 0.1.0 -FLAG_KEEP_ALIVE = 0x01 -FLAG_ADD_DATE = 0x02 ;(not supported) -FLAG_NO_SET_CACHE = 0x04 ;(not supported) -FLAG_NO_CONTENT_ENCODING = 0x08 ;(not supported) -FLAG_TRASFER_CHUNKED = 0x10 ;(not supported) +FLAG_KEEP_ALIVE = 0x01 +FLAG_NO_CONNECTION = 0x02 +FLAG_NO_SERVER_HEADER = 0x04 +FLAG_NO_CONTENT_ENCODING = 0x08 +FLAG_NO_DATE = 0x10 ;(not supported) + +FLAG_NO_CONTENT_LENGTH = 0x20 +FLAG_NO_CONTENT_TYPE = 0x40 +FLAG_NO_CACHE_CONTROL = 0x80 + +FLAG_TRANSFER_CHUNKED = 0x100 +FLAG_RAW_STREAM = 0x200 struct CONNECT_DATA ; 16*4 = 64 bytes socket dd 0 ; номер сокета подключения @@ -64,14 +71,17 @@ struct IMPORT_DATA ; no del std header set_http_ver rd 1 ; void set_http_ver(RESPD* ptr, char* version, uint32_t length); - ; example: 'RTSP/1.1 ' + ; example: 'RTSP/1.1' find_uri_arg rd 1 ;char* find_uri_arg(CONNECT_DATA* session, char* key); find_header rd 1 ;char* find_header(CONNECT_DATA* session, char* key); close_server rd 1 ;void close_server(); - + begin_send_resp rd 1 + ;uint32_t begin_send_resp(RESPD* ptr, uint64_t content_length); + finish_send_resp rd 1 + ;uint32_t finish_send_resp(RESPD* ptr); base_response rd 1 GLOBAL_DATA rd 1 diff --git a/readme.md b/readme.md index 022eaba..d70b254 100644 --- a/readme.md +++ b/readme.md @@ -43,9 +43,9 @@ - void stdcall httpd_close(uint32_t pdata) Эта функция вызывается сервером при завершении работы или в процессе управления сервером. Данная функция предназначена для корректного завершения работы модуля и освобождением связанных с ним ресурсов. + ## Bugs - Сервер не поддерживает работу с файлами, имеющими не ascii символы, так как не производит преобразование uri пути; - - Сервер не имеет интерфейса для управления, что может вызвать трудности в сохранении временных данных модулей при завершении работы; - В ходе тестов был обнаружена ошибка отправки "больших" файлов. Это баг сетевого стека; - При длительной работе сервер может начать "подзависать" или перестать отвечать на сообщения. Это баг сетевого стека. diff --git a/settings.inc b/settings.inc index 626d6b7..22f6a25 100644 --- a/settings.inc +++ b/settings.inc @@ -38,6 +38,9 @@ ends load_settings: mov ebp, ecx + mov dword[srv_stop], 0 + mov dword[srv_shutdown], 0 + sub esp, 16 mov esi, esp invoke ini.get_str, ebp, ini_section_main, ini_key_ip, esi, 16, 0 ; ip diff --git a/sys_func.inc b/sys_func.inc index 34a51e0..5070cdc 100644 --- a/sys_func.inc +++ b/sys_func.inc @@ -246,7 +246,7 @@ destruct_resp: set_http_status: mov eax, [esp + 4] mov ecx, [esp + 8] - and ecx, 0x00ffffff ; clear 3 byte register + and ecx, 0x00ffffff ; clear 3 byte of register add ecx, 0x20 shl 24 ; set 3 byte in ' ' mov [eax + RESPD.http_status], ecx ret 8 @@ -309,7 +309,7 @@ del_http_header: jmp @b .exit: mov ecx, [esp + 4] - inc dword[ecx + RESPD.count_header] + dec dword[ecx + RESPD.count_header] ret 8 .err: mov eax, -1 @@ -319,108 +319,80 @@ del_http_header: ;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 + test [ebp + RESPD.flags], FLAG_TRANSFER_CHUNKED + jnz .chunked + + test [ebp + RESPD.flags], FLAG_RAW_STREAM + jnz .send_content + + mov edx, [esp + 4*3 + 12] + xor ecx, ecx + 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 + push ebp + call begin_send_resp cmp eax, -1 jz .exit ; send content - +.send_content: mov ecx, [esp + 4*3 + 8] ; ptr test ecx, ecx - jz .free + jz .exit + + 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 +.exit: + pop ebp edi esi + ret 12 + + +.chunked: + ; TODO + mov eax, -1 + mov ecx, [esp + 4*3 + 8] ; ptr + test ecx, ecx + jz .exit + mov ecx, [esp + 4*3 + 12] ; size + test ecx, ecx + jz .exit + + ; send db , 13, 10 + mov eax, [esp + 4*3 + 12] + push word 0x0a0d + sub esp, 8 + lea esi, [esp + 7] + mov ecx, 8 +@@: + mov edx, eax + and edx, 1111b + mov dl, byte[hex_chars + edx] + mov [esi], dl + shr eax, 4 + dec esi + loop @b + + mov ecx, esp + mov eax, [ebp + RESPD.session] + push dword 0 + push 10 ; size + push ecx + push dword[eax + CONNECT_DATA.socket] + call netfunc_send + add esp, 2+8 + + cmp eax, -1 + jz .exit + + ; send content + mov ecx, [esp + 4*3 + 8] ; ptr mov eax, [ebp + RESPD.session] push dword 0 @@ -431,22 +403,21 @@ send_resp: cmp eax, -1 jz .exit -.free: - xor eax, eax - ; free buffer - push eax - stdcall Free, [ebp + RESPD.buffer] - pop eax -.exit: + + ; send db 13, 10 + push word 0x0a0d + mov ecx, esp ; ptr + + mov eax, [ebp + RESPD.session] + push dword 0 + push 2 ; size + push ecx + push dword[eax + CONNECT_DATA.socket] + call netfunc_send + pop ebp edi esi ret 12 -.error_alloc: - mov eax, -1 - jmp .exit - - - ;char* find_uri_arg(CONNECT_DATA* session, char* key) find_uri_arg: push esi edi @@ -509,7 +480,260 @@ find_header: pop edi esi ret 8 -;void close_server() -close_server: +;uint32_t begin_send_resp(RESPD* ptr, uint64_t content_length); +begin_send_resp: + ; send status line, headers and \n + 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 + 5 + 2 ; finish 0x0d 0x0a + status(5 byte) + 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 size default headers + ;add ecx, response.end_headers - response.code + test [ebp + RESPD.flags], FLAG_NO_CONNECTION + jnz @f + add ecx, default_http_connection.length +@@: + test [ebp + RESPD.flags], FLAG_NO_SERVER_HEADER + jnz @f + add ecx, default_http_header_server.length +@@: + test [ebp + RESPD.flags], FLAG_NO_CONTENT_ENCODING + jnz @f + add ecx, default_http_cont_encod.length +@@: + test [ebp + RESPD.flags], FLAG_TRANSFER_CHUNKED + jz @f + add ecx, http_header_transfer_chunked.length +@@: + test [ebp + RESPD.flags], FLAG_NO_CONTENT_LENGTH + jnz @f + add ecx, default_http_cont_len.length +@@: + test [ebp + RESPD.flags], FLAG_NO_CONTENT_TYPE + jnz @f + add ecx, default_http_cont_type.length +@@: + test [ebp + RESPD.flags], FLAG_NO_CACHE_CONTROL + jnz @f + add ecx, default_http_cache_ctl.length +@@: + mov [ebp + RESPD.buffer_size], ecx + ; alloc buffer + stdcall Alloc, ecx + test eax, eax + jz .error + mov [ebp + RESPD.buffer], eax + mov edi, eax + ; copy data - version, status code, default header + mov ecx, [ebp + RESPD.http_ver_len] + mov esi, [ebp + RESPD.http_ver_ptr] + rep movsb ; copy http ver + + mov al, ' ' + stosb + mov eax, [ebp + RESPD.http_status] + stosd + mov eax, 0x0a0d + stosw + + ; copy and creating default headers + test [ebp + RESPD.flags], FLAG_NO_SERVER_HEADER + jnz @f + mov esi, default_http_header_server + mov ecx, default_http_header_server.length + rep movsb +@@: + test [ebp + RESPD.flags], FLAG_NO_CACHE_CONTROL + jnz @f + mov esi, default_http_cache_ctl + mov ecx, default_http_cache_ctl.length + rep movsb +@@: + test [ebp + RESPD.flags], FLAG_NO_CONTENT_ENCODING + jnz @f + mov esi, default_http_cont_encod + mov ecx, default_http_cont_encod.length + rep movsb +@@: + test [ebp + RESPD.flags], FLAG_TRANSFER_CHUNKED + jz @f + + mov esi, http_header_transfer_chunked + mov ecx, http_header_transfer_chunked.length + rep movsb +@@: + test [ebp + RESPD.flags], FLAG_NO_CONTENT_LENGTH + jnz .no_content_length + + mov esi, default_http_cont_len + mov ecx, default_http_cont_len.length + rep movsb + ; set content length + mov eax, [esp + 4*3 + 8] + mov edx, [esp + 4*3 + 12] + + test eax, eax + jne @f + test edx, edx + jz .no_content_length +@@: + cmp edx, 1000 ; 999*4 - max length of file + jae .error_free + + lea ecx, [edi - 1 - 2] ; skip 13, 10 and set on finish char number + +.div1000: + div dword[_DIV_1000_] + push eax + + mov eax, edx + xor edx, edx + + div dword[_DIV_100_] + push eax + + mov eax, edx + xor edx, edx + div dword[_DIV_10_] + add byte[ecx], dl + add byte[ecx - 1], al + sub ecx, 3 + + pop eax + add byte[ecx + 1], al + + pop eax + xor edx, edx + test eax, eax + jne .div1000 + +.no_content_length: + test [ebp + RESPD.flags], FLAG_NO_CONTENT_TYPE + jnz @f + + mov esi, default_http_cont_type + mov ecx, default_http_cont_type.length + rep movsb +@@: + test [ebp + RESPD.flags], FLAG_NO_CONNECTION + jnz @f + + mov esi, default_http_connection + mov ecx, default_http_connection.length + rep movsb + + test [ebp + RESPD.flags], FLAG_KEEP_ALIVE + jz @f + + lea ecx, [edi - default_http_connection.length \ + + default_http_connection.value] + mov dword[ecx], 'keep' + mov dword[ecx + 4], '-ali' + mov word[ecx + 8], 've' +@@: + ; 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 + + push eax + stdcall Free, [ebp + RESPD.buffer] + pop eax + pop ebp edi esi + ret 12 + +.error_free: + stdcall Free, [ebp + RESPD.buffer] +.error: + mov eax, -1 + pop ebp edi esi + ret 12 + + +;uint32_t finish_send_resp(RESPD* ptr); +finish_send_resp: + ; send finish block for chunked encoding + mov edx, [esp + 4] + test dword[edx + RESPD.flags], FLAG_TRANSFER_CHUNKED + jz .exit + ; send finish block - zero length + sub esp, 5 + mov dword[esp + 1], 0x0a0d0a0d + mov byte[esp], '0' + + mov ecx, esp + mov eax, [ebp + RESPD.session] + + push dword 0 + push dword 5 ; size buffer + push ecx + push dword[eax + CONNECT_DATA.socket] + call netfunc_send + + add esp, 5 +.exit: + ret 4 + + +;void close_server() +; TODO: added free HTTPD_MODULE structures +close_server: + ; call function httpd_close() for all modules + ; terminate main thread server + mov dword[srv_stop], 1 + + cmp dword[GLOBAL_DATA.modules], 0 + jz .no_modules + + mov eax, [GLOBAL_DATA.modules] +.next_module: + push eax + push dword[eax + HTTPD_MODULE.pdata] ; context of module + call dword[eax + HTTPD_MODULE.httpd_close] + pop eax + + mov eax, [eax] ; HTTPD_MODULE.next + test eax, eax ; terminate list + jne .next_module + +.no_modules: + mov dword[srv_shutdown], 1 ret \ No newline at end of file