mirror of
https://github.com/MontFerret/ferret.git
synced 2025-07-13 01:20:35 +02:00
Added HTTP methods (#452)
* Added HTTP methods * Added unit tests * Fixed linting issue * Replaced localhost with 127.0.0.1 * Added random port generator * Disabled http tests * Disabled HTTP GET test
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
// Code generated from antlr/FqlLexer.g4 by ANTLR 4.7.2. DO NOT EDIT.
|
// Code generated from antlr/FqlLexer.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||||
|
|
||||||
package fql
|
package fql
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.7.2. DO NOT EDIT.
|
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||||
|
|
||||||
package fql // FqlParser
|
package fql // FqlParser
|
||||||
import (
|
import (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.7.2. DO NOT EDIT.
|
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||||
|
|
||||||
package fql // FqlParser
|
package fql // FqlParser
|
||||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.7.2. DO NOT EDIT.
|
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||||
|
|
||||||
package fql // FqlParser
|
package fql // FqlParser
|
||||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.7.2. DO NOT EDIT.
|
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||||
|
|
||||||
package fql // FqlParser
|
package fql // FqlParser
|
||||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Code generated from antlr/FqlParser.g4 by ANTLR 4.7.2. DO NOT EDIT.
|
// Code generated from antlr/FqlParser.g4 by ANTLR 4.8. DO NOT EDIT.
|
||||||
|
|
||||||
package fql // FqlParser
|
package fql // FqlParser
|
||||||
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
import "github.com/antlr/antlr4/runtime/Go/antlr"
|
||||||
|
@ -3,16 +3,20 @@ package io
|
|||||||
import (
|
import (
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/stdlib/io/fs"
|
"github.com/MontFerret/ferret/pkg/stdlib/io/fs"
|
||||||
|
"github.com/MontFerret/ferret/pkg/stdlib/io/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterLib register `IO` namespace functions.
|
// RegisterLib register `IO` namespace functions.
|
||||||
func RegisterLib(ns core.Namespace) error {
|
func RegisterLib(ns core.Namespace) error {
|
||||||
io := ns.Namespace("IO")
|
io := ns.Namespace("IO")
|
||||||
|
|
||||||
err := fs.RegisterLib(io)
|
if err := fs.RegisterLib(io); err != nil {
|
||||||
if err != nil {
|
|
||||||
return core.Error(err, "register `FS`")
|
return core.Error(err, "register `FS`")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := net.RegisterLib(io); err != nil {
|
||||||
|
return core.Error(err, "register `NET`")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
14
pkg/stdlib/io/net/http/delete.go
Normal file
14
pkg/stdlib/io/net/http/delete.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
h "net/http"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DELETE makes a DELETE request to the specified URL.
|
||||||
|
// @params url or (String) - path to file to write into.
|
||||||
|
func DELETE(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||||
|
return execMethod(ctx, h.MethodDelete, args)
|
||||||
|
}
|
109
pkg/stdlib/io/net/http/delete_test.go
Normal file
109
pkg/stdlib/io/net/http/delete_test.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package http_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
h "net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
"github.com/MontFerret/ferret/pkg/stdlib/io/net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func randPort() string {
|
||||||
|
min := 8000
|
||||||
|
max := 8999
|
||||||
|
return fmt.Sprintf(":%d", rand.Intn(max-min)+min)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDELETE(t *testing.T) {
|
||||||
|
SkipConvey("Should successfully make request", t, func() {
|
||||||
|
type User struct {
|
||||||
|
FirstName string `json:"first_name"`
|
||||||
|
LastName string `json:"last_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
port := randPort()
|
||||||
|
|
||||||
|
server := &h.Server{
|
||||||
|
Addr: port,
|
||||||
|
Handler: h.HandlerFunc(func(w h.ResponseWriter, r *h.Request) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if r.Method != "DELETE" {
|
||||||
|
err = errors.Errorf("Expected method to be DELETE, but got %s", r.Method)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := User{}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, &user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.FirstName != "Rob" {
|
||||||
|
err = errors.Errorf("Expected FirstName to be Rob, but got %s", user.FirstName)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.LastName != "Pike" {
|
||||||
|
err = errors.Errorf("Expected LastName to be Pike, but got %s", user.LastName)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
server.ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
server.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
b, err := json.Marshal(User{
|
||||||
|
FirstName: "Rob",
|
||||||
|
LastName: "Pike",
|
||||||
|
})
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
out, err := http.DELETE(ctx, values.NewObjectWith(
|
||||||
|
values.NewObjectProperty("url", values.NewString("http://127.0.0.1"+port)),
|
||||||
|
values.NewObjectProperty("body", values.NewBinary(b)),
|
||||||
|
))
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(out.Type().ID(), ShouldEqual, types.Binary.ID())
|
||||||
|
So(out.String(), ShouldEqual, "OK")
|
||||||
|
})
|
||||||
|
}
|
35
pkg/stdlib/io/net/http/get.go
Normal file
35
pkg/stdlib/io/net/http/get.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
h "net/http"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GET makes a GET request to the specified URL.
|
||||||
|
// @params url or (String) - path to file to write into.
|
||||||
|
func GET(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||||
|
if err := core.ValidateArgs(args, 1, 1); err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := args[0]
|
||||||
|
|
||||||
|
if err := core.ValidateType(arg, types.String, types.Object); err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg.Type() == types.String {
|
||||||
|
return makeRequest(ctx, Params{
|
||||||
|
Method: "GET",
|
||||||
|
URL: values.ToString(arg),
|
||||||
|
Headers: nil,
|
||||||
|
Body: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return execMethod(ctx, h.MethodGet, args)
|
||||||
|
}
|
112
pkg/stdlib/io/net/http/get_test.go
Normal file
112
pkg/stdlib/io/net/http/get_test.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package http_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
h "net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/stdlib/io/net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGET(t *testing.T) {
|
||||||
|
SkipConvey("Should successfully make request", t, func() {
|
||||||
|
port := randPort()
|
||||||
|
|
||||||
|
server := &h.Server{
|
||||||
|
Addr: port,
|
||||||
|
Handler: h.HandlerFunc(func(w h.ResponseWriter, r *h.Request) {
|
||||||
|
if r.Method == "GET" {
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte("Expected method to be GET"))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
server.ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
server.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
out, err := http.GET(ctx, values.NewString("http://localhost"+port))
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(out.Type().ID(), ShouldEqual, types.Binary.ID())
|
||||||
|
So(out.String(), ShouldEqual, "OK")
|
||||||
|
})
|
||||||
|
|
||||||
|
SkipConvey("Should add headers to a request", t, func() {
|
||||||
|
port := randPort()
|
||||||
|
|
||||||
|
server := &h.Server{
|
||||||
|
Addr: port,
|
||||||
|
Handler: h.HandlerFunc(func(w h.ResponseWriter, r *h.Request) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if r.Method != "GET" {
|
||||||
|
err = errors.Errorf("Expected method to be GET, but got %s", r.Method)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token := r.Header.Get("X-Token")
|
||||||
|
|
||||||
|
if token != "Ferret" {
|
||||||
|
err = errors.Errorf("Expected X-Token header to equal to Ferret, but got %s", token)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
from := r.Header.Get("X-From")
|
||||||
|
|
||||||
|
if from != "localhost" {
|
||||||
|
err = errors.Errorf("Expected X-From header to equal to localhost, but got %s", from)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
server.ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
server.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
out, err := http.GET(ctx, values.NewObjectWith(
|
||||||
|
values.NewObjectProperty("url", values.NewString("http://127.0.0.1"+port)),
|
||||||
|
values.NewObjectProperty("headers", values.NewObjectWith(
|
||||||
|
values.NewObjectProperty("X-Token", values.NewString("Ferret")),
|
||||||
|
values.NewObjectProperty("X-From", values.NewString("localhost")),
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(out.Type().ID(), ShouldEqual, types.Binary.ID())
|
||||||
|
So(out.String(), ShouldEqual, "OK")
|
||||||
|
})
|
||||||
|
}
|
17
pkg/stdlib/io/net/http/lib.go
Normal file
17
pkg/stdlib/io/net/http/lib.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import "github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
|
||||||
|
// RegisterLib register `HTTP` namespace functions.
|
||||||
|
func RegisterLib(ns core.Namespace) error {
|
||||||
|
return ns.
|
||||||
|
Namespace("HTTP").
|
||||||
|
RegisterFunctions(
|
||||||
|
core.NewFunctionsFromMap(map[string]core.Function{
|
||||||
|
"GET": GET,
|
||||||
|
"POST": POST,
|
||||||
|
"PUT": PUT,
|
||||||
|
"DELETE": DELETE,
|
||||||
|
"DO": REQUEST,
|
||||||
|
}))
|
||||||
|
}
|
14
pkg/stdlib/io/net/http/post.go
Normal file
14
pkg/stdlib/io/net/http/post.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
h "net/http"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// POST makes a POST request to the specified URL.
|
||||||
|
// @params url or (String) - path to file to write into.
|
||||||
|
func POST(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||||
|
return execMethod(ctx, h.MethodPost, args)
|
||||||
|
}
|
101
pkg/stdlib/io/net/http/post_test.go
Normal file
101
pkg/stdlib/io/net/http/post_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package http_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
h "net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
"github.com/MontFerret/ferret/pkg/stdlib/io/net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPOST(t *testing.T) {
|
||||||
|
SkipConvey("Should successfully make request", t, func() {
|
||||||
|
type User struct {
|
||||||
|
FirstName string `json:"first_name"`
|
||||||
|
LastName string `json:"last_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
port := randPort()
|
||||||
|
|
||||||
|
server := &h.Server{
|
||||||
|
Addr: port,
|
||||||
|
Handler: h.HandlerFunc(func(w h.ResponseWriter, r *h.Request) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if r.Method != "POST" {
|
||||||
|
err = errors.Errorf("Expected method to be POST, but got %s", r.Method)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := User{}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, &user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.FirstName != "Rob" {
|
||||||
|
err = errors.Errorf("Expected FirstName to be Rob, but got %s", user.FirstName)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.LastName != "Pike" {
|
||||||
|
err = errors.Errorf("Expected LastName to be Pike, but got %s", user.LastName)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
server.ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
server.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
b, err := json.Marshal(User{
|
||||||
|
FirstName: "Rob",
|
||||||
|
LastName: "Pike",
|
||||||
|
})
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
out, err := http.POST(ctx, values.NewObjectWith(
|
||||||
|
values.NewObjectProperty("url", values.NewString("http://127.0.0.1"+port)),
|
||||||
|
values.NewObjectProperty("body", values.NewBinary(b)),
|
||||||
|
))
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(out.Type().ID(), ShouldEqual, types.Binary.ID())
|
||||||
|
So(out.String(), ShouldEqual, "OK")
|
||||||
|
})
|
||||||
|
}
|
14
pkg/stdlib/io/net/http/put.go
Normal file
14
pkg/stdlib/io/net/http/put.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
h "net/http"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PUT makes a PUT request to the specified URL.
|
||||||
|
// @params url or (String) - path to file to write into.
|
||||||
|
func PUT(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||||
|
return execMethod(ctx, h.MethodPut, args)
|
||||||
|
}
|
101
pkg/stdlib/io/net/http/put_test.go
Normal file
101
pkg/stdlib/io/net/http/put_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package http_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
h "net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
"github.com/MontFerret/ferret/pkg/stdlib/io/net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPUT(t *testing.T) {
|
||||||
|
SkipConvey("Should successfully make request", t, func() {
|
||||||
|
type User struct {
|
||||||
|
FirstName string `json:"first_name"`
|
||||||
|
LastName string `json:"last_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
port := randPort()
|
||||||
|
|
||||||
|
server := &h.Server{
|
||||||
|
Addr: port,
|
||||||
|
Handler: h.HandlerFunc(func(w h.ResponseWriter, r *h.Request) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if r.Method != "PUT" {
|
||||||
|
err = errors.Errorf("Expected method to be PUT, but got %s", r.Method)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := User{}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, &user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.FirstName != "Rob" {
|
||||||
|
err = errors.Errorf("Expected FirstName to be Rob, but got %s", user.FirstName)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.LastName != "Pike" {
|
||||||
|
err = errors.Errorf("Expected LastName to be Pike, but got %s", user.LastName)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
server.ListenAndServe()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
server.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
b, err := json.Marshal(User{
|
||||||
|
FirstName: "Rob",
|
||||||
|
LastName: "Pike",
|
||||||
|
})
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
out, err := http.PUT(ctx, values.NewObjectWith(
|
||||||
|
values.NewObjectProperty("url", values.NewString("http://127.0.0.1"+port)),
|
||||||
|
values.NewObjectProperty("body", values.NewBinary(b)),
|
||||||
|
))
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(out.Type().ID(), ShouldEqual, types.Binary.ID())
|
||||||
|
So(out.String(), ShouldEqual, "OK")
|
||||||
|
})
|
||||||
|
}
|
122
pkg/stdlib/io/net/http/request.go
Normal file
122
pkg/stdlib/io/net/http/request.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
h "net/http"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Params struct {
|
||||||
|
Method values.String
|
||||||
|
URL values.String
|
||||||
|
Headers *values.Object
|
||||||
|
Body values.Binary
|
||||||
|
}
|
||||||
|
|
||||||
|
func REQUEST(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||||
|
return execMethod(ctx, "", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execMethod(ctx context.Context, method values.String, args []core.Value) (core.Value, error) {
|
||||||
|
if err := core.ValidateArgs(args, 1, 1); err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := args[0]
|
||||||
|
|
||||||
|
if err := core.ValidateType(arg, types.Object); err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := newParamsFrom(arg.(*values.Object))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if method != "" {
|
||||||
|
p.Method = method
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeRequest(ctx, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRequest(ctx context.Context, params Params) (core.Value, error) {
|
||||||
|
client := h.Client{}
|
||||||
|
req, err := h.NewRequest(params.Method.String(), params.URL.String(), bytes.NewBuffer(params.Body))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header = h.Header{}
|
||||||
|
|
||||||
|
if params.Headers != nil {
|
||||||
|
params.Headers.ForEach(func(value core.Value, key string) bool {
|
||||||
|
req.Header.Set(key, value.String())
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req.WithContext(ctx))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return values.None, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return values.NewBinary(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newParamsFrom(obj *values.Object) (Params, error) {
|
||||||
|
p := Params{}
|
||||||
|
|
||||||
|
method, exists := obj.Get("method")
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
p.Method = values.ToString(method)
|
||||||
|
}
|
||||||
|
|
||||||
|
url, exists := obj.Get("url")
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return Params{}, core.Error(core.ErrMissedArgument, ".url")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.URL = values.NewString(url.String())
|
||||||
|
|
||||||
|
headers, exists := obj.Get("headers")
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
if err := core.ValidateType(headers, types.Object); err != nil {
|
||||||
|
return Params{}, core.Error(err, ".headers")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Headers = headers.(*values.Object)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, exists := obj.Get("body")
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
if err := core.ValidateType(body, types.Binary); err != nil {
|
||||||
|
return Params{}, core.Error(err, ".body")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Body = body.(values.Binary)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
17
pkg/stdlib/io/net/lib.go
Normal file
17
pkg/stdlib/io/net/lib.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/MontFerret/ferret/pkg/stdlib/io/net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterLib register `NET` namespace functions.
|
||||||
|
func RegisterLib(ns core.Namespace) error {
|
||||||
|
io := ns.Namespace("NET")
|
||||||
|
|
||||||
|
if err := http.RegisterLib(io); err != nil {
|
||||||
|
return core.Error(err, "register `HTTP`")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Reference in New Issue
Block a user