源战役客户端
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

381 řádky
14 KiB

před 4 týdny
  1. -- -----------------------------------------------------------------------------
  2. -- -- HTTP/1.1 client support for the Lua language.
  3. -- -- LuaSocket toolkit.
  4. -- -- Author: Diego Nehab
  5. -- -----------------------------------------------------------------------------
  6. -- -----------------------------------------------------------------------------
  7. -- -- Declare module and import dependencies
  8. -- -------------------------------------------------------------------------------
  9. -- local socket = require("socket")
  10. -- local url = require("socket.url")
  11. -- local ltn12 = require("ltn12")
  12. -- local mime = require("mime")
  13. -- local string = require("string")
  14. -- local headers = require("socket.headers")
  15. -- local base = _G
  16. -- local table = require("table")
  17. -- socket.http = {}
  18. -- local _M = socket.http
  19. -- -----------------------------------------------------------------------------
  20. -- -- Program constants
  21. -- -----------------------------------------------------------------------------
  22. -- -- connection timeout in seconds
  23. -- _M.TIMEOUT = 60
  24. -- -- user agent field sent in request
  25. -- _M.USERAGENT = socket._VERSION
  26. -- -- supported schemes
  27. -- local SCHEMES = { ["http"] = true }
  28. -- -- default port for document retrieval
  29. -- local PORT = 80
  30. -- -----------------------------------------------------------------------------
  31. -- -- Reads MIME headers from a connection, unfolding where needed
  32. -- -----------------------------------------------------------------------------
  33. -- local function receiveheaders(sock, headers)
  34. -- local line, name, value, err
  35. -- headers = headers or {}
  36. -- -- get first line
  37. -- line, err = sock:receive()
  38. -- if err then return nil, err end
  39. -- -- headers go until a blank line is found
  40. -- while line ~= "" do
  41. -- -- get field-name and value
  42. -- name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
  43. -- if not (name and value) then return nil, "malformed reponse headers" end
  44. -- name = string.lower(name)
  45. -- -- get next line (value might be folded)
  46. -- line, err = sock:receive()
  47. -- if err then return nil, err end
  48. -- -- unfold any folded values
  49. -- while string.find(line, "^%s") do
  50. -- value = value .. line
  51. -- line = sock:receive()
  52. -- if err then return nil, err end
  53. -- end
  54. -- -- save pair in table
  55. -- if headers[name] then headers[name] = headers[name] .. ", " .. value
  56. -- else headers[name] = value end
  57. -- end
  58. -- return headers
  59. -- end
  60. -- -----------------------------------------------------------------------------
  61. -- -- Extra sources and sinks
  62. -- -----------------------------------------------------------------------------
  63. -- socket.sourcet["http-chunked"] = function(sock, headers)
  64. -- return base.setmetatable({
  65. -- getfd = function() return sock:getfd() end,
  66. -- dirty = function() return sock:dirty() end
  67. -- }, {
  68. -- __call = function()
  69. -- -- get chunk size, skip extention
  70. -- local line, err = sock:receive()
  71. -- if err then return nil, err end
  72. -- local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
  73. -- if not size then return nil, "invalid chunk size" end
  74. -- -- was it the last chunk?
  75. -- if size > 0 then
  76. -- -- if not, get chunk and skip terminating CRLF
  77. -- local chunk, err, part = sock:receive(size)
  78. -- if chunk then sock:receive() end
  79. -- return chunk, err
  80. -- else
  81. -- -- if it was, read trailers into headers table
  82. -- headers, err = receiveheaders(sock, headers)
  83. -- if not headers then return nil, err end
  84. -- end
  85. -- end
  86. -- })
  87. -- end
  88. -- socket.sinkt["http-chunked"] = function(sock)
  89. -- return base.setmetatable({
  90. -- getfd = function() return sock:getfd() end,
  91. -- dirty = function() return sock:dirty() end
  92. -- }, {
  93. -- __call = function(self, chunk, err)
  94. -- if not chunk then return sock:send("0\r\n\r\n") end
  95. -- local size = string.format("%X\r\n", string.len(chunk))
  96. -- return sock:send(size .. chunk .. "\r\n")
  97. -- end
  98. -- })
  99. -- end
  100. -- -----------------------------------------------------------------------------
  101. -- -- Low level HTTP API
  102. -- -----------------------------------------------------------------------------
  103. -- local metat = { __index = {} }
  104. -- function _M.open(host, port, create)
  105. -- -- create socket with user connect function, or with default
  106. -- local c = socket.try((create or socket.tcp)())
  107. -- local h = base.setmetatable({ c = c }, metat)
  108. -- -- create finalized try
  109. -- h.try = socket.newtry(function() h:close() end)
  110. -- -- set timeout before connecting
  111. -- h.try(c:settimeout(_M.TIMEOUT))
  112. -- h.try(c:connect(host, port or PORT))
  113. -- -- here everything worked
  114. -- return h
  115. -- end
  116. -- function metat.__index:sendrequestline(method, uri)
  117. -- local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
  118. -- return self.try(self.c:send(reqline))
  119. -- end
  120. -- function metat.__index:sendheaders(tosend)
  121. -- local canonic = headers.canonic
  122. -- local h = "\r\n"
  123. -- for f, v in base.pairs(tosend) do
  124. -- h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h
  125. -- end
  126. -- self.try(self.c:send(h))
  127. -- return 1
  128. -- end
  129. -- function metat.__index:sendbody(headers, source, step)
  130. -- source = source or ltn12.source.empty()
  131. -- step = step or ltn12.pump.step
  132. -- -- if we don't know the size in advance, send chunked and hope for the best
  133. -- local mode = "http-chunked"
  134. -- if headers["content-length"] then mode = "keep-open" end
  135. -- return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
  136. -- end
  137. -- function metat.__index:receivestatusline()
  138. -- local status = self.try(self.c:receive(5))
  139. -- -- identify HTTP/0.9 responses, which do not contain a status line
  140. -- -- this is just a heuristic, but is what the RFC recommends
  141. -- if status ~= "HTTP/" then return nil, status end
  142. -- -- otherwise proceed reading a status line
  143. -- status = self.try(self.c:receive("*l", status))
  144. -- local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
  145. -- return self.try(base.tonumber(code), status)
  146. -- end
  147. -- function metat.__index:receiveheaders()
  148. -- return self.try(receiveheaders(self.c))
  149. -- end
  150. -- function metat.__index:receivebody(headers, sink, step)
  151. -- sink = sink or ltn12.sink.null()
  152. -- step = step or ltn12.pump.step
  153. -- local length = base.tonumber(headers["content-length"])
  154. -- local t = headers["transfer-encoding"] -- shortcut
  155. -- local mode = "default" -- connection close
  156. -- if t and t ~= "identity" then mode = "http-chunked"
  157. -- elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
  158. -- return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
  159. -- sink, step))
  160. -- end
  161. -- function metat.__index:receive09body(status, sink, step)
  162. -- local source = ltn12.source.rewind(socket.source("until-closed", self.c))
  163. -- source(status)
  164. -- return self.try(ltn12.pump.all(source, sink, step))
  165. -- end
  166. -- function metat.__index:close()
  167. -- return self.c:close()
  168. -- end
  169. -- -----------------------------------------------------------------------------
  170. -- -- High level HTTP API
  171. -- -----------------------------------------------------------------------------
  172. -- local function adjusturi(reqt)
  173. -- local u = reqt
  174. -- -- if there is a proxy, we need the full url. otherwise, just a part.
  175. -- if not reqt.proxy and not _M.PROXY then
  176. -- u = {
  177. -- path = socket.try(reqt.path, "invalid path 'nil'"),
  178. -- params = reqt.params,
  179. -- query = reqt.query,
  180. -- fragment = reqt.fragment
  181. -- }
  182. -- end
  183. -- return url.build(u)
  184. -- end
  185. -- local function adjustproxy(reqt)
  186. -- local proxy = reqt.proxy or _M.PROXY
  187. -- if proxy then
  188. -- proxy = url.parse(proxy)
  189. -- return proxy.host, proxy.port or 3128
  190. -- else
  191. -- return reqt.host, reqt.port
  192. -- end
  193. -- end
  194. -- local function adjustheaders(reqt)
  195. -- -- default headers
  196. -- local host = string.gsub(reqt.authority, "^.-@", "")
  197. -- local lower = {
  198. -- ["user-agent"] = _M.USERAGENT,
  199. -- ["host"] = host,
  200. -- ["connection"] = "close, TE",
  201. -- ["te"] = "trailers"
  202. -- }
  203. -- -- if we have authentication information, pass it along
  204. -- if reqt.user and reqt.password then
  205. -- lower["authorization"] =
  206. -- "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
  207. -- end
  208. -- -- if we have proxy authentication information, pass it along
  209. -- local proxy = reqt.proxy or _M.PROXY
  210. -- if proxy then
  211. -- proxy = url.parse(proxy)
  212. -- if proxy.user and proxy.password then
  213. -- lower["proxy-authorization"] =
  214. -- "Basic " .. (mime.b64(proxy.user .. ":" .. proxy.password))
  215. -- end
  216. -- end
  217. -- -- override with user headers
  218. -- for i,v in base.pairs(reqt.headers or lower) do
  219. -- lower[string.lower(i)] = v
  220. -- end
  221. -- return lower
  222. -- end
  223. -- -- default url parts
  224. -- local default = {
  225. -- host = "",
  226. -- port = PORT,
  227. -- path ="/",
  228. -- scheme = "http"
  229. -- }
  230. -- local function adjustrequest(reqt)
  231. -- -- parse url if provided
  232. -- local nreqt = reqt.url and url.parse(reqt.url, default) or {}
  233. -- -- explicit components override url
  234. -- for i,v in base.pairs(reqt) do nreqt[i] = v end
  235. -- if nreqt.port == "" then nreqt.port = PORT end
  236. -- if not (nreqt.host and nreqt.host ~= "") then
  237. -- socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'")
  238. -- end
  239. -- -- compute uri if user hasn't overriden
  240. -- nreqt.uri = reqt.uri or adjusturi(nreqt)
  241. -- -- adjust headers in request
  242. -- nreqt.headers = adjustheaders(nreqt)
  243. -- -- ajust host and port if there is a proxy
  244. -- nreqt.host, nreqt.port = adjustproxy(nreqt)
  245. -- return nreqt
  246. -- end
  247. -- local function shouldredirect(reqt, code, headers)
  248. -- local location = headers.location
  249. -- if not location then return false end
  250. -- location = string.gsub(location, "%s", "")
  251. -- if location == "" then return false end
  252. -- local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:")
  253. -- if scheme and not SCHEMES[scheme] then return false end
  254. -- return (reqt.redirect ~= false) and
  255. -- (code == 301 or code == 302 or code == 303 or code == 307) and
  256. -- (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
  257. -- and (not reqt.nredirects or reqt.nredirects < 5)
  258. -- end
  259. -- local function shouldreceivebody(reqt, code)
  260. -- if reqt.method == "HEAD" then return nil end
  261. -- if code == 204 or code == 304 then return nil end
  262. -- if code >= 100 and code < 200 then return nil end
  263. -- return 1
  264. -- end
  265. -- -- forward declarations
  266. -- local trequest, tredirect
  267. -- --[[local]] function tredirect(reqt, location)
  268. -- local result, code, headers, status = trequest {
  269. -- -- the RFC says the redirect URL has to be absolute, but some
  270. -- -- servers do not respect that
  271. -- url = url.absolute(reqt.url, location),
  272. -- source = reqt.source,
  273. -- sink = reqt.sink,
  274. -- headers = reqt.headers,
  275. -- proxy = reqt.proxy,
  276. -- nredirects = (reqt.nredirects or 0) + 1,
  277. -- create = reqt.create
  278. -- }
  279. -- -- pass location header back as a hint we redirected
  280. -- headers = headers or {}
  281. -- headers.location = headers.location or location
  282. -- return result, code, headers, status
  283. -- end
  284. -- --[[local]] function trequest(reqt)
  285. -- -- we loop until we get what we want, or
  286. -- -- until we are sure there is no way to get it
  287. -- local nreqt = adjustrequest(reqt)
  288. -- local h = _M.open(nreqt.host, nreqt.port, nreqt.create)
  289. -- -- send request line and headers
  290. -- h:sendrequestline(nreqt.method, nreqt.uri)
  291. -- h:sendheaders(nreqt.headers)
  292. -- -- if there is a body, send it
  293. -- if nreqt.source then
  294. -- h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
  295. -- end
  296. -- local code, status = h:receivestatusline()
  297. -- -- if it is an HTTP/0.9 server, simply get the body and we are done
  298. -- if not code then
  299. -- h:receive09body(status, nreqt.sink, nreqt.step)
  300. -- return 1, 200
  301. -- end
  302. -- local headers
  303. -- -- ignore any 100-continue messages
  304. -- while code == 100 do
  305. -- headers = h:receiveheaders()
  306. -- code, status = h:receivestatusline()
  307. -- end
  308. -- headers = h:receiveheaders()
  309. -- -- at this point we should have a honest reply from the server
  310. -- -- we can't redirect if we already used the source, so we report the error
  311. -- if shouldredirect(nreqt, code, headers) and not nreqt.source then
  312. -- h:close()
  313. -- return tredirect(reqt, headers.location)
  314. -- end
  315. -- -- here we are finally done
  316. -- if shouldreceivebody(nreqt, code) then
  317. -- h:receivebody(headers, nreqt.sink, nreqt.step)
  318. -- end
  319. -- h:close()
  320. -- return 1, code, headers, status
  321. -- end
  322. -- -- turns an url and a body into a generic request
  323. -- local function genericform(u, b)
  324. -- local t = {}
  325. -- local reqt = {
  326. -- url = u,
  327. -- sink = ltn12.sink.table(t),
  328. -- target = t
  329. -- }
  330. -- if b then
  331. -- reqt.source = ltn12.source.string(b)
  332. -- reqt.headers = {
  333. -- ["content-length"] = string.len(b),
  334. -- ["content-type"] = "application/x-www-form-urlencoded"
  335. -- }
  336. -- reqt.method = "POST"
  337. -- end
  338. -- return reqt
  339. -- end
  340. -- _M.genericform = genericform
  341. -- local function srequest(u, b)
  342. -- local reqt = genericform(u, b)
  343. -- local _, code, headers, status = trequest(reqt)
  344. -- return table.concat(reqt.target), code, headers, status
  345. -- end
  346. -- _M.request = socket.protect(function(reqt, body)
  347. -- if base.type(reqt) == "string" then return srequest(reqt, body)
  348. -- else return trequest(reqt) end
  349. -- end)
  350. -- return _M