215 lines
4.8 KiB
Go
215 lines
4.8 KiB
Go
package gluahttp
|
|
|
|
import "github.com/yuin/gopher-lua"
|
|
import "net/http"
|
|
import "fmt"
|
|
import "errors"
|
|
import "io"
|
|
import "io/ioutil"
|
|
import "strings"
|
|
|
|
type httpModule struct {
|
|
client *http.Client
|
|
}
|
|
|
|
type empty struct{}
|
|
|
|
func NewHttpModule(client *http.Client) *httpModule {
|
|
return &httpModule{
|
|
client: client,
|
|
}
|
|
}
|
|
|
|
func (h *httpModule) Loader(L *lua.LState) int {
|
|
mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
|
|
"get": h.get,
|
|
"delete": h.delete,
|
|
"head": h.head,
|
|
"patch": h.patch,
|
|
"post": h.post,
|
|
"put": h.put,
|
|
"request": h.request,
|
|
"request_batch": h.requestBatch,
|
|
})
|
|
registerHttpResponseType(mod, L)
|
|
L.Push(mod)
|
|
return 1
|
|
}
|
|
|
|
func (h *httpModule) get(L *lua.LState) int {
|
|
return h.doRequestAndPush(L, "get", L.ToString(1), L.ToTable(2))
|
|
}
|
|
|
|
func (h *httpModule) delete(L *lua.LState) int {
|
|
return h.doRequestAndPush(L, "delete", L.ToString(1), L.ToTable(2))
|
|
}
|
|
|
|
func (h *httpModule) head(L *lua.LState) int {
|
|
return h.doRequestAndPush(L, "head", L.ToString(1), L.ToTable(2))
|
|
}
|
|
|
|
func (h *httpModule) patch(L *lua.LState) int {
|
|
return h.doRequestAndPush(L, "patch", L.ToString(1), L.ToTable(2))
|
|
}
|
|
|
|
func (h *httpModule) post(L *lua.LState) int {
|
|
return h.doRequestAndPush(L, "post", L.ToString(1), L.ToTable(2))
|
|
}
|
|
|
|
func (h *httpModule) put(L *lua.LState) int {
|
|
return h.doRequestAndPush(L, "put", L.ToString(1), L.ToTable(2))
|
|
}
|
|
|
|
func (h *httpModule) request(L *lua.LState) int {
|
|
return h.doRequestAndPush(L, L.ToString(1), L.ToString(2), L.ToTable(3))
|
|
}
|
|
|
|
func (h *httpModule) requestBatch(L *lua.LState) int {
|
|
requests := L.ToTable(1)
|
|
amountRequests := requests.Len()
|
|
|
|
errs := make([]error, amountRequests)
|
|
responses := make([]*lua.LUserData, amountRequests)
|
|
sem := make(chan empty, amountRequests)
|
|
|
|
i := 0
|
|
|
|
requests.ForEach(func(_ lua.LValue, value lua.LValue) {
|
|
requestTable := toTable(value)
|
|
|
|
if requestTable != nil {
|
|
method := requestTable.RawGet(lua.LNumber(1)).String()
|
|
url := requestTable.RawGet(lua.LNumber(2)).String()
|
|
options := toTable(requestTable.RawGet(lua.LNumber(3)))
|
|
|
|
go func(i int, L *lua.LState, method string, url string, options *lua.LTable) {
|
|
response, err := h.doRequest(L, method, url, options)
|
|
|
|
if err == nil {
|
|
errs[i] = nil
|
|
responses[i] = response
|
|
} else {
|
|
errs[i] = err
|
|
responses[i] = nil
|
|
}
|
|
|
|
sem <- empty{}
|
|
}(i, L, method, url, options)
|
|
} else {
|
|
errs[i] = errors.New("Request must be a table")
|
|
responses[i] = nil
|
|
sem <- empty{}
|
|
}
|
|
|
|
i = i + 1
|
|
})
|
|
|
|
for i = 0; i < amountRequests; i++ {
|
|
<-sem
|
|
}
|
|
|
|
hasErrors := false
|
|
errorsTable := L.NewTable()
|
|
responsesTable := L.NewTable()
|
|
for i = 0; i < amountRequests; i++ {
|
|
if errs[i] == nil {
|
|
responsesTable.Append(responses[i])
|
|
errorsTable.Append(lua.LNil)
|
|
} else {
|
|
responsesTable.Append(lua.LNil)
|
|
errorsTable.Append(lua.LString(fmt.Sprintf("%s", errs[i])))
|
|
hasErrors = true
|
|
}
|
|
}
|
|
|
|
if hasErrors {
|
|
L.Push(responsesTable)
|
|
L.Push(errorsTable)
|
|
return 2
|
|
} else {
|
|
L.Push(responsesTable)
|
|
return 1
|
|
}
|
|
}
|
|
|
|
func (h *httpModule) doRequest(L *lua.LState, method string, url string, options *lua.LTable) (*lua.LUserData, error) {
|
|
req, err := http.NewRequest(strings.ToUpper(method), url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if options != nil {
|
|
if reqHeaders, ok := options.RawGet(lua.LString("headers")).(*lua.LTable); ok {
|
|
reqHeaders.ForEach(func(key lua.LValue, value lua.LValue) {
|
|
req.Header.Set(key.String(), value.String())
|
|
})
|
|
}
|
|
|
|
if reqCookies, ok := options.RawGet(lua.LString("cookies")).(*lua.LTable); ok {
|
|
reqCookies.ForEach(func(key lua.LValue, value lua.LValue) {
|
|
req.AddCookie(&http.Cookie{Name: key.String(), Value: value.String()})
|
|
})
|
|
}
|
|
|
|
switch reqQuery := options.RawGet(lua.LString("query")).(type) {
|
|
case *lua.LNilType:
|
|
break
|
|
|
|
case lua.LString:
|
|
req.URL.RawQuery = reqQuery.String()
|
|
break
|
|
}
|
|
|
|
switch reqForm := options.RawGet(lua.LString("form")).(type) {
|
|
case *lua.LNilType:
|
|
break
|
|
|
|
case lua.LString:
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.Body = ioutil.NopCloser(strings.NewReader(reqForm.String()))
|
|
break
|
|
}
|
|
}
|
|
|
|
res, err := h.client.Do(req)
|
|
|
|
if err != nil {
|
|
if res != nil {
|
|
io.Copy(ioutil.Discard, res.Body)
|
|
defer res.Body.Close()
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
// TODO: Add a way to discard body
|
|
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return newHttpResponse(res, &body, len(body), L), nil
|
|
}
|
|
|
|
func (h *httpModule) doRequestAndPush(L *lua.LState, method string, url string, options *lua.LTable) int {
|
|
response, err := h.doRequest(L, method, url, options)
|
|
|
|
if err != nil {
|
|
L.Push(lua.LNil)
|
|
L.Push(lua.LString(fmt.Sprintf("%s", err)))
|
|
return 2
|
|
}
|
|
|
|
L.Push(response)
|
|
return 1
|
|
}
|
|
|
|
func toTable(v lua.LValue) *lua.LTable {
|
|
if lv, ok := v.(*lua.LTable); ok {
|
|
return lv
|
|
}
|
|
return nil
|
|
}
|