Netrequire Server

Catalogue > netrequire.lua

Netrequire client implementation. This is what you need to download the modules easily from here from within Iguana.

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
-- $Revision: 1.18 $
-- $Date: 2012-12-06 17:11:53 $
-- $.Path: lua_netrequire $

--
-- The netrequire module
-- Copyright (c) 2011-2012 iNTERFACEWARE Inc. ALL RIGHTS RESERVED
-- iNTERFACEWARE permits you to use, modify, and distribute this file in accordance
-- with the terms of the iNTERFACEWARE license agreement accompanying the software
-- in which it is used.
--

local function trace(a,b,c,d) end

local HEADER_MD5 = 'X-Content-Md5'
local HEADER_MESSAGE = 'X-CodeServer-Message'

local function readFile(FileName)
   assert(FileName, "FileName is nil - logical error")
   local File = io.open(FileName, 'r')
   local Contents = File:read('*a')
   File:close()
   return Contents
end

local function writeFile(FileName, Contents)
   assert(FileName, "FileName is nil - logical error")
   local File = io.open( FileName, 'w')
   File:write(Contents)
   File:close()
end

local function fileExistsAndSize(FileName)
   if os.stat then
      local Stat = os.stat(FileName)
      if Stat then
         return true, Stat.size
      end
      return false, 0
   end
   -- Iguana 5.0 compatibility - no os.stat()
   local Success, Handle, Message = pcall(io.open, FileName, 'r')
   local Result = Success and (Handle~=nil)
   local FileSize=0
   if Handle then 
      local FileContent = Handle:read('*a')
      FileSize = #FileContent
      Handle:close()
   end
   return Result, FileSize
end

function netrequire(Name)
   
   if type(Name) ~= type('') then
      error('Name must be a string',2)
   end
   
   local _, ValidNameMatch = Name:gsub('^[0-9a-zA-Z_-]+$','')
   trace(ValidNameMatch)
   if ValidNameMatch ~= 1 then
      error("Name can only contain letters, numbers, dashes, underscores",2)
   end

   if not iguana.isTest() then
      -- if we're in runtime mode, we don't mess with any of this stuff
      return require(Name)
   end
   
   local SharedFileName
   if type(iguana.project.root)=='function' then
      SharedFileName = iguana.project.root() ..'/shared/'..Name..'.lua'
   else
      SharedFileName = iguana.project.root ..'/shared/'..Name..'.lua'      
   end
   trace(SharedFileName)
   
   local FileExists, FileSize = fileExistsAndSize(SharedFileName) 
   if FileExists and FileSize>0 then
      if FileSize >= 16 then
         return require(Name)
      end
      -- if FileSize is less than 16, check if it's just whitespace
      local FileContent = readFile(SharedFileName)
      local Empty, Match = FileContent:gsub('^[ \t\r\n]+$','') 
      if Match==0 then
         -- not just whitespace
         return require(Name)
      end
      -- just whitespace - proceed with fetching
   end
   
   local QueryFileName = Name..'.lua'
   trace(QueryFileName)

   local Url = 'http://code.interfaceware.com/code'
   ..'?file='..filter.uri.enc(QueryFileName)
   trace(Url)
   
   local Success, ResponseBody, ResponseStatus, ResponseHeaders = pcall(net.http.get, {url=Url, live=true, timeout=5})
   trace('Response:', Success, ResponseStatus, ResponseHeaders, ResponseBody)
   local ShouldRetry = not Success or ResponseStatus~=200
   -- Retry if failed, except for known cases where it's unlikely to help
   if Success and ResponseHeaders and ResponseHeaders[HEADER_MESSAGE] then
      ShouldRetry = false -- we reached the Code Server, and it supplied a message - it should have the reason
   elseif Success and ResponseStatus==404 then
      ShouldRetry = false -- file not found - retrying won't help
   end
   if ShouldRetry then
      -- retry - in case something was being restarted
      util.sleep(7000)
      Success, ResponseBody, ResponseStatus, ResponseHeaders = pcall(net.http.get, {url=Url, live=true, timeout=5})
   end
   trace('Response:', Success, ResponseStatus, ResponseHeaders, ResponseBody)
   
   if not Success then
      error('Request to Code Server failed. Try again, or contact support if the error persists.\n'
         ..'Error was: '..ResponseBody,2)
   end

   if ResponseStatus ~= 200 then
      local ServerMessage = ResponseHeaders[HEADER_MESSAGE]
      if ServerMessage then
         local ErrorMessage = 'Received response from Code Server: '..ServerMessage
         if ResponseBody ~= ServerMessage then
            ErrorMessage = ErrorMessage .. '\n' .. ResponseBody
         end
         error(ErrorMessage, 2)
      end
      if ResponseStatus == 503 then
         error('Code Server is unavailable. Try again, or contact support if the error persists.',2)
      end
      error('Received unexpected response code '..ResponseStatus..' from Code Server - contact support',2)
   end
   
   if not ResponseHeaders[HEADER_MD5] then
      error('Response headers are missing MD5 hash - contact support',2)
   end
   if ResponseHeaders[HEADER_MD5] ~= util.md5(ResponseBody) then
      error("Response content doesn't match MD5 hash - contact support",2)
   end

   writeFile(SharedFileName, ResponseBody)
   return require(Name)     
end

return { load=netrequire }