forked from KolibriOS/kolibrios
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