ooRexx logo
../code/xhr.cls/var/www/html/ooRexx/wip #!/usr/bin/rexx /* --------------------------------------------------------------------------------------- */ /* No freakin' copyright, no stinkin' license, no guarantees or warrantees */ /* (implied, explicit or whatever). Usage is totally and completely at your own risk. */ /* Please keep this comment block as is if modifying this code. Thanks in advance, */ /* Ruurd Idenburg */ /* --------------------------------------------------------------------------------------- */ ::class xhr public -- using attributes without‘expose’, so that methods are free in choosing variable names ::attribute host get ::attribute host set private ::attribute port get ::attribute port set private ::attribute socket get ::attribute socket set private ::attribute reqHeaders get ::attribute reqHeaders set private ::attribute hostname get ::attribute hostname set private ::attribute rspHeaders get ::attribute rspHeaders set private ::attribute response get ::attribute response set private ::method init use strict arg host, port=80 self~hostname = host self~port = port self~host = .inetaddress~new(host,port) self~rspHeaders = .array~new exit ::method doHeaders private self~reqHeaders = .array~new self~addHeader('Host:' self~hostname) self~addHeader('User-Agent: Not My Browser 0.9') self~addHeader('Accept: */*') exit ::method get -- HTTP/1.1 GET with support for Content-Length: or Transfer-Encoding: chunked use strict arg query self~socket = .socket~new if self~socket~connect(self~host)<0 then do raise syntax 48.900 array('Socket connection to' self~hostname 'on port' self~port 'failed') end self~doHeaders request = 'GET' query 'HTTP/1.1' || '0d0a'x || self~reqHeaders~makeString(,'0d0a'x) || '0d0a0d0a'x if self~socket~send(request)<0 then do raise syntax 48.900 array('Sending request to' self~hostname 'on port' self~port 'failed') end recvLength = 1024 response = self~socket~recv(recvLength) if response==.nil then do if self~socket~errno<0 then do raise syntax 48.900 array('Receiving response from' self~hostname 'on port' self~port 'failed') end else do raise syntax 48.900 array('Socket on' self~hostname 'on port' self~port 'already closed') end end parse var response headers '0d0a0d0a'x data parse var headers . 'Content-Length:' length '0d0a'x . if length=='' then do parse var headers . 'Transfer-Encoding:' how '0d0a'x . if how='chunked' then do data = self~getChunked(data) end else do raise syntax 48.900 array('Unsupported Transfer-Encoding:' how) end end else do (length>data~length) -- if we need to receive more content data = self~getLength(data,length) end self~rspHeaders = headers~makeArray('0d0a'x) self~response = data if self~socket~close<0 then do raise syntax 48.900 array('Closing socket for' self~hostname 'on port' self~port 'failed') end exit ::method post -- HTTP/1.1 POST, for now copied mostly from GET; will do finetuning/combining later on use strict arg target,content self~socket = .socket~new if self~socket~connect(self~host)<0 then do raise syntax 48.900 array('Socket connection to' self~hostname 'on port' self~port 'failed') end self~doHeaders self~addHeader('Content-Type: application/x-www-form-urlencoded') self~addHeader('Content-Length:' content~length) request = 'POST' target 'HTTP/1.1' || '0d0a'x || self~reqHeaders~makeString(,'0d0a'x) || '0d0a0d0a'x || content if self~socket~send(request)<0 then do raise syntax 48.900 array('Sending request to' self~hostname 'on port' self~port 'failed') end recvLength = 1024 response = self~socket~recv(recvLength) if response==.nil then do if self~socket~errno<0 then do raise syntax 48.900 array('Receiving response from' self~hostname 'on port' self~port 'failed') end else do raise syntax 48.900 array('Socket on' self~hostname 'on port' self~port 'already closed') end end parse var response headers '0d0a0d0a'x data parse var headers . 'Content-Length:' length '0d0a'x . if length=='' then do parse var headers . 'Transfer-Encoding:' how '0d0a'x . if how='chunked' then do data = self~getChunked(data) end else do raise syntax 48.900 array('Unsupported Transfer-Encoding:' how) end end else do (length>data~length) -- if we need to receive more content data = self~getLength(data,length) end self~rspHeaders = headers~makeArray('0d0a'x) self~response = data if self~socket~close<0 then do raise syntax 48.900 array('Closing socket for' self~hostname 'on port' self~port 'failed') end exit ::method addHeader -- Allow for user supplied request headers use strict arg header self~reqHeaders~append(header) exit ::method getChunked private use strict arg chunks -- The end of content in chunked mode is identified by the last chunk ('0' || '0d0a0d0a'x ) lastChunk = .false if chunks<>"" then do lastChunk = (chunks~substr(chunks~length-4)==(0 || '0d0a0d0a'x)) end do while \lastChunk nextChunk = self~socket~recv(1024) if nextChunk==.nil then do if self~socket~errno<0 then do raise syntax 48.900 array('Receiving response chunk from' self~hostname 'on port' self~port 'failed') end else do raise syntax 48.900 array('Socket on' self~hostname 'on port' self~port 'already closed') end end chunks ||= nextChunk lastChunk = (chunks~substr(chunks~length-4)==(0 || '0d0a0d0a'x)) end data = '' do until chunks==(0 || '0d0a0d0a'x) parse var chunks header '0d0a'x chunk '0d0a'x chunks data ||= chunk end return data exit ::method getLength private use strict arg content,length recvLength = length-content~length -- already received part of the content do while recvLength>0 nextContent = self~socket~recv(recvLength) if nextContent==.nil then do if self~socket~errno<0 then do raise syntax 48.900 array('Receiving response chunk from' self~hostname 'on port' self~port 'failed') end else do raise syntax 48.900 array('Socket on' self~hostname 'on port' self~port 'already closed') end end content ||= nextContent recvLength -= nextContent~length end return content exit ::requires 'socket.cls'
If you feel inclined to make corrections, suggestions etc., please mail me any.
All content © Ruurd Idenburg, 2007–, except where marked otherwise. All rights reserved. This page is primarily for non-commercial use only. The Idenburg website records no personal information and sets no ‘cookies’. This site is hosted on a VPS(Virtual Private System) rented from Transip.nl, a Dutch company, falling under Dutch (privacy) laws (I think).

This page updated on by Ruurd Idenburg.