|
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 |