From 501706cdc8e0f84d847295fe4ab3c10540c1558f Mon Sep 17 00:00:00 2001 From: hidnplayr Date: Mon, 4 Nov 2013 13:07:21 +0000 Subject: [PATCH] Added HTTP library + example git-svn-id: svn://kolibrios.org@4158 a494cfbc-eb01-0410-851d-a64ba20cac60 --- .../libraries/http/examples/downloader.asm | 253 ++++++ programs/develop/libraries/http/http.asm | 787 ++++++++++++++++++ programs/develop/libraries/http/http.inc | 38 + 3 files changed, 1078 insertions(+) create mode 100644 programs/develop/libraries/http/examples/downloader.asm create mode 100644 programs/develop/libraries/http/http.asm create mode 100644 programs/develop/libraries/http/http.inc diff --git a/programs/develop/libraries/http/examples/downloader.asm b/programs/develop/libraries/http/examples/downloader.asm new file mode 100644 index 0000000000..6fe3502752 --- /dev/null +++ b/programs/develop/libraries/http/examples/downloader.asm @@ -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: + + + diff --git a/programs/develop/libraries/http/http.asm b/programs/develop/libraries/http/http.asm new file mode 100644 index 0000000000..900f44003d --- /dev/null +++ b/programs/develop/libraries/http/http.asm @@ -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 ? \ No newline at end of file diff --git a/programs/develop/libraries/http/http.inc b/programs/develop/libraries/http/http.inc new file mode 100644 index 0000000000..ab7c9480f8 --- /dev/null +++ b/programs/develop/libraries/http/http.inc @@ -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: +} \ No newline at end of file