Bugfixes in chunked decoder and timeouts, refactoring.

git-svn-id: svn://kolibrios.org@4690 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
hidnplayr 2014-03-23 19:37:44 +00:00
parent 258fbcb9f4
commit 27e8fa73e3

View File

@ -20,7 +20,7 @@
URLMAXLEN = 65535 URLMAXLEN = 65535
BUFFERSIZE = 8192 BUFFERSIZE = 8192
TIMEOUT = 1000 ; in 1/100 s TIMEOUT = 500 ; in 1/100 s
__DEBUG__ = 1 __DEBUG__ = 1
__DEBUG_LEVEL__ = 1 __DEBUG_LEVEL__ = 1
@ -73,7 +73,7 @@ macro HTTP_init_buffer buffer, socketnum {
push eax ebp push eax ebp
mov ebp, eax mov ebp, eax
mcall 29, 9 mcall 26, 9
mov [ebp + http_msg.timestamp], eax mov [ebp + http_msg.timestamp], eax
pop ebp eax pop ebp eax
} }
@ -494,6 +494,7 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;; ;;------------------------------------------------------------------------------------------------;;
;< eax = -1 (not finished) / 0 finished ;; ;< eax = -1 (not finished) / 0 finished ;;
;;================================================================================================;; ;;================================================================================================;;
pusha pusha
mov ebp, [identifier] mov ebp, [identifier]
@ -513,7 +514,7 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
; Update timestamp ; Update timestamp
push eax push eax
mcall 29, 9 mcall 26, 9
mov [ebp + http_msg.timestamp], eax mov [ebp + http_msg.timestamp], eax
pop eax pop eax
@ -521,7 +522,6 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
mov edi, [ebp + http_msg.write_ptr] mov edi, [ebp + http_msg.write_ptr]
add [ebp + http_msg.write_ptr], eax add [ebp + http_msg.write_ptr], eax
sub [ebp + http_msg.buffer_length], eax sub [ebp + http_msg.buffer_length], eax
; jz .got_all_data
; If data is chunked, combine chunks into contiguous data. ; If data is chunked, combine chunks into contiguous data.
test [ebp + http_msg.flags], FLAG_CHUNKED test [ebp + http_msg.flags], FLAG_CHUNKED
@ -531,6 +531,11 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
test [ebp + http_msg.flags], FLAG_GOT_HEADER test [ebp + http_msg.flags], FLAG_GOT_HEADER
jnz .header_parsed jnz .header_parsed
;--------------------------------------------------------------
;
; Header parsing code begins here
;
; We havent found the (final) header yet, search for it.. ; We havent found the (final) header yet, search for it..
.scan_again: .scan_again:
; eax = total number of bytes received so far ; eax = total number of bytes received so far
@ -544,7 +549,7 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
; put it in esi for next proc too ; put it in esi for next proc too
mov esi, edi mov esi, edi
sub eax, 3 sub eax, 3
jle .need_more_data jle .need_more_data_for_header
.scan_loop: .scan_loop:
; scan for end of header (empty line) ; scan for end of header (empty line)
cmp dword[edi], 0x0a0d0a0d ; end of header cmp dword[edi], 0x0a0d0a0d ; end of header
@ -554,7 +559,7 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
inc edi inc edi
dec eax dec eax
jnz .scan_loop jnz .scan_loop
jmp .need_more_data jmp .need_more_data_for_header
.end_of_header: .end_of_header:
add edi, 4 - http_msg.http_header add edi, 4 - http_msg.http_header
@ -562,17 +567,17 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
mov [ebp + http_msg.header_length], edi ; If this isnt the final header, we'll use this as an offset to find real header. mov [ebp + http_msg.header_length], edi ; If this isnt the final header, we'll use this as an offset to find real header.
DEBUGF 1, "Header length: %u\n", edi DEBUGF 1, "Header length: %u\n", edi
; Ok, we have found header: ; Ok, we have found the header
cmp dword[esi], 'HTTP' cmp dword[esi], 'HTTP'
jne .invalid_header jne .err_header
cmp dword[esi+4], '/1.0' cmp dword[esi+4], '/1.0'
je .http_1.0 je .http_1.0
cmp dword[esi+4], '/1.1' cmp dword[esi+4], '/1.1'
jne .invalid_header jne .err_header
or [ebp + http_msg.flags], FLAG_HTTP11 or [ebp + http_msg.flags], FLAG_HTTP11
.http_1.0: .http_1.0:
cmp byte[esi+8], ' ' cmp byte[esi+8], ' '
jne .invalid_header jne .err_header
add esi, 9 add esi, 9
xor eax, eax xor eax, eax
@ -581,16 +586,16 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
.statusloop: .statusloop:
lodsb lodsb
sub al, '0' sub al, '0'
jb .invalid_header jb .err_header
cmp al, 9 cmp al, 9
ja .invalid_header ja .err_header
lea ebx, [ebx + 4*ebx] lea ebx, [ebx + 4*ebx]
shl ebx, 1 shl ebx, 1
add ebx, eax add ebx, eax
dec ecx dec ecx
jnz .statusloop jnz .statusloop
; Ignore "100 - Continue" headers ; Ignore "100 - Continue" lines
cmp ebx, 100 cmp ebx, 100
je .scan_again je .scan_again
@ -600,7 +605,6 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
; Now, convert all header names to lowercase. ; Now, convert all header names to lowercase.
; This way, it will be much easier to find certain header fields, later on. ; This way, it will be much easier to find certain header fields, later on.
lea esi, [ebp + http_msg.http_header] lea esi, [ebp + http_msg.http_header]
mov ecx, [ebp + http_msg.header_length] mov ecx, [ebp + http_msg.header_length]
.need_newline: .need_newline:
@ -609,8 +613,8 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
jz .convert_done jz .convert_done
cmp byte[esi], 10 cmp byte[esi], 10
jne .need_newline jne .need_newline
; Ok, we have a newline, a line beginning with space or tabs has no header fields. ; We have found a newline
; A line beginning with space or tabs has no header fields.
inc esi inc esi
dec ecx dec ecx
jz .convert_done jz .convert_done
@ -654,9 +658,9 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
cmp bl, ' ' cmp bl, ' '
je .cl_ok je .cl_ok
sub bl, '0' sub bl, '0'
jb .invalid_header jb .err_header
cmp bl, 9 cmp bl, 9
ja .invalid_header ja .err_header
lea edx, [edx + edx*4] ; edx = edx*10 lea edx, [edx + edx*4] ; edx = edx*10
shl edx, 1 ; shl edx, 1 ;
add edx, ebx add edx, ebx
@ -671,7 +675,7 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
call alloc_contentbuff call alloc_contentbuff
test eax, eax test eax, eax
jz .no_ram jz .err_no_ram
xor eax, eax xor eax, eax
jmp .header_parsed jmp .header_parsed
@ -688,7 +692,7 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
mov edx, BUFFERSIZE mov edx, BUFFERSIZE
call alloc_contentbuff call alloc_contentbuff
test eax, eax test eax, eax
jz .no_ram jz .err_no_ram
xor eax, eax xor eax, eax
jmp .header_parsed jmp .header_parsed
@ -709,117 +713,153 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
mov edx, BUFFERSIZE mov edx, BUFFERSIZE
call alloc_contentbuff call alloc_contentbuff
test eax, eax test eax, eax
jz .no_ram jz .err_no_ram
; Set chunk pointer where first chunk should begin. ; Set chunk pointer where first chunk should begin.
mov eax, [ebp + http_msg.content_ptr] mov eax, [ebp + http_msg.content_ptr]
mov [ebp + http_msg.chunk_ptr], eax mov [ebp + http_msg.chunk_ptr], eax
;--------------------------------------------------------------
;
; Chunk parsing code begins here
;
.chunk_loop: .chunk_loop:
mov ecx, [ebp + http_msg.write_ptr] mov ecx, [ebp + http_msg.write_ptr]
sub ecx, [ebp + http_msg.chunk_ptr] sub ecx, [ebp + http_msg.chunk_ptr]
jb .need_more_data_chunked jbe .need_more_data_chunked
; Chunkline starts here, convert the ASCII hex number into ebx ; Chunkline starts here, convert the ASCII hex number into ebx
mov esi, [ebp + http_msg.chunk_ptr] mov esi, [ebp + http_msg.chunk_ptr]
DEBUGF 1, "Chunkline begins at 0x%x\n", esi
xor ebx, ebx xor ebx, ebx
.chunk_hexloop: cmp byte[esi], 0x0d
jne .chunk_hex_loop
dec ecx
jz .need_more_data_chunked
inc esi
cmp byte[esi], 0x0a
jne .chunk_hex_loop
dec ecx
jz .need_more_data_chunked
inc esi
.chunk_hex_loop:
lodsb lodsb
sub al, '0' sub al, '0'
jb .chunk_ jb .chunk_hex_end
cmp al, 9 cmp al, 9
jbe .chunk_hex jbe .chunk_hex
sub al, 'A' - '0' - 10 sub al, 'A' - '0' - 10
jb .chunk_ jb .chunk_hex_end
cmp al, 15 cmp al, 15
jbe .chunk_hex jbe .chunk_hex
sub al, 'a' - 'A' sub al, 'a' - 'A'
cmp al, 15 cmp al, 15
ja .chunk_ ja .chunk_hex_end
.chunk_hex: .chunk_hex:
shl ebx, 4 shl ebx, 4
add bl, al add bl, al
jmp .chunk_hexloop dec ecx
.chunk_: jnz .chunk_hex_loop
; Chunkline ends with a CR, LF or simply LF jmp .need_more_data_chunked
.chunk_hex_end:
; Chunkline ends with a CR LF or simply LF
dec esi dec esi
.end_of_chunkline?: .end_of_chunkline?:
lodsb lodsb
cmp al, 10 cmp al, 10 ; chunkline must always end with LF
je .end_of_chunkline je .end_of_chunkline
cmp esi, [ebp + http_msg.write_ptr] dec ecx
jb .end_of_chunkline? jnz .end_of_chunkline?
jmp .need_more_data xor eax, eax
jmp .need_more_data_chunked ; chunkline is incomplete, request more data
.end_of_chunkline: .end_of_chunkline:
DEBUGF 1, "Chunk of %u bytes\n", ebx DEBUGF 1, "Chunk of 0x%x bytes\n", ebx
; If chunk size is 0, all chunks have been received. ; If chunk size is 0, all chunks have been received.
test ebx, ebx test ebx, ebx
jz .got_all_data_chunked jz .got_all_data_chunked
; Calculate how many data bytes we'll need to shift ; Calculate how many data bytes we have received already
mov ecx, [ebp + http_msg.write_ptr] mov ecx, [ebp + http_msg.write_ptr]
sub ecx, [ebp + http_msg.chunk_ptr] sub ecx, [ebp + http_msg.chunk_ptr] ; ecx is now number of received data bytes
; Calculate how many bytes we'll need to shift them ; Update content_received counter
sub esi, [ebp + http_msg.chunk_ptr] add [ebp + http_msg.content_received], ecx
; Update write ptr ; Calculate new write ptr
sub [ebp + http_msg.write_ptr], esi mov edx, esi
sub edx, [ebp + http_msg.chunk_ptr] ; edx is now length of chunkline
sub [ebp + http_msg.write_ptr], edx
; Realloc buffer, make it 'chunksize' bigger. ; Realloc buffer, make it 'chunksize' bigger.
add ebx, [ebp + http_msg.chunk_ptr] lea edx, [ebx + BUFFERSIZE]
sub ebx, [ebp + http_msg.content_ptr] mov [ebp + http_msg.buffer_length], edx ; remaining space in new buffer
add ebx, BUFFERSIZE ; add some space for new chunkline header add edx, [ebp + http_msg.write_ptr]
DEBUGF 1, "Resizing buffer 0x%x, it will now be %u bytes\n", [ebp + http_msg.content_ptr], ebx sub edx, [ebp + http_msg.content_ptr]
invoke mem.realloc, [ebp + http_msg.content_ptr], ebx DEBUGF 1, "Resizing buffer 0x%x, it will now be %u bytes\n", [ebp + http_msg.content_ptr], edx
invoke mem.realloc, [ebp + http_msg.content_ptr], edx
DEBUGF 1, "New buffer = 0x%x\n", eax DEBUGF 1, "New buffer = 0x%x\n", eax
or eax, eax or eax, eax
jz .no_ram jz .err_no_ram
call recalculate_pointers call recalculate_pointers ; Because it's possible that buffer begins on another address now
; Calculate remaining available buffer size add esi, eax ; recalculate esi too!
mov eax, [ebp + http_msg.content_ptr] ; Remove chunk header (aka chunkline) from the buffer by shifting all received data after chunkt_ptr to the left
add eax, ebx
sub eax, [ebp + http_msg.write_ptr]
mov [ebp + http_msg.buffer_length], eax
; Move all received data to the left (remove chunk header).
mov edi, [ebp + http_msg.chunk_ptr] mov edi, [ebp + http_msg.chunk_ptr]
add esi, edi
; Update chunk ptr so it points to next chunk
sub ebx, BUFFERSIZE
add [ebp + http_msg.chunk_ptr], ebx
; Update number of received content bytes
add [ebp + http_msg.content_received], ecx
DEBUGF 1, "Moving %u bytes from 0x%x to 0x%x\n", ecx, esi, edi
rep movsb rep movsb
; Update chunk ptr to point to next chunk
add [ebp + http_msg.chunk_ptr], ebx
; Set number of received bytes to 0, we already updated content_received
xor eax, eax xor eax, eax
jmp .chunk_loop jmp .chunk_loop
;--------------------------------------------------------- ;--------------------------------------------------------------
; Check if we got all the data. ;
; end of proc code begins here
;
.header_parsed: .header_parsed:
; Header was already parsed and connection isnt chunked.
; Update content_received
add [ebp + http_msg.content_received], eax add [ebp + http_msg.content_received], eax
; If we received content-length parameter, check if we received all the data
test [ebp + http_msg.flags], FLAG_CONTENT_LENGTH test [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
jz .need_more_data_and_space jz @f
mov eax, [ebp + http_msg.content_received] mov eax, [ebp + http_msg.content_received]
cmp eax, [ebp + http_msg.content_length] cmp eax, [ebp + http_msg.content_length]
jae .got_all_data jae .got_all_data
jmp .need_more_data @@:
cmp [ebp + http_msg.buffer_length], 0
je .buffer_full
; Need more data
popa
xor eax, eax
dec eax
ret
.need_more_data_and_space: .buffer_full:
test [ebp + http_msg.flags], FLAG_GOT_HEADER ; Lets make it bigger..
jz .invalid_header ; It's just too damn long!
mov eax, [ebp + http_msg.write_ptr] mov eax, [ebp + http_msg.write_ptr]
add eax, BUFFERSIZE add eax, BUFFERSIZE
sub eax, [ebp + http_msg.content_ptr] sub eax, [ebp + http_msg.content_ptr]
invoke mem.realloc, [ebp + http_msg.content_ptr], eax invoke mem.realloc, [ebp + http_msg.content_ptr], eax
or eax, eax or eax, eax
jz .no_ram jz .err_no_ram
call recalculate_pointers call recalculate_pointers
mov [ebp + http_msg.buffer_length], BUFFERSIZE mov [ebp + http_msg.buffer_length], BUFFERSIZE
; Need more data
popa
xor eax, eax
dec eax
ret
.need_more_data: .need_more_data_for_header:
cmp [ebp + http_msg.buffer_length], 0
je .err_header ; It's just too damn long!
; Need more data
popa popa
xor eax, eax xor eax, eax
dec eax dec eax
ret ret
.need_more_data_chunked: .need_more_data_chunked:
; We only got a partial chunk, or need more chunks, update content_received and request more data
add [ebp + http_msg.content_received], eax add [ebp + http_msg.content_received], eax
popa popa
xor eax, eax xor eax, eax
@ -827,6 +867,7 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
ret ret
.got_all_data_chunked: .got_all_data_chunked:
; Woohoo, we got all the chunked data, calculate total number of bytes received.
mov eax, [ebp + http_msg.chunk_ptr] mov eax, [ebp + http_msg.chunk_ptr]
sub eax, [ebp + http_msg.content_ptr] sub eax, [ebp + http_msg.content_ptr]
mov [ebp + http_msg.content_length], eax mov [ebp + http_msg.content_length], eax
@ -840,46 +881,56 @@ proc HTTP_process identifier ;//////////////////////////////////////////////////
xor eax, eax xor eax, eax
ret ret
;--------------------------------------------------------------
;
; error handeling code begins here
;
.check_socket: .check_socket:
cmp ebx, EWOULDBLOCK cmp ebx, EWOULDBLOCK
jne .socket_error jne .err_socket
mcall 26, 9
mcall 29, 9 sub eax, [ebp + http_msg.timestamp]
sub eax, TIMEOUT cmp eax, TIMEOUT
cmp eax, [ebp + http_msg.timestamp] ja .err_timeout
jl .need_more_data ; Need more data
DEBUGF 1, "ERROR: timeout\n" popa
or [ebp + http_msg.flags], FLAG_TIMEOUT_ERROR xor eax, eax
jmp .disconnect dec eax
ret
.server_closed: .server_closed:
DEBUGF 1, "server closed connection, transfer complete?\n" DEBUGF 1, "server closed connection, transfer complete?\n"
test [ebp + http_msg.flags], FLAG_GOT_HEADER test [ebp + http_msg.flags], FLAG_GOT_HEADER
jz .server_error jz .err_server_closed
test [ebp + http_msg.flags], FLAG_CONTENT_LENGTH test [ebp + http_msg.flags], FLAG_CONTENT_LENGTH
jz .got_all_data jz .got_all_data
.err_server_closed:
.server_error:
pop eax pop eax
DEBUGF 1, "ERROR: server closed connection unexpectedly\n" DEBUGF 1, "ERROR: server closed connection unexpectedly\n"
or [ebp + http_msg.flags], FLAG_TRANSFER_FAILED or [ebp + http_msg.flags], FLAG_TRANSFER_FAILED
jmp .disconnect jmp .abort
.invalid_header: .err_header:
pop eax pop eax
DEBUGF 1, "ERROR: invalid header\n" DEBUGF 1, "ERROR: invalid header\n"
or [ebp + http_msg.flags], FLAG_INVALID_HEADER or [ebp + http_msg.flags], FLAG_INVALID_HEADER
jmp .disconnect jmp .abort
.no_ram: .err_no_ram:
DEBUGF 1, "ERROR: out of RAM\n" DEBUGF 1, "ERROR: out of RAM\n"
or [ebp + http_msg.flags], FLAG_NO_RAM or [ebp + http_msg.flags], FLAG_NO_RAM
jmp .disconnect jmp .abort
.socket_error: .err_timeout:
DEBUGF 1, "ERROR: timeout\n"
or [ebp + http_msg.flags], FLAG_TIMEOUT_ERROR
jmp .abort
.err_socket:
DEBUGF 1, "ERROR: socket error %u\n", ebx DEBUGF 1, "ERROR: socket error %u\n", ebx
or [ebp + http_msg.flags], FLAG_SOCKET_ERROR or [ebp + http_msg.flags], FLAG_SOCKET_ERROR
.disconnect: .abort:
and [ebp + http_msg.flags], not FLAG_CONNECTED and [ebp + http_msg.flags], not FLAG_CONNECTED
mcall close, [ebp + http_msg.socket] mcall close, [ebp + http_msg.socket]
.connection_closed: .connection_closed: