files
simple-httpd/httpd.asm
Doczom cde50c18ed Update to 0.2.0 version
## Program interface
- Added the function ``` char* find_uri_args(CONNECT_DATA* session, char* key) ```
- Added the function ``` char* find_header(CONNECT_DATA* session, char* key) ```
- Fixed a bug in ``` Get_MIME_Type ```
- Added the function ``` void close_server(); ```

## Module interface
- The initialization function and the request processing function have been changed:

``` uint32_t stdcall httpd_init(IMPORT_DATA* import, char* cmdline) ```

```` void stdcall httpd_server(CONNECT_DATA* request_data, uint32_t pdata) ```

Added a module shutdown function for a specific uri:

``` void stdcall httpd_close(uint32_t pdata) ```

## Modules
- Added a module for testing parameter transmission during initialization

## Other
- Added a build script
- Added a single file for the program and modules with constants and structures
2024-02-11 21:45:47 +05:00

327 lines
9.9 KiB
NASM

;*****************************************************************************;
; Copyright (C) 2023, 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 ;
; ;
;*****************************************************************************;
;include "macros.inc"
use32
org 0
db 'MENUET01'
dd 1, START, I_END, MEM, STACKTOP, PATH, 0
include "macros.inc"
purge mov,add,sub
include 'module_api.inc'
include "proc32.inc"
include "dll.inc"
;include 'D:\kos\programs\network.inc'
;KOS_APP_START
include 'sys_func.inc'
include 'settings.inc'
;CODE
START:
mcall 68, 11 ; init heap
mcall 40, EVM_STACK ;set event bitmap
; init library
stdcall dll.Load, @IMPORT
test eax, eax
jnz .err_settings
mov ecx, PATH
cmp byte[ecx],0
jnz @f
mov ecx, default_ini_path
@@:
; get settings
call load_settings ; ecx -> string to config file
test eax, eax
jnz .err_settings
;init server socket
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
cmp eax, -1
je .sock_err
mov [srv_socket], eax
push srv_sockaddr.length
push dword srv_sockaddr
push dword[srv_socket]
call netfunc_bind; [srv_socket], srv_sockaddr, srv_sockaddr.length
cmp eax, -1
je .bind_err
; listen()
push dword[srv_backlog]
push dword[srv_socket]
call netfunc_listen; [srv_socket], [srv_backlog]
cmp eax, -1
jz .listen_err
.mainloop:
mcall 23, 100 ; get event to network stack
test eax, eax
jz .mainloop
push dword thread_connect
call CreateThread ; not save PID
jmp .mainloop
.listen_err:
.bind_err:
push dword[srv_socket]
call netfunc_close; [srv_socket]
.err_settings:
.sock_err:
mcall -1
thread_connect:
sub esp, sizeof.CONNECT_DATA
mcall 40, EVM_STACK ; set event bitmap - network event
; ожидание подключения Accept, sockaddr находится на вершине стека нового потока
;lea edx, [esp + CONNECT_DATA.sockaddr] ; new sockaddr
;push dword 16 ; 16 byte - sockaddr length
;push edx
push srv_sockaddr.length
push dword srv_sockaddr
push dword[srv_socket]
call netfunc_accept
cmp eax, -1
jz .err_accept
mov [esp + CONNECT_DATA.socket], eax
mov ebp, esp
; соединение установленно, теперь нужно выделить буфер(тоже на 16 кб наверное), и
; прочитать в этот буфер из сокета, когда прочтём ноль(или больше 4 кб), тогда
; выходим из цикла и анализируем заголовки и стартовую стоку.
mov esi, 0x8000
push esi
call Alloc ;alloc memory 32 kib
test eax, eax
jz .err_alloc
mov [esp + CONNECT_DATA.buffer_request], eax
mov edx, eax
mov esi, esp ; SAVE CONNECT_DATA
mov dword[esi + CONNECT_DATA.request_size], 0
@@:
;read data from socket
push dword 0 ;flags
mov eax, [esi + CONNECT_DATA.request_size]
push dword 0x8000
sub [esp], eax
push dword[esi + CONNECT_DATA.buffer_request]
add [esp], eax
push dword[esi + CONNECT_DATA.socket]
call netfunc_recv
cmp eax, -1
jz .err_recv_sock
test eax, eax
jz @f
add [esi + CONNECT_DATA.request_size], eax
; cmp [esi + CONNECT_DATA.request_size], 0x8000 ; check end buffer
; jb @b
@@:
; после получения всего запроса(более или менее всего) выделяем озу для
; ассоциативного массива заголовков и аргументов запроса
; 8*50 + 8*100
; esp .. esp + 1024 -> for http headers
; esp + 1024 .. esp + 2048 -> for URI args
sub esp, 2048
; parse http message
mov ecx, [esi + CONNECT_DATA.buffer_request]
call parse_http_query ; ecx - buffer
test eax, eax
jz .err_parse
; find unit for uri path
cmp dword[GLOBAL_DATA.modules], 0
jz .no_modules
mov eax, [GLOBAL_DATA.modules]
.next_unit:
push esi edi
mov esi, [esi + CONNECT_DATA.uri_path]
lea edi, [eax + HTTPD_MODULE.uri_path]
@@:
cmpsb
jne @f
cmp byte[edi - 1], 0
jne @b
; found module
pop edi esi
push dword[eax + HTTPD_MODULE.pdata] ; context of module
push esi ; coutext of request
call dword[eax + HTTPD_MODULE.httpd_serv] ; call unit function
jmp .end_work
@@:
pop edi esi
mov eax, [eax] ; HTTPD_MODULE.next
test eax, eax ; terminate list
jne .next_unit
.no_modules:
; if not found modules, call file_server
call file_server ; esi - struct
; end work thread
jmp .end_work
.err_parse:
call file_server.err_http_501
.end_work:
add esp, 2048
; free OUT buffer
cmp dword[esp + CONNECT_DATA.buffer_response], 0
jz .err_recv_sock
push dword[esp + CONNECT_DATA.buffer_response]
call Free
.err_recv_sock:
; free IN buffer
cmp dword[esp + CONNECT_DATA.buffer_request], 0
jz .err_alloc
push dword[esp + CONNECT_DATA.buffer_request]
call Free
.err_alloc:
push dword[esp + CONNECT_DATA.socket]
call netfunc_close
.err_accept:
lea ecx,[esp + sizeof.CONNECT_DATA - 0x4000] ; get pointer to alloc memory
mcall 68, 13 ; free
mcall -1 ; close thread
include 'parser.inc'
include 'file_server.inc'
; DATA AND FUNCTION
include 'httpd_lib.inc'
I_END:
;DATA
@IMPORT:
library libini, 'libini.obj'
import libini,\
ini.get_str, 'ini_get_str',\
ini.get_int, 'ini_get_int',\
ini.enum_keys, 'ini_enum_keys'
default_ini_path: db 'httpd.ini',0
ini_section_units: db 'MODULES',0
ini_section_main: db 'MAIN', 0
ini_key_ip db 'ip',0
ini_key_port db 'port',0
ini_key_conn db 'conn',0
ini_key_flags db 'flags',0
ini_key_work_dir db 'work_dir',0
ini_key_modules_dir db 'modules_dir',0
ini_key_mime_file db 'mime_file',0
httpd_module_init db 'httpd_init',0
httpd_module_serv db 'httpd_serv',0
httpd_module_close db 'httpd_close',0
IMPORT_MODULE:
dd httpd_import, GLOBAL_DATA.modules_dir, 0
httpd_import:
.init dd httpd_module_init
.serv dd httpd_module_serv
.close dd httpd_module_close
dd 0
EXPORT_DATA: ; in modules for this table using struct IMPORT_DATA
dd API_VERSION
dd .size
dd netfunc_socket
dd netfunc_close
dd netfunc_bind
dd netfunc_accept
dd netfunc_listen
dd netfunc_recv
dd netfunc_send
dd FileInfo
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 find_uri_arg
dd find_header
dd close_server
dd base_response
dd GLOBAL_DATA
.size = $ - EXPORT_DATA ; (count func)*4 + size(api ver) + 4
dd 0
; DATA
;UDATA
srv_backlog: rd 1 ; maximum number of simultaneous open connections
srv_socket: rd 1
srv_sockaddr:
rw 1
.port rw 1
.ip rd 1
rb 8
.length = $ - srv_sockaddr
GLOBAL_DATA:
.modules 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
.modules_dir rb 1024
.modules_dir.end rd 1
.MIME_types_arr rd 1
.flags rd 1
._module_cmd rd 1
PATH:
rb 256
; stack memory
rb 4096
STACKTOP:
MEM:
;KOS_APP_END