To do this, we open a socket and send the request to the webserver: GET /project/download/14 HTTP/1.1\r\nHost: solarstrike.net\r\n\r\n
Once the request is received by the server, it will send us a reply telling us some information about the file, and then the contents of it. The reply will be formatted like this:
Note: Each file line ending has "\r\n". When we see "\r\n\r\n" is when we know where the file's content begins.HTTP/1.1 200 OK
Date: Thu, 19 Mar 2015 16:12:01 GMT
Content-Disposition: attachment; filename="rombot.3.29.r788.zip"
Set-Cookie: laravel_session=<snipped>; expires=Thu, 19-Mar-2015 18:12:01 GMT; Max-Age=7200; path=/; httponly
Last-Modified: Fri, 26 Dec 2014 17:54:37 GMT
< actual binary file content begins here >
While we could parse and make use of anything from that header (the true filename, content-type, etc.), we are only going to bother with the content-length in this example. We will check the received content and compare it against the expected file size to see when we have completed the transfer and then terminate the connection.
In macro.init(), we will open our socket and send the request to the server:
Code: Select all
local host = "solarstrike.net"; local connected,err = socket:connect(host, 80); print("Connected:", connected, err); local proto = "http"; local file = "/project/download/14"; local get = sprintf("GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", file, host); print("Send:", socket:send(get)); outfile = io.open("download.zip", "wb"); assert(outfile);
Our macro.main() simply continues execution until signaled to stop with the variable 'done':
Code: Select all
function macro.main() if( done ) then printf("Total received bytes: %d\n\n", totalReceived); outfile:close(); return false; else return true; -- Keep on waiting until we're done. end end
Just for good measure, we'll even display a fancy progress bar that shows the user how much of the file has been downloaded.
Code: Select all
function macro.event(e, ...) if( e == "socketreceived" ) then -- For every packet we receive... local sockId,data = ...; if( not beginSaving ) then -- Parse needed header content and make use of it. contentLength = data:match("Content%-Length:%s+(%d+)"); if( contentLength ) then contentLength = tonumber(contentLength); printf("Expected file size: %d\n", contentLength); end -- A \r\n\r\n signals the start of the content, so we'll save anything past it. local foundPos = data:find("\r\n\r\n"); if( foundPos ) then print("Data:", string.sub(data, 0, foundPos)); print("Begin saving\n\n"); data = string.sub(data, foundPos + string.len("\r\n\r\n")); beginSaving = true; printf(progressFmt, 0, ''); -- Start displaying our progress bar (blank right now) end end if( beginSaving ) then -- We've received another piece of the file, so save it and update progress. outfile:write(data); totalReceived = totalReceived + string.len(data); local progressRatio = totalReceived / contentLength; local goodCount = 0; if( progressRatio > 0 ) then goodCount = math.floor(progressRatio * progressBarWidth); else goodCount = 0; end printf(progressFmt, math.floor(progressRatio * 100), string.rep('=', goodCount)); if( totalReceived == contentLength ) then print("\n\nSuccessfully completed file transfer."); socket:close(); done = true; end end end