Some more internet fixes

Add logic for when the socket should be closed
Same for HTTP requests
Add support for when HTTP requests fail
Remove and readd machine.lua's sethook to get around how slow
http.request can be at times
Patch socket.http to return a table for headers only when there are
multiple values, only because other code will break
Support table's for headers in our internet component
Change header case to a more normal version
This commit is contained in:
gamax92 2015-06-19 15:45:38 -06:00
parent ca66a9e20c
commit b3494872f1
2 changed files with 97 additions and 5 deletions

View File

@ -5,6 +5,7 @@ if not okay then
cprint("Cannot use internet component: " .. socket) cprint("Cannot use internet component: " .. socket)
return return
end end
require("support.http_patch")
local url = require("socket.url") local url = require("socket.url")
local okay, http = pcall(require, "ssl.https") local okay, http = pcall(require, "ssl.https")
if not okay then if not okay then
@ -53,6 +54,7 @@ function obj.connect(address, port) -- Opens a new TCP connection. Returns the h
-- TODO: not OC behaviour, but needed to prevent hanging -- TODO: not OC behaviour, but needed to prevent hanging
client:settimeout(10) client:settimeout(10)
local connected = false local connected = false
local closed = false
local function connect() local function connect()
cprint("(socket) connect",host,port) cprint("(socket) connect",host,port)
local did, err = client:connect(host,port) local did, err = client:connect(host,port)
@ -60,39 +62,49 @@ function obj.connect(address, port) -- Opens a new TCP connection. Returns the h
if did then if did then
connected = true connected = true
client:settimeout(0) client:settimeout(0)
else
pcall(client.close,client)
closed = true
end end
end end
local fakesocket = { local fakesocket = {
read = function(n) read = function(n)
cprint("(socket) read",n) cprint("(socket) read",n)
-- TODO: Error handling -- TODO: Better Error handling
if closed then return nil, "connection lost" end
if not connected then connect() return "" end if not connected then connect() return "" end
if type(n) ~= "number" then n = math.huge end if type(n) ~= "number" then n = math.huge end
local data, err, part = client:receive(n) local data, err, part = client:receive(n)
if err == nil or err == "timeout" or part ~= "" then if err == nil or err == "timeout" or part ~= "" then
return data or part return data or part
else else
if err == "closed" then closed = true err = "connection lost" end
return nil, err return nil, err
end end
end, end,
write = function(data) write = function(data)
cprint("(socket) write",data) cprint("(socket) write",data)
-- TODO: Better Error handling
if closed then return nil, "connection lost" end
if not connected then connect() return 0 end if not connected then connect() return 0 end
checkArg(1,data,"string") checkArg(1,data,"string")
local data, err, part = client:send(data) local data, err, part = client:send(data)
if err == nil or err == "timeout" or part ~= 0 then if err == nil or err == "timeout" or part ~= 0 then
return data or part return data or part
else else
if err == "closed" then closed = true err = "connection lost" end
return nil, err return nil, err
end end
end, end,
close = function() close = function()
cprint("(socket) close") cprint("(socket) close")
pcall(client.close,client) pcall(client.close,client)
closed = true
end, end,
finishConnect = function() finishConnect = function()
cprint("(socket) finishConnect") cprint("(socket) finishConnect")
-- TODO: Does this actually error? -- TODO: Does this actually error?
if closed then return nil, "connection lost" end
return connected return connected
end end
} }
@ -109,15 +121,43 @@ function obj.request(url, postData) -- Starts an HTTP request. If this returns t
postData = nil postData = nil
end end
-- TODO: This works ... but is slow. -- TODO: This works ... but is slow.
local page, _, headers, status = http.request(url, postData) -- TODO: Infact so slow, it can trigger the machine's sethook, so we have to work around that.
local protocol, code, message = status:match("(.-) (.-) (.*)") local hookf,hookm,hookc = debug.gethook()
code = tonumber(code) local co = coroutine.running()
debug.sethook(co)
local page, err, headers, status = http.request(url, postData)
debug.sethook(co,hookf,hookm,hookc)
if not page then
cprint("(request) request failed",err)
end
-- Experimental fix for headers
if headers ~= nil then
local oldheaders = headers
headers = {}
for k,v in pairs(oldheaders) do
local name = k:gsub("^.",string.upper):gsub("%-.",string.upper)
if type(v) == "table" then
v.n = #v
headers[name] = v
else
headers[name] = {v,n=1}
end
end
end
local procotol, code, message
if status then
protocol, code, message = status:match("(.-) (.-) (.*)")
code = tonumber(code)
end
local closed = false
local fakesocket = { local fakesocket = {
read = function(n) read = function(n)
cprint("(socket) read",n) cprint("(socket) read",n)
-- OC doesn't actually return n bytes when requested. -- OC doesn't actually return n bytes when requested.
if page == nil then if closed then
return nil, "connection lost" return nil, "connection lost"
elseif headers == nil then
return nil, "Connection refused"
elseif page == "" then elseif page == "" then
return nil return nil
else else
@ -129,14 +169,23 @@ function obj.request(url, postData) -- Starts an HTTP request. If this returns t
end, end,
response = function() response = function()
cprint("(socket) response") cprint("(socket) response")
if headers == nil then
return nil
end
return code, message, headers return code, message, headers
end, end,
close = function() close = function()
cprint("(request) close") cprint("(request) close")
closed = true
page = nil page = nil
end, end,
finishConnect = function() finishConnect = function()
cprint("(socket) finishConnect") cprint("(socket) finishConnect")
if closed then
return nil, "connection lost"
elseif headers == nil then
return nil, "Connection refused"
end
return true return true
end end
} }

View File

@ -0,0 +1,43 @@
-- Welcome to hack town!
-- Patch luasocket's http library to be less stupid
local function gsub_escape(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")..""
end
cprint("http_patch start")
-- Patch data
local patches = {
{[[if headers[name] then headers[name] = headers[name] .. ", " .. value]],[[if headers[name] then if type(headers[name]) == "string" then headers[name] = {headers[name]} end headers[name][#headers[name]+1] = value]]},
}
package.loaded["socket.http"] = nil
local path = package.searchpath("socket.http",package.path)
if path then
local file, err = io.open(path,"rb")
if not file then
cprint("Failed to patch socket.http: " .. err)
return
end
local data = file:read("*a")
file:close()
for i = 1,#patches do
local newdata = data:gsub(gsub_escape(patches[i][1]), (patches[i][2]:gsub("%%","%%%%")..""))
if newdata == data then
cprint("Patch " .. i .. " failed")
else
data = newdata
end
end
local fn, err = load(data,"="..path)
if not fn then
cprint("Failed to compile socket.http: " .. err)
return
end
local ok, err = pcall(fn)
if not ok then
cprint("Failed to load socket.http: " .. err)
return
end
package.loaded["socket.http"] = err
else
cprint("Could not find socket.http")
end
cprint("http_patch end")