mirror of
https://github.com/KolibriOS/kolibrios.git
synced 2024-12-29 17:59:50 +03:00
Added HTTP library + example
git-svn-id: svn://kolibrios.org@4158 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
a92e5cb862
commit
501706cdc8
253
programs/develop/libraries/http/examples/downloader.asm
Normal file
253
programs/develop/libraries/http/examples/downloader.asm
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; ;;
|
||||||
|
;; Copyright (C) KolibriOS team 2009-2013. All rights reserved. ;;
|
||||||
|
;; Distributed under terms of the GNU General Public License ;;
|
||||||
|
;; ;;
|
||||||
|
;; downloader.asm - HTTP client for KolibriOS ;;
|
||||||
|
;; ;;
|
||||||
|
;; ;;
|
||||||
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
||||||
|
;; Version 2, June 1991 ;;
|
||||||
|
;; ;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
URLMAXLEN = 1024
|
||||||
|
BUFFERSIZE = 4096
|
||||||
|
|
||||||
|
__DEBUG__ = 1
|
||||||
|
__DEBUG_LEVEL__ = 1
|
||||||
|
|
||||||
|
format binary as ""
|
||||||
|
|
||||||
|
use32
|
||||||
|
org 0x0
|
||||||
|
|
||||||
|
db 'MENUET01' ; header
|
||||||
|
dd 0x01 ; header version
|
||||||
|
dd START ; entry point
|
||||||
|
dd IM_END ; image size
|
||||||
|
dd I_END+0x1000 ; required memory
|
||||||
|
dd I_END+0x1000 ; esp
|
||||||
|
dd params ; I_PARAM
|
||||||
|
dd 0x0 ; I_Path
|
||||||
|
|
||||||
|
include '../../macros.inc'
|
||||||
|
include '../../proc32.inc'
|
||||||
|
include '../../develop/libraries/box_lib/trunk/box_lib.mac'
|
||||||
|
include '../../dll.inc'
|
||||||
|
include '../../debug-fdo.inc'
|
||||||
|
include '../../develop/libraries/http/http.inc'
|
||||||
|
|
||||||
|
virtual at 0
|
||||||
|
http_msg http_msg
|
||||||
|
end virtual
|
||||||
|
|
||||||
|
START:
|
||||||
|
|
||||||
|
mcall 68, 11 ; init heap so we can allocate memory dynamically
|
||||||
|
|
||||||
|
; load libraries
|
||||||
|
stdcall dll.Load, @IMPORT
|
||||||
|
test eax, eax
|
||||||
|
jnz exit
|
||||||
|
|
||||||
|
; check parameters
|
||||||
|
cmp byte[params], 0 ; no parameters ?
|
||||||
|
je reset_events ; load the GUI
|
||||||
|
|
||||||
|
download:
|
||||||
|
|
||||||
|
DEBUGF 1, "Starting download\n"
|
||||||
|
|
||||||
|
invoke HTTP_get, params
|
||||||
|
test eax, eax
|
||||||
|
jz fail
|
||||||
|
mov [identifier], eax
|
||||||
|
|
||||||
|
.loop:
|
||||||
|
invoke HTTP_process, [identifier]
|
||||||
|
test eax, eax
|
||||||
|
jnz .loop
|
||||||
|
|
||||||
|
reset_events:
|
||||||
|
DEBUGF 1, "resetting events\n"
|
||||||
|
|
||||||
|
; Report events
|
||||||
|
; defaults + mouse
|
||||||
|
mcall 40,EVM_REDRAW+EVM_KEY+EVM_BUTTON+EVM_MOUSE+EVM_MOUSE_FILTER
|
||||||
|
|
||||||
|
redraw:
|
||||||
|
call draw_window
|
||||||
|
|
||||||
|
still:
|
||||||
|
;; DEBUGF 1, "waiting for events\n"
|
||||||
|
|
||||||
|
mcall 10 ; wait here for event
|
||||||
|
|
||||||
|
cmp eax, EV_REDRAW
|
||||||
|
je redraw
|
||||||
|
|
||||||
|
cmp eax, EV_KEY
|
||||||
|
je key
|
||||||
|
|
||||||
|
cmp eax, EV_BUTTON
|
||||||
|
je button
|
||||||
|
|
||||||
|
cmp eax, EV_MOUSE
|
||||||
|
je mouse
|
||||||
|
|
||||||
|
jmp still
|
||||||
|
|
||||||
|
key:
|
||||||
|
mcall 2 ; read key
|
||||||
|
|
||||||
|
stdcall [edit_box_key], dword edit1
|
||||||
|
|
||||||
|
cmp ax, 13 shl 8
|
||||||
|
je download
|
||||||
|
|
||||||
|
jmp still
|
||||||
|
|
||||||
|
button:
|
||||||
|
|
||||||
|
mcall 17 ; get id
|
||||||
|
|
||||||
|
cmp ah, 26
|
||||||
|
jne @f
|
||||||
|
call save
|
||||||
|
jmp still
|
||||||
|
@@:
|
||||||
|
cmp ah, 1 ; button id=1 ?
|
||||||
|
je exit
|
||||||
|
|
||||||
|
jmp download
|
||||||
|
|
||||||
|
mouse:
|
||||||
|
stdcall [edit_box_mouse], edit1
|
||||||
|
jmp still
|
||||||
|
|
||||||
|
exit:
|
||||||
|
DEBUGF 1, "Exiting\n"
|
||||||
|
mcall 68, 13, [identifier] ; free buffer
|
||||||
|
fail:
|
||||||
|
or eax, -1 ; close this program
|
||||||
|
mcall
|
||||||
|
|
||||||
|
|
||||||
|
save:
|
||||||
|
mov ebp, [identifier]
|
||||||
|
mov eax, [ebp + http_msg.content_length]
|
||||||
|
mov [final_size], eax
|
||||||
|
lea ebx, [ebp + http_msg.data]
|
||||||
|
add ebx, [ebp + http_msg.header_length]
|
||||||
|
mov [final_buffer], ebx
|
||||||
|
mcall 70, fileinfo
|
||||||
|
|
||||||
|
.done:
|
||||||
|
|
||||||
|
; TODO: if called from command line, then exit
|
||||||
|
|
||||||
|
mov ecx, [sc.work_text]
|
||||||
|
or ecx, 0x80000000
|
||||||
|
mcall 4, <10, 93>, , download_complete
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
; *********************************************
|
||||||
|
; ******* WINDOW DEFINITIONS AND DRAW ********
|
||||||
|
; *********************************************
|
||||||
|
|
||||||
|
draw_window:
|
||||||
|
|
||||||
|
mcall 12, 1 ; start window draw
|
||||||
|
|
||||||
|
; get system colors
|
||||||
|
mcall 48, 3, sc, 40
|
||||||
|
|
||||||
|
; draw window
|
||||||
|
mov edx, [sc.work]
|
||||||
|
or edx, 0x34000000
|
||||||
|
mcall 0, <50, 370>, <350, 140>, , 0, title
|
||||||
|
|
||||||
|
; draw "url:" text
|
||||||
|
mov ecx, [sc.work_text]
|
||||||
|
or ecx, 80000000h
|
||||||
|
mcall 4, <14, 14>, , type_pls
|
||||||
|
|
||||||
|
; draw editbox
|
||||||
|
edit_boxes_set_sys_color edit1, editboxes_end, sc
|
||||||
|
stdcall [edit_box_draw], edit1
|
||||||
|
|
||||||
|
; draw buttons
|
||||||
|
mcall 8, <90, 68>, <54, 16>, 22, [sc.work_button] ; reload
|
||||||
|
mcall , <166, 50>, <54, 16>, 24 ; stop
|
||||||
|
mcall , <224, 54>, , 26 ; save
|
||||||
|
|
||||||
|
; draw buttons text
|
||||||
|
mov ecx, [sc.work_button_text]
|
||||||
|
or ecx, 80000000h
|
||||||
|
mcall 4, <102, 59>, , button_text
|
||||||
|
|
||||||
|
mcall 12, 2 ; end window redraw
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
;-----------------------------------------------------------------------------
|
||||||
|
; Data area
|
||||||
|
;-----------------------------------------------------------------------------
|
||||||
|
align 4
|
||||||
|
@IMPORT:
|
||||||
|
|
||||||
|
library lib_http, 'http.obj', \
|
||||||
|
box_lib, 'box_lib.obj'
|
||||||
|
|
||||||
|
import lib_http, \
|
||||||
|
HTTP_get , 'get' , \
|
||||||
|
find_header_field , 'find_header_field' , \
|
||||||
|
HTTP_process , 'process'
|
||||||
|
|
||||||
|
import box_lib, \
|
||||||
|
edit_box_draw, 'edit_box', \
|
||||||
|
edit_box_key, 'edit_box_key', \
|
||||||
|
edit_box_mouse, 'edit_box_mouse'
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
fileinfo dd 2, 0, 0
|
||||||
|
final_size dd 0
|
||||||
|
final_buffer dd 0
|
||||||
|
db '/rd/1/.download', 0
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
|
||||||
|
mouse_dd dd 0
|
||||||
|
edit1 edit_box 295, 48, 10, 0xffffff, 0xff, 0x80ff, 0, 0x8000, URLMAXLEN, document_user, mouse_dd, ed_focus+ed_always_focus, 7, 7
|
||||||
|
editboxes_end:
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
|
||||||
|
include_debug_strings
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
|
||||||
|
type_pls db 'URL:', 0
|
||||||
|
button_text db 'DOWNLOAD STOP RESAVE', 0
|
||||||
|
download_complete db 'File saved as /rd/1/.download', 0
|
||||||
|
title db 'HTTP Downloader', 0
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
document_user db 'http://'
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
IM_END:
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
params rb URLMAXLEN
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
sc system_colors
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
identifier dd ?
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
|
||||||
|
I_END:
|
||||||
|
|
||||||
|
|
||||||
|
|
787
programs/develop/libraries/http/http.asm
Normal file
787
programs/develop/libraries/http/http.asm
Normal file
@ -0,0 +1,787 @@
|
|||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; ;;
|
||||||
|
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
|
||||||
|
;; Distributed under terms of the GNU General Public License ;;
|
||||||
|
;; ;;
|
||||||
|
;; HTTP library for KolibriOS ;;
|
||||||
|
;; ;;
|
||||||
|
;; Written by hidnplayr@kolibrios.org ;;
|
||||||
|
;; ;;
|
||||||
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
||||||
|
;; Version 2, June 1991 ;;
|
||||||
|
;; ;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; references:
|
||||||
|
; "HTTP made really easy", http://www.jmarshall.com/easy/http/
|
||||||
|
; "Hypertext Transfer Protocol -- HTTP/1.1", http://tools.ietf.org/html/rfc2616
|
||||||
|
|
||||||
|
|
||||||
|
URLMAXLEN = 65535
|
||||||
|
BUFFERSIZE = 4096
|
||||||
|
|
||||||
|
__DEBUG__ = 1
|
||||||
|
__DEBUG_LEVEL__ = 1
|
||||||
|
|
||||||
|
|
||||||
|
format MS COFF
|
||||||
|
|
||||||
|
public @EXPORT as 'EXPORTS'
|
||||||
|
|
||||||
|
include '../../../struct.inc'
|
||||||
|
include '../../../proc32.inc'
|
||||||
|
include '../../../macros.inc'
|
||||||
|
purge section,mov,add,sub
|
||||||
|
include '../../../debug-fdo.inc'
|
||||||
|
|
||||||
|
include '../../../network.inc'
|
||||||
|
include 'http.inc'
|
||||||
|
|
||||||
|
virtual at 0
|
||||||
|
http_msg http_msg
|
||||||
|
end virtual
|
||||||
|
|
||||||
|
macro copy_till_zero {
|
||||||
|
@@:
|
||||||
|
lodsb
|
||||||
|
test al, al
|
||||||
|
jz @f
|
||||||
|
stosb
|
||||||
|
jmp @r
|
||||||
|
@@:
|
||||||
|
}
|
||||||
|
|
||||||
|
section '.flat' code readable align 16
|
||||||
|
|
||||||
|
;;===========================================================================;;
|
||||||
|
lib_init: ;//////////////////////////////////////////////////////////////////;;
|
||||||
|
;;---------------------------------------------------------------------------;;
|
||||||
|
;? Library entry point (called after library load) ;;
|
||||||
|
;;---------------------------------------------------------------------------;;
|
||||||
|
;> eax = pointer to memory allocation routine ;;
|
||||||
|
;> ebx = pointer to memory freeing routine ;;
|
||||||
|
;> ecx = pointer to memory reallocation routine ;;
|
||||||
|
;> edx = pointer to library loading routine ;;
|
||||||
|
;;---------------------------------------------------------------------------;;
|
||||||
|
;< eax = 1 (fail) / 0 (ok) (library initialization result) ;;
|
||||||
|
;;===========================================================================;;
|
||||||
|
mov [mem.alloc], eax
|
||||||
|
mov [mem.free], ebx
|
||||||
|
mov [mem.realloc], ecx
|
||||||
|
mov [dll.load], edx
|
||||||
|
|
||||||
|
invoke dll.load, @IMPORT
|
||||||
|
or eax, eax
|
||||||
|
jz .ok
|
||||||
|
|
||||||
|
; load proxy settings
|
||||||
|
invoke ini.get_str, inifile, sec_proxy, key_proxy, proxyAddr, 256, proxyAddr
|
||||||
|
invoke ini.get_int, inifile, sec_proxy, key_proxyport, 80
|
||||||
|
mov [proxyPort], eax
|
||||||
|
invoke ini.get_str, inifile, sec_proxy, key_user, proxyUser, 256, proxyUser
|
||||||
|
invoke ini.get_str, inifile, sec_proxy, key_password, proxyPassword, 256, proxyPassword
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
inc eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.ok:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;================================================================================================;;
|
||||||
|
proc HTTP_get URL ;///////////////////////////////////////////////////////////////////////////////;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;? ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;> _ ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;< eax = 0 (error) / buffer ptr ;;
|
||||||
|
;;================================================================================================;;
|
||||||
|
locals
|
||||||
|
hostname dd ?
|
||||||
|
pageaddr dd ?
|
||||||
|
sockaddr dd ?
|
||||||
|
socketnum dd ?
|
||||||
|
buffer dd ?
|
||||||
|
endl
|
||||||
|
|
||||||
|
; split the URL into hostname and pageaddr
|
||||||
|
stdcall parse_url, [URL]
|
||||||
|
test eax, eax
|
||||||
|
jz .error
|
||||||
|
mov [hostname], eax
|
||||||
|
mov [pageaddr], ebx
|
||||||
|
|
||||||
|
; Do we need to use a proxy?
|
||||||
|
cmp [proxyAddr], 0
|
||||||
|
jne .proxy_done
|
||||||
|
|
||||||
|
; TODO
|
||||||
|
.proxy_done:
|
||||||
|
|
||||||
|
; Resolve the hostname
|
||||||
|
DEBUGF 1, "Resolving hostname\n"
|
||||||
|
push esp ; reserve stack place
|
||||||
|
push esp ; fourth parameter
|
||||||
|
push 0 ; third parameter
|
||||||
|
push 0 ; second parameter
|
||||||
|
push [hostname]
|
||||||
|
call [getaddrinfo]
|
||||||
|
pop esi
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
|
||||||
|
; getaddrinfo returns addrinfo struct, make the pointer to sockaddr struct
|
||||||
|
mov esi, [esi + addrinfo.ai_addr]
|
||||||
|
mov [sockaddr], esi
|
||||||
|
mov eax, [esi + sockaddr_in.sin_addr]
|
||||||
|
test eax, eax
|
||||||
|
jz .error
|
||||||
|
|
||||||
|
DEBUGF 1, "Server ip=%u.%u.%u.%u\n", \
|
||||||
|
[esi + sockaddr_in.sin_addr]:1, [esi + sockaddr_in.sin_addr + 1]:1, \
|
||||||
|
[esi + sockaddr_in.sin_addr + 2]:1, [esi + sockaddr_in.sin_addr + 3]:1
|
||||||
|
|
||||||
|
mov [esi + sockaddr_in.sin_family], AF_INET4
|
||||||
|
mov [esi + sockaddr_in.sin_port], 80 shl 8 ;;; FIXME
|
||||||
|
|
||||||
|
; Connect to the server.
|
||||||
|
mcall socket, AF_INET4, SOCK_STREAM, 0
|
||||||
|
test eax, eax
|
||||||
|
jz .error
|
||||||
|
mov [socketnum], eax
|
||||||
|
DEBUGF 1, "Socket: 0x%x\n", eax
|
||||||
|
|
||||||
|
mcall connect, [socketnum], [sockaddr], 18
|
||||||
|
test eax, eax
|
||||||
|
jnz .error
|
||||||
|
DEBUGF 1, "Socket is now connected.\n"
|
||||||
|
|
||||||
|
; TODO: free address buffer(s)
|
||||||
|
|
||||||
|
; Create the HTTP request.
|
||||||
|
invoke mem.alloc, BUFFERSIZE
|
||||||
|
test eax, eax
|
||||||
|
jz .error
|
||||||
|
mov [buffer], eax
|
||||||
|
DEBUGF 1, "Buffer has been allocated.\n"
|
||||||
|
|
||||||
|
mov dword[eax], 'GET '
|
||||||
|
lea edi, [eax + 4]
|
||||||
|
mov esi, [pageaddr] ; TODO: for proxy use http:// and then full URL
|
||||||
|
copy_till_zero
|
||||||
|
|
||||||
|
mov esi, str_http11
|
||||||
|
mov ecx, str_http11.length
|
||||||
|
rep movsb
|
||||||
|
|
||||||
|
mov esi, [hostname]
|
||||||
|
copy_till_zero
|
||||||
|
|
||||||
|
mov esi, str_close
|
||||||
|
mov ecx, str_close.length
|
||||||
|
rep movsb
|
||||||
|
|
||||||
|
mov byte[edi], 0
|
||||||
|
DEBUGF 1, "Request:\n%s", [buffer]
|
||||||
|
|
||||||
|
; now send the request
|
||||||
|
mov esi, edi
|
||||||
|
sub esi, [buffer] ; length
|
||||||
|
xor edi, edi ; flags
|
||||||
|
|
||||||
|
mcall send, [socketnum], [buffer]
|
||||||
|
test eax, eax
|
||||||
|
jz .error
|
||||||
|
DEBUGF 1, "Request has been sent to server.\n"
|
||||||
|
|
||||||
|
; Now that we have sent the request, re-purpose buffer as receive buffer
|
||||||
|
mov eax, [buffer]
|
||||||
|
push [socketnum]
|
||||||
|
popd [eax + http_msg.socket]
|
||||||
|
lea esi, [eax + http_msg.data]
|
||||||
|
mov [eax + http_msg.flags], 0
|
||||||
|
mov [eax + http_msg.write_ptr], esi
|
||||||
|
mov [eax + http_msg.buffer_length], BUFFERSIZE - http_msg.data
|
||||||
|
mov [eax + http_msg.chunk_ptr], 0
|
||||||
|
|
||||||
|
mov [eax + http_msg.status], 0
|
||||||
|
mov [eax + http_msg.header_length], 0
|
||||||
|
mov [eax + http_msg.content_length], 0
|
||||||
|
|
||||||
|
ret ; return buffer ptr
|
||||||
|
|
||||||
|
.error:
|
||||||
|
DEBUGF 1, "Error!\n"
|
||||||
|
xor eax, eax ; return 0 = error
|
||||||
|
ret
|
||||||
|
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;================================================================================================;;
|
||||||
|
proc HTTP_process identifier ;////////////////////////////////////////////////////////////////////;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;? ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;> _ ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;< eax = -1 (not finished) / 0 finished ;;
|
||||||
|
;;================================================================================================;;
|
||||||
|
pusha
|
||||||
|
mov ebp, [identifier]
|
||||||
|
mcall recv, [ebp + http_msg.socket], [ebp + http_msg.write_ptr], \
|
||||||
|
[ebp + http_msg.buffer_length], MSG_DONTWAIT
|
||||||
|
cmp eax, 0xffffffff
|
||||||
|
je .check_socket
|
||||||
|
DEBUGF 1, "Received %u bytes\n", eax
|
||||||
|
|
||||||
|
mov edi, [ebp + http_msg.write_ptr]
|
||||||
|
add [ebp + http_msg.write_ptr], eax
|
||||||
|
sub [ebp + http_msg.buffer_length], eax
|
||||||
|
jz .got_all_data
|
||||||
|
test [ebp + http_msg.flags], FLAG_GOT_HEADER
|
||||||
|
jnz .header_parsed
|
||||||
|
|
||||||
|
sub eax, 4
|
||||||
|
jl .no_header
|
||||||
|
.scan:
|
||||||
|
; scan for end of header (empty line)
|
||||||
|
cmp dword[edi], 0x0a0d0a0d ; end of header
|
||||||
|
je .end_of_header
|
||||||
|
cmp word[edi+2], 0x0a0a
|
||||||
|
je .end_of_header
|
||||||
|
inc edi
|
||||||
|
dec eax
|
||||||
|
jnz .scan
|
||||||
|
|
||||||
|
.no_header:
|
||||||
|
popa
|
||||||
|
xor eax, eax
|
||||||
|
dec eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.end_of_header:
|
||||||
|
add edi, 4 - http_msg.data
|
||||||
|
sub edi, ebp
|
||||||
|
mov [ebp + http_msg.header_length], edi
|
||||||
|
or [ebp + http_msg.flags], FLAG_GOT_HEADER
|
||||||
|
DEBUGF 1, "Header length: %u\n", edi
|
||||||
|
|
||||||
|
; Ok, we have found header:
|
||||||
|
cmp dword[ebp + http_msg.data], 'HTTP'
|
||||||
|
jne .invalid_header
|
||||||
|
cmp dword[ebp + http_msg.data+4], '/1.0'
|
||||||
|
je .http_1.0
|
||||||
|
cmp dword[ebp + http_msg.data+4], '/1.1'
|
||||||
|
jne .invalid_header
|
||||||
|
or [ebp + http_msg.flags], FLAG_HTTP11
|
||||||
|
.http_1.0:
|
||||||
|
cmp byte[ebp + http_msg.data+8], ' '
|
||||||
|
jne .invalid_header
|
||||||
|
DEBUGF 1, "Header seems valid.\n"
|
||||||
|
|
||||||
|
lea esi, [ebp + http_msg.data+9]
|
||||||
|
xor eax, eax
|
||||||
|
xor ebx, ebx
|
||||||
|
mov ecx, 3
|
||||||
|
.statusloop:
|
||||||
|
lodsb
|
||||||
|
sub al, '0'
|
||||||
|
jb .invalid_header
|
||||||
|
cmp al, 9
|
||||||
|
ja .invalid_header
|
||||||
|
lea ebx, [ebx + 4*ebx]
|
||||||
|
shl ebx, 1
|
||||||
|
add ebx, eax
|
||||||
|
dec ecx
|
||||||
|
jnz .statusloop
|
||||||
|
mov [ebp + http_msg.status], ebx
|
||||||
|
DEBUGF 1, "Status: %u\n", ebx
|
||||||
|
|
||||||
|
; Now, convert all header names to lowercase.
|
||||||
|
; This way, it will be much easier to find certain header fields, later on.
|
||||||
|
|
||||||
|
lea esi, [ebp + http_msg.data]
|
||||||
|
mov ecx, [ebp + http_msg.header_length]
|
||||||
|
.need_newline:
|
||||||
|
inc esi
|
||||||
|
dec ecx
|
||||||
|
jz .convert_done
|
||||||
|
cmp byte[esi], 10
|
||||||
|
jne .need_newline
|
||||||
|
; Ok, we have a newline, a line beginning with space or tabs has no header fields.
|
||||||
|
|
||||||
|
inc esi
|
||||||
|
dec ecx
|
||||||
|
jz .convert_done
|
||||||
|
cmp byte[esi], ' '
|
||||||
|
je .need_newline
|
||||||
|
cmp byte[esi], 9 ; horizontal tab
|
||||||
|
je .need_newline
|
||||||
|
jmp .convert_loop
|
||||||
|
.next_char:
|
||||||
|
inc esi
|
||||||
|
dec ecx
|
||||||
|
jz .convert_done
|
||||||
|
.convert_loop:
|
||||||
|
cmp byte[esi], ':'
|
||||||
|
je .need_newline
|
||||||
|
cmp byte[esi], 'A'
|
||||||
|
jb .next_char
|
||||||
|
cmp byte[esi], 'Z'
|
||||||
|
ja .next_char
|
||||||
|
or byte[esi], 0x20 ; convert to lowercase
|
||||||
|
jmp .next_char
|
||||||
|
.convert_done:
|
||||||
|
mov byte[esi-1], 0
|
||||||
|
lea esi, [ebp + http_msg.data]
|
||||||
|
DEBUGF 1, "Header names converted to lowercase:\n%s\n", esi
|
||||||
|
|
||||||
|
; Check for content-length header field.
|
||||||
|
stdcall find_header_field, ebp, str_cl
|
||||||
|
test eax, eax
|
||||||
|
jz .no_content
|
||||||
|
or [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
|
||||||
|
|
||||||
|
xor edx, edx
|
||||||
|
.cl_loop:
|
||||||
|
movzx ebx, byte[eax]
|
||||||
|
inc eax
|
||||||
|
cmp bl, 10
|
||||||
|
je .cl_ok
|
||||||
|
cmp bl, 13
|
||||||
|
je .cl_ok
|
||||||
|
cmp bl, ' '
|
||||||
|
je .cl_ok
|
||||||
|
sub bl, '0'
|
||||||
|
jb .invalid_header
|
||||||
|
cmp bl, 9
|
||||||
|
ja .invalid_header
|
||||||
|
lea edx, [edx + edx*4] ; edx = edx*10
|
||||||
|
shl edx, 1 ;
|
||||||
|
add edx, ebx
|
||||||
|
jmp .cl_loop
|
||||||
|
|
||||||
|
.cl_ok:
|
||||||
|
mov [ebp + http_msg.content_length], edx
|
||||||
|
DEBUGF 1, "Content-length: %u\n", edx
|
||||||
|
|
||||||
|
; Resize buffer according to content-length.
|
||||||
|
mov eax, [ebp + http_msg.header_length]
|
||||||
|
add eax, [ebp + http_msg.content_length]
|
||||||
|
add eax, http_msg.data
|
||||||
|
|
||||||
|
mov ecx, eax
|
||||||
|
sub ecx, [ebp + http_msg.write_ptr]
|
||||||
|
mov [ebp + http_msg.buffer_length], ecx
|
||||||
|
|
||||||
|
invoke mem.realloc, ebp, eax
|
||||||
|
or eax, eax
|
||||||
|
jz .no_ram
|
||||||
|
jmp .header_parsed ; hooray!
|
||||||
|
|
||||||
|
.no_content:
|
||||||
|
DEBUGF 1, "Content-length not found.\n"
|
||||||
|
|
||||||
|
; We didnt find 'content-length', maybe server is using chunked transfer encoding?
|
||||||
|
; Try to find 'transfer-encoding' header.
|
||||||
|
stdcall find_header_field, ebp, str_te
|
||||||
|
test eax, eax
|
||||||
|
jz .invalid_header
|
||||||
|
|
||||||
|
mov ebx, dword[eax]
|
||||||
|
or eax, 0x20202020
|
||||||
|
cmp ebx, 'chun'
|
||||||
|
jne .invalid_header
|
||||||
|
mov ebx, dword[eax+4]
|
||||||
|
or eax, 0x00202020
|
||||||
|
and eax, 0x00ffffff
|
||||||
|
cmp ebx, 'ked'
|
||||||
|
jne .invalid_header
|
||||||
|
|
||||||
|
or [ebp + http_msg.flags], FLAG_CHUNKED
|
||||||
|
|
||||||
|
DEBUGF 1, "Transfer type is: chunked\n"
|
||||||
|
|
||||||
|
; Set chunk pointer where first chunk should begin.
|
||||||
|
mov eax, [ebp + http_msg.header_length]
|
||||||
|
add eax, http_msg.data
|
||||||
|
mov [ebp + http_msg.chunk_ptr], eax
|
||||||
|
|
||||||
|
.header_parsed:
|
||||||
|
; If data is chunked, combine chunks into contiguous data if so.
|
||||||
|
test [ebp + http_msg.flags], FLAG_CHUNKED
|
||||||
|
jz .not_chunked
|
||||||
|
|
||||||
|
.chunkloop:
|
||||||
|
mov ecx, [ebp + http_msg.write_ptr]
|
||||||
|
sub ecx, [ebp + http_msg.chunk_ptr]
|
||||||
|
jb .not_finished
|
||||||
|
|
||||||
|
mov esi, [ebp + http_msg.chunk_ptr]
|
||||||
|
xor ebx, ebx
|
||||||
|
.chunk_hexloop:
|
||||||
|
lodsb
|
||||||
|
sub al, '0'
|
||||||
|
jb .chunk_
|
||||||
|
cmp al, 9
|
||||||
|
jbe .chunk_hex
|
||||||
|
sub al, 'A' - '0'
|
||||||
|
jb .chunk_
|
||||||
|
cmp al, 5
|
||||||
|
jbe .chunk_hex
|
||||||
|
sub al, 'a' - 'A'
|
||||||
|
cmp al, 5
|
||||||
|
ja .chunk_
|
||||||
|
.chunk_hex:
|
||||||
|
shl ebx, 4
|
||||||
|
add bl, al
|
||||||
|
jmp .chunk_hexloop
|
||||||
|
.chunk_:
|
||||||
|
DEBUGF 1, "got chunk of %u bytes\n", ebx
|
||||||
|
; If chunk size is 0, all chunks have been received.
|
||||||
|
test ebx, ebx
|
||||||
|
jz .got_all_data ; last chunk, hooray! FIXME: what if it wasnt a valid hex number???
|
||||||
|
add [ebp + http_msg.chunk_ptr], ebx
|
||||||
|
|
||||||
|
; Chunkline ends with a CR, LF or simply LF
|
||||||
|
.end_of_chunkline?: ; FIXME: buffer overflow possible!
|
||||||
|
cmp al, 10
|
||||||
|
je .end_of_chunkline
|
||||||
|
lodsb
|
||||||
|
jmp .end_of_chunkline?
|
||||||
|
|
||||||
|
.end_of_chunkline:
|
||||||
|
; Now move all received data to the left (remove chunk header).
|
||||||
|
; Meanwhile, update write_ptr and content_length accordingly.
|
||||||
|
mov edi, [ebp + http_msg.chunk_ptr]
|
||||||
|
mov ecx, [ebp + http_msg.write_ptr]
|
||||||
|
sub ecx, esi
|
||||||
|
mov eax, esi
|
||||||
|
sub eax, edi
|
||||||
|
sub [ebp + http_msg.write_ptr], eax
|
||||||
|
add [ebp + http_msg.content_length], ecx
|
||||||
|
rep movsb
|
||||||
|
jmp .chunkloop
|
||||||
|
|
||||||
|
.not_chunked:
|
||||||
|
; Check if we got all the data.
|
||||||
|
mov eax, [ebp + http_msg.header_length]
|
||||||
|
add eax, [ebp + http_msg.content_length]
|
||||||
|
cmp eax, [ebp + http_msg.buffer_length]
|
||||||
|
je .got_all_data
|
||||||
|
|
||||||
|
.not_finished:
|
||||||
|
; DEBUGF 1, "Needs more processing...\n"
|
||||||
|
popa
|
||||||
|
xor eax, eax
|
||||||
|
dec eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.got_all_data:
|
||||||
|
DEBUGF 1, "We got all the data!\n"
|
||||||
|
or [ebp + http_msg.flags], FLAG_GOT_DATA
|
||||||
|
mcall close, [ebp + http_msg.socket]
|
||||||
|
popa
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.check_socket:
|
||||||
|
cmp ebx, EWOULDBLOCK
|
||||||
|
je .not_finished
|
||||||
|
DEBUGF 1, "ERROR: socket error %u\n", ebx
|
||||||
|
|
||||||
|
or [ebp + http_msg.flags], FLAG_SOCKET_ERROR
|
||||||
|
popa
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.invalid_header:
|
||||||
|
DEBUGF 1, "ERROR: invalid header\n"
|
||||||
|
or [ebp + http_msg.flags], FLAG_INVALID_HEADER
|
||||||
|
popa
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_ram:
|
||||||
|
DEBUGF 1, "ERROR: out of RAM\n"
|
||||||
|
or [ebp + http_msg.flags], FLAG_NO_RAM
|
||||||
|
popa
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;================================================================================================;;
|
||||||
|
proc find_header_field identifier, headername ;///////////////////////////////////////////////////;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;? ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;> _ ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;< eax = -1 (error) / 0 ;;
|
||||||
|
;;================================================================================================;;
|
||||||
|
push ebx ecx edx esi edi
|
||||||
|
|
||||||
|
DEBUGF 1, "Find header field: %s\n", [headername]
|
||||||
|
|
||||||
|
mov ebx, [identifier]
|
||||||
|
lea edx, [ebx + http_msg.data]
|
||||||
|
mov ecx, edx
|
||||||
|
add ecx, [ebx + http_msg.header_length]
|
||||||
|
|
||||||
|
.restart:
|
||||||
|
mov esi, [headername]
|
||||||
|
mov edi, edx
|
||||||
|
.loop:
|
||||||
|
cmp edi, ecx
|
||||||
|
jae .fail
|
||||||
|
lodsb
|
||||||
|
scasb
|
||||||
|
je .loop
|
||||||
|
test al, al
|
||||||
|
jz .done?
|
||||||
|
.next:
|
||||||
|
inc edx
|
||||||
|
jmp .restart
|
||||||
|
|
||||||
|
.not_done:
|
||||||
|
inc edi
|
||||||
|
.done?:
|
||||||
|
cmp byte[edi-1], ':'
|
||||||
|
je .almost_done
|
||||||
|
cmp byte[edi-1], ' '
|
||||||
|
je .not_done
|
||||||
|
cmp byte[edi-1], 9 ; tab
|
||||||
|
je .not_done
|
||||||
|
|
||||||
|
jmp .next
|
||||||
|
|
||||||
|
.almost_done: ; FIXME: buffer overflow?
|
||||||
|
dec edi
|
||||||
|
DEBUGF 1, "Found header field\n"
|
||||||
|
.spaceloop:
|
||||||
|
inc edi
|
||||||
|
cmp byte[edi], ' '
|
||||||
|
je .spaceloop
|
||||||
|
cmp byte[edi], 9 ; tab
|
||||||
|
je .spaceloop
|
||||||
|
|
||||||
|
mov eax, edi
|
||||||
|
pop edi esi edx ecx ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.fail:
|
||||||
|
pop edi esi edx ecx ebx
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
; internal procedures start here:
|
||||||
|
|
||||||
|
|
||||||
|
;;================================================================================================;;
|
||||||
|
proc parse_url URL ;//////////////////////////////////////////////////////////////////////////////;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;? ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;> _ ;;
|
||||||
|
;;------------------------------------------------------------------------------------------------;;
|
||||||
|
;< eax = -1 (error) / 0 ;;
|
||||||
|
;;================================================================================================;;
|
||||||
|
|
||||||
|
locals
|
||||||
|
urlsize dd ?
|
||||||
|
hostname dd ?
|
||||||
|
pageaddr dd ?
|
||||||
|
endl
|
||||||
|
|
||||||
|
DEBUGF 1, "URL: %s\n", [URL]
|
||||||
|
|
||||||
|
; remove any leading protocol text
|
||||||
|
mov esi, [URL]
|
||||||
|
mov ecx, URLMAXLEN
|
||||||
|
mov ax, '//'
|
||||||
|
.loop1:
|
||||||
|
cmp byte[esi], 0 ; end of URL?
|
||||||
|
je .url_ok ; yep, so not found
|
||||||
|
cmp [esi], ax
|
||||||
|
je .skip_proto
|
||||||
|
inc esi
|
||||||
|
dec ecx
|
||||||
|
jnz .loop1
|
||||||
|
|
||||||
|
; URL invalid !
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.skip_proto:
|
||||||
|
inc esi ; skip the two '/'
|
||||||
|
inc esi
|
||||||
|
mov [URL], esi ; update pointer so it skips protocol
|
||||||
|
jmp .loop1 ; we still need to find the length of the URL
|
||||||
|
|
||||||
|
.url_ok:
|
||||||
|
sub esi, [URL] ; calculate total length of URL
|
||||||
|
mov [urlsize], esi
|
||||||
|
|
||||||
|
;;; FIXME: urls with no pageaddr are not parsed correctly!!
|
||||||
|
|
||||||
|
; now look for page delimiter - it's a '/' character
|
||||||
|
mov ecx, esi ; URL length
|
||||||
|
mov edi, [URL]
|
||||||
|
mov al, '/'
|
||||||
|
repne scasb
|
||||||
|
dec edi ; return one char, '/' must be part of the pageaddr
|
||||||
|
inc ecx ;
|
||||||
|
push ecx edi ; remember the pointer and length of pageaddr
|
||||||
|
|
||||||
|
mov ecx, edi
|
||||||
|
sub ecx, [URL]
|
||||||
|
inc ecx ; we will add a 0 byte at the end
|
||||||
|
invoke mem.alloc, ecx
|
||||||
|
or eax, eax
|
||||||
|
jz .no_mem
|
||||||
|
|
||||||
|
mov [hostname], eax ; copy hostname to buffer
|
||||||
|
mov edi, eax
|
||||||
|
mov esi, [URL]
|
||||||
|
dec ecx
|
||||||
|
rep movsb
|
||||||
|
xor al, al
|
||||||
|
stosb
|
||||||
|
|
||||||
|
mov [pageaddr], null_str ; assume there is no pageaddr
|
||||||
|
pop esi ecx
|
||||||
|
test ecx, ecx
|
||||||
|
jz .no_page
|
||||||
|
inc ecx ; we will add a 0 byte at the end
|
||||||
|
invoke mem.alloc, ecx
|
||||||
|
or eax, eax
|
||||||
|
jz .no_mem
|
||||||
|
|
||||||
|
mov [pageaddr], eax ; copy pageaddr to buffer
|
||||||
|
mov edi, eax
|
||||||
|
dec ecx
|
||||||
|
rep movsb
|
||||||
|
xor al, al
|
||||||
|
stosb
|
||||||
|
.no_page:
|
||||||
|
|
||||||
|
mov eax, [hostname]
|
||||||
|
mov ebx, [pageaddr]
|
||||||
|
|
||||||
|
DEBUGF 1, "hostname: %s\n", eax
|
||||||
|
DEBUGF 1, "pageaddr: %s\n", ebx
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_mem:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
endp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;================================================================================================;;
|
||||||
|
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
|
||||||
|
;;================================================================================================;;
|
||||||
|
;! Imported functions section ;;
|
||||||
|
;;================================================================================================;;
|
||||||
|
;;////////////////////////////////////////////////////////////////////////////////////////////////;;
|
||||||
|
;;================================================================================================;;
|
||||||
|
|
||||||
|
|
||||||
|
align 16
|
||||||
|
@IMPORT:
|
||||||
|
|
||||||
|
library \
|
||||||
|
libini, 'libini.obj', \
|
||||||
|
network, 'network.obj'
|
||||||
|
|
||||||
|
import libini, \
|
||||||
|
ini.get_str, 'ini_get_str', \
|
||||||
|
ini.get_int, 'ini_get_int'
|
||||||
|
|
||||||
|
import network,\
|
||||||
|
getaddrinfo, 'getaddrinfo',\
|
||||||
|
freeaddrinfo, 'freeaddrinfo',\
|
||||||
|
inet_ntoa, 'inet_ntoa'
|
||||||
|
|
||||||
|
;;===========================================================================;;
|
||||||
|
;;///////////////////////////////////////////////////////////////////////////;;
|
||||||
|
;;===========================================================================;;
|
||||||
|
;! Exported functions section ;;
|
||||||
|
;;===========================================================================;;
|
||||||
|
;;///////////////////////////////////////////////////////////////////////////;;
|
||||||
|
;;===========================================================================;;
|
||||||
|
|
||||||
|
|
||||||
|
align 4
|
||||||
|
@EXPORT:
|
||||||
|
export \
|
||||||
|
lib_init , 'lib_init' , \
|
||||||
|
0x00010001 , 'version' , \
|
||||||
|
HTTP_get , 'get' , \
|
||||||
|
find_header_field , 'find_header_field' , \
|
||||||
|
HTTP_process , 'process'
|
||||||
|
|
||||||
|
; HTTP_head , 'head' , \
|
||||||
|
; HTTP_post , 'post' , \
|
||||||
|
; HTTP_put , 'put' , \
|
||||||
|
; HTTP_delete , 'delete' , \
|
||||||
|
; HTTP_trace , 'trace' , \
|
||||||
|
; HTTP_connect , 'connect' , \
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
section '.data' data readable writable align 16
|
||||||
|
|
||||||
|
inifile db '/sys/settings/network.ini', 0
|
||||||
|
|
||||||
|
sec_proxy:
|
||||||
|
key_proxy db 'proxy', 0
|
||||||
|
key_proxyport db 'port', 0
|
||||||
|
key_user db 'user', 0
|
||||||
|
key_password db 'password', 0
|
||||||
|
|
||||||
|
str_http11 db ' HTTP/1.1', 13, 10, 'Host: '
|
||||||
|
.length = $ - str_http11
|
||||||
|
str_close db 13, 10, 'User-Agent: KolibriOS libHTTP/1.0', 13, 10, 'Connection: Close', 13, 10, 13, 10
|
||||||
|
.length = $ - str_close
|
||||||
|
str_proxy_auth db 13, 10, 'Proxy-Authorization: Basic '
|
||||||
|
.length = $ - str_proxy_auth
|
||||||
|
|
||||||
|
base64_table db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||||
|
db '0123456789+/'
|
||||||
|
|
||||||
|
str_cl db 'content-length', 0
|
||||||
|
null_str db 0
|
||||||
|
str_te db 'transfer-encoding', 0
|
||||||
|
|
||||||
|
include_debug_strings
|
||||||
|
|
||||||
|
; uninitialized data
|
||||||
|
mem.alloc dd ?
|
||||||
|
mem.free dd ?
|
||||||
|
mem.realloc dd ?
|
||||||
|
dll.load dd ?
|
||||||
|
|
||||||
|
proxyAddr rb 256
|
||||||
|
proxyUser rb 256
|
||||||
|
proxyPassword rb 256
|
||||||
|
proxyPort dd ?
|
38
programs/develop/libraries/http/http.inc
Normal file
38
programs/develop/libraries/http/http.inc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; ;;
|
||||||
|
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
|
||||||
|
;; Distributed under terms of the GNU General Public License ;;
|
||||||
|
;; ;;
|
||||||
|
;; HTTP library for KolibriOS ;;
|
||||||
|
;; ;;
|
||||||
|
;; Written by hidnplayr@kolibrios.org ;;
|
||||||
|
;; ;;
|
||||||
|
;; GNU GENERAL PUBLIC LICENSE ;;
|
||||||
|
;; Version 2, June 1991 ;;
|
||||||
|
;; ;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|
||||||
|
FLAG_HTTP11 = 1 shl 0
|
||||||
|
FLAG_GOT_HEADER = 1 shl 1
|
||||||
|
FLAG_GOT_DATA = 1 shl 2
|
||||||
|
FLAG_CONTENT_LENGTH = 1 shl 3
|
||||||
|
FLAG_CHUNKED = 1 shl 4
|
||||||
|
|
||||||
|
; error flags go into the upper word
|
||||||
|
FLAG_INVALID_HEADER = 1 shl 16
|
||||||
|
FLAG_NO_RAM = 1 shl 17
|
||||||
|
FLAG_SOCKET_ERROR = 1 shl 18
|
||||||
|
|
||||||
|
struc http_msg {
|
||||||
|
.socket dd ?
|
||||||
|
.flags dd ?
|
||||||
|
.write_ptr dd ?
|
||||||
|
.buffer_length dd ?
|
||||||
|
.chunk_ptr dd ?
|
||||||
|
|
||||||
|
.status dd ?
|
||||||
|
.header_length dd ?
|
||||||
|
.content_length dd ?
|
||||||
|
.data:
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user