I was looking for http library in lua and landed in luasocket.http page. It isn’t well documented, sent few GET, POST, PUT requests and figured few bits. This blog post aims in bridging the gap(code examples).

In this example, I will use httpbin as target site. The complete code is available as gist.

The following code should be executed like a standalone lua file( lua lua_httpbin.lua ) and while executing the code in interpreter please make local variables http, ltn12, base_url as global variables.

local http = require("socket.http") local ltn12 = require("ltn12") local base_url = "https://httpbin.org/" --Helper for priniting nested table function deep_print(tbl) for i, v in pairs(tbl) do if type(v) == "table" then deep_print(v) else print(i, v) end end end function http_request( args ) --http.request(url [, body]) --http.request{ -- url = string, -- [sink = LTN12 sink,] -- [method = string,] -- [headers = header-table,] -- [source = LTN12 source], -- [step = LTN12 pump step,] -- [proxy = string,] -- [redirect = boolean,] -- [create = function] --} -- -- local resp, r = {}, {} if args.endpoint then local params = "" if args.method == nil or args.method == "GET" then -- prepare query parameters like http://xyz.com?q=23&a=2 if args.params then for i, v in pairs(args.params) do params = params .. i .. "=" .. v .. "&" end end end params = string.sub(params, 1, -2) local url = "" if params then url = base_url .. args.endpoint .. "?" .. params else url = base_url .. args.endpoint end client, code, headers, status = http.request{url=url, sink=ltn12.sink.table(resp), method=args.method or "GET", headers=args.headers, source=args.source, step=args.step, proxy=args.proxy, redirect=args.redirect, create=args.create } r['code'], r['headers'], r['status'], r['response'] = code, headers, status, resp else error("endpoint is missing") end return r end

http_request takes table as an argument which is almost similar to http.request function with an extra parameter params which will be helpful for passing parameters to GET request.

http_request{endpoint=endpoint, params={age=23, name="kracekumar"}} rather than http_request{endpoint=endpoint .. "?age=23&name=kracekumar"} .

Remaining piece of code

function main() -- Normal GET request endpoint = "/user-agent" print(endpoint) deep_print(http_request{endpoint=endpoint}) -- GET request with parameters endpoint = "/get" print(endpoint) deep_print(http_request{endpoint=endpoint, params={age=23, name ="kracekumar"}}) -- POST request with form endpoint = "/post" print(endpoint) local req_body = "a=2" local headers = { ["Content-Type"] = "application/x-www-form-urlencoded"; ["Content-Length"] = #req_body; } deep_print(http_request{endpoint=endpoint, method="POST", source=ltn12.source.string(req_body), headers=headers}) -- PATCH Method endpoint = "/patch" print(endpoint) deep_print(http_request{endpoint=endpoint, method="PATCH"}) -- PUT Method endpoint = "/put" print(endpoint) deep_print(http_request{endpoint=endpoint, method="PUT", source =ltn12.source.string("a=23")}) -- Delete method endpoint = "/delete" print(endpoint) deep_print(http_request{endpoint=endpoint, method="DELETE", source=ltn12.source.string("a=23")}) end main()

output

# First lua is directory name ➜ lua lua lua_httpbin.lua /user-agent status HTTP/1.1 200 OK code 200 connection Close content-type application/json date Fri, 19 Jul 2013 07:32:14 GMT content-length 37 access-control-allow-origin * server gunicorn/0.17.4 1 { "user-agent": "LuaSocket 2.0.2" } /get status HTTP/1.1 200 OK code 200 connection Close content-type application/json date Fri, 19 Jul 2013 07:32:15 GMT content-length 258 access-control-allow-origin * server gunicorn/0.17.4 1 { "headers": { "Host": "httpbin.org", "Connection": "close", "User-Agent": "LuaSocket 2.0.2" }, "url": "http://httpbin.org/get?age=23&name=kracekumar", "args": { "age": "23", "name": "kracekumar" }, "origin": "106.51.166.47" } /post status HTTP/1.1 200 OK code 200 connection Close content-type application/json date Fri, 19 Jul 2013 07:41:54 GMT content-length 350 access-control-allow-origin * server gunicorn/0.17.4 1 { "json": null, "origin": "106.51.166.47", "form": { "a": "2" }, "url": "http://httpbin.org/post", "args": {}, "headers": { "Connection": "close", "Host": "httpbin.org", "User-Agent": "LuaSocket 2.0.2", "Content-Type": "application/x-www-form-urlencoded", "Content-Length": "3" }, "files": {}, "data": "" } /patch status HTTP/1.1 200 OK code 200 connection Close content-type application/json date Fri, 19 Jul 2013 07:32:17 GMT content-length 251 access-control-allow-origin * server gunicorn/0.17.4 1 { "files": {}, "headers": { "Host": "httpbin.org", "Connection": "close", "User-Agent": "LuaSocket 2.0.2" }, "args": {}, "json": null, "form": {}, "origin": "106.51.166.47", "url": "http://httpbin.org/patch", "data": "" } /put status HTTP/1.1 200 OK code 200 connection Close content-type application/json date Fri, 19 Jul 2013 07:32:18 GMT content-length 276 access-control-allow-origin * server gunicorn/0.17.4 1 { "json": null, "origin": "106.51.166.47", "form": {}, "url": "http://httpbin.org/put", "args": {}, "headers": { "Connection": "close", "Host": "httpbin.org", "User-Agent": "LuaSocket 2.0.2", "Content-Length": "0" }, "files": {}, "data": "" } /delete status HTTP/1.1 200 OK code 200 connection Close content-type application/json date Fri, 19 Jul 2013 07:32:19 GMT content-length 223 access-control-allow-origin * server gunicorn/0.17.4 1 { "args": {}, "json": null, "headers": { "Connection": "close", "User-Agent": "LuaSocket 2.0.2", "Host": "httpbin.org" }, "origin": "106.51.166.47", "data": "", "url": "http://httpbin.org/delete" }

My 2p

When sending GET request don’t send paramters in request body. When sending POST request set custom header for form.

My experience of luasocket.http produces solid reason to create a new library with helper functions like requests.

See also