src/https.lua

1
----------------------------------------------------------------------------
2
-- LuaSec 0.4
3
-- Copyright (C) 2009 PUC-Rio
4
--
5
-- Author: Pablo Musa
6
-- Author: Tomas Guisasola
7
---------------------------------------------------------------------------
8
 
9
local socket = require("socket")
10
local ssl    = require("ssl")
11
local ltn12  = require("ltn12")
12
local http   = require("socket.http")
13
local url    = require("socket.url")
14
 
15
local table  = require("table")
16
local string = require("string")
17
 
18
local try          = socket.try
19
local type         = type
20
local pairs        = pairs
21
local getmetatable = getmetatable
22
 
23
module("ssl.https")
24
 
25
_VERSION   = "0.4"
26
_COPYRIGHT = "LuaSec 0.4 - Copyright (C) 2009 PUC-Rio"
27
 
28
-- Default settings
29
PORT = 443
30
 
31
local cfg = {
32
  protocol = "tlsv1",
33
  options  = "all",
34
  verify   = "none",
35
}
36
 
37
--------------------------------------------------------------------
38
-- Auxiliar Functions
39
--------------------------------------------------------------------
40
 
41
-- Insert default HTTPS port.
42
local function default_https_port(u)
43
   return url.build(url.parse(u, {port = PORT}))
44
end
45
 
46
-- Convert an URL to a table according to Luasocket needs.
47
local function urlstring_totable(url, body, result_table)
48
   url = {
49
      url = default_https_port(url),
50
      method = body and "POST" or "GET",
51
      sink = ltn12.sink.table(result_table)
52
   }
53
   if body then
54
      url.source = ltn12.source.string(body)
55
      url.headers = {
56
         ["content-length"] = #body,
57
         ["content-type"] = "application/x-www-form-urlencoded",
58
      }
59
   end
60
   return url
61
end
62
 
63
-- Forward calls to the real connection object.
64
local function reg(conn)
65
   local mt = getmetatable(conn.sock).__index
66
   for name, method in pairs(mt) do
67
      if type(method) == "function" then
68
         conn[name] = function (self, ...)
69
                         return method(self.sock, ...)
70
                      end
71
      end
72
   end
73
end
74
 
75
-- Return a function which performs the SSL/TLS connection.
76
local function tcp(params)
77
   params = params or {}
78
   -- Default settings
79
   for k, v in pairs(cfg) do 
80
      params[k] = params[k] or v
81
   end
82
   -- Force client mode
83
   params.mode = "client"
84
   -- 'create' function for LuaSocket
85
   return function ()
86
      local conn = {}
87
      conn.sock = try(socket.tcp())
88
      local st = getmetatable(conn.sock).__index.settimeout
89
      function conn:settimeout(...)
90
         return st(self.sock, ...)
91
      end
92
      -- Replace TCP's connection function
93
      function conn:connect(host, port)
94
         try(self.sock:connect(host, port))
95
         self.sock = try(ssl.wrap(self.sock, params))
96
         try(self.sock:dohandshake())
97
         reg(self, getmetatable(self.sock))
98
         return 1
99
      end
100
      return conn
101
  end
102
end
103
 
104
--------------------------------------------------------------------
105
-- Main Function
106
--------------------------------------------------------------------
107
 
108
-- Make a HTTP request over secure connection.  This function receives
109
--  the same parameters of LuaSocket's HTTP module (except 'proxy' and
110
--  'redirect') plus LuaSec parameters.
111
--
112
-- @param url mandatory (string or table)
113
-- @param body optional (string)
114
-- @return (string if url == string or 1), code, headers, status
115
--
116
function request(url, body)
117
  local result_table = {}
118
  local stringrequest = type(url) == "string"
119
  if stringrequest then
120
    url = urlstring_totable(url, body, result_table)
121
  else
122
    url.url = default_https_port(url.url)
123
  end
124
  if http.PROXY or url.proxy then
125
    return nil, "proxy not supported"
126
  elseif url.redirect then
127
    return nil, "redirect not supported"
128
  elseif url.create then
129
    return nil, "create function not permitted"
130
  end
131
  -- New 'create' function to establish a secure connection
132
  url.create = tcp(url)
133
  local res, code, headers, status = http.request(url)
134
  if res and stringrequest then
135
    return table.concat(result_table), code, headers, status
136
  end
137
  return res, code, headers, status
138
end