Skip to content

Commit

Permalink
http, http/utils, handler, pool, cmd/flatend: implement codec registr…
Browse files Browse the repository at this point in the history
…ation/negotiation, decoding header/query/body params, and barebones cuelang repl
  • Loading branch information
lithdew committed May 11, 2020
1 parent cb53197 commit a5d210a
Show file tree
Hide file tree
Showing 16 changed files with 1,034 additions and 198 deletions.
182 changes: 149 additions & 33 deletions cmd/flatend/main.go
Original file line number Diff line number Diff line change
@@ -1,57 +1,173 @@
package main

import (
"cuelang.org/go/cue"
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/parser"
"encoding/json"
"flag"
"fmt"
"github.com/lithdew/flatend"
flag "github.com/spf13/pflag"
"github.com/valyala/fasthttp/reuseport"
"net"
"os"
"os/signal"
"strconv"
"github.com/chzyer/readline"
"io/ioutil"
"log"
"strings"
)

const program = `
http_get("hello_world")
`

func check(err error) {
if err != nil {
panic(err)
log.Panic(err)
}
}

var (
bindHost string
bindPort uint16
)
func hold(fn func() error) {
check(fn())
}

func main() {
flag.StringVarP(&bindHost, "host", "h", "", "binding host")
flag.Uint16VarP(&bindPort, "port", "p", 0, "binding port")
var runtime cue.Runtime

func main() {
flag.Parse()

addr := net.JoinHostPort(bindHost, strconv.FormatUint(uint64(bindPort), 10))
code := ""

fmt.Printf("Bind address: %q\n", addr)
filename := flag.Arg(0)
if filename != "" {
buf, err := ioutil.ReadFile(filename)
check(err)

ln, err := reuseport.Listen("tcp4", addr)
check(err)
code = string(buf)
}

_, err = flatend.LoadConfig(program)
rl, err := readline.New("> ")
check(err)
defer hold(rl.Close)

rl.Config.FuncFilterInputRune = func(r rune) (rune, bool) {
switch r {
case readline.CharCtrlZ:
return 0, false
default:
return r, true
}
}

for {
ln := rl.Line()
if ln.CanContinue() {
continue
}
if ln.CanBreak() {
break
}

if len(ln.Line) == 0 {
in, err := runtime.Compile("", code)
if err == nil {
err = in.Err
}
if err != nil {
fmt.Fprintln(rl, err)
continue
}

formatted, err := format.Node(in.Value().Syntax())
if err != nil {
fmt.Fprintln(rl, err)
continue
}

fmt.Fprint(rl, string(formatted))

srv := flatend.NewHTTP()
check(srv.Listen(ln))
continue
}

defer func() {
check(srv.Close())
}()
if ln.Line[0] == ':' {
fields := strings.SplitN(ln.Line[1:], " ", 2)

ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt)
<-ch
switch fields[0] {
case "q", "quit":
return
case "o", "out":
in, err := runtime.Compile("", code)
if err == nil {
err = in.Err
}
if err != nil {
fmt.Fprintln(rl, err)
continue
}

println()
formatted, err := json.MarshalIndent(in.Value(), "", "\t")
if err != nil {
fmt.Fprintln(rl, err)
continue
}

fmt.Fprintln(rl, string(formatted))
case "a", "append":
in, err := runtime.Compile("", code)
if err == nil {
err = in.Err
}
if err != nil {
fmt.Fprintln(rl, err)
continue
}

out, err := runtime.Compile("", fields[1])
if err == nil {
err = in.Err
}
if err != nil {
fmt.Fprintln(rl, err)
continue
}

val := in.Value().Unify(out.Value())
if err := val.Err(); err != nil {
fmt.Fprintln(rl, err)
continue
}

formatted, err := format.Node(val.Syntax())
if err != nil {
fmt.Fprintln(rl, err)
continue
}

code = string(formatted)
}

continue
}

expr, err := parser.ParseExpr("", ln.Line)
if err != nil {
fmt.Fprintln(rl, err)
continue
}

in, err := runtime.Compile("", code)
if err == nil {
err = in.Err
}
if err != nil {
fmt.Fprintln(rl, err)
continue
}

result := in.Eval(expr)
if err := result.Err(); err != nil {
fmt.Fprintln(rl, err)
continue
}

formatted, err := format.Node(result.Syntax())
if err != nil {
fmt.Fprintln(rl, err)
continue
}

fmt.Fprintln(rl, string(formatted))
}
}
100 changes: 100 additions & 0 deletions codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package flatend

import (
"bytes"
"encoding/csv"
"encoding/gob"
"encoding/json"
"encoding/xml"
"fmt"
"gopkg.in/yaml.v3"
)

type (
EncodeFunc = func(values Values) ([]byte, error)
DecodeFunc = func(buf []byte, values Values) error
)

type Codec struct {
Encode EncodeFunc
Decode DecodeFunc
}

var Codecs = map[string]*Codec{
"json": reflectCodec(json.Marshal, json.Unmarshal),
"yaml": reflectCodec(yaml.Marshal, yaml.Unmarshal),
"xml": reflectCodec(xml.Marshal, xml.Unmarshal),
"csv": newCodec(csvEncoder, csvDecoder),
"gob": newCodec(gobEncoder, gobDecoder),
}

func reflectCodec(me func(src interface{}) ([]byte, error), md func(buf []byte, dst interface{}) error) *Codec {
return &Codec{Encode: marshalEncoder(me), Decode: unmarshalDecoder(md)}
}

func newCodec(encoder EncodeFunc, decoder DecodeFunc) *Codec {
return &Codec{Encode: encoder, Decode: decoder}
}

func unmarshalDecoder(f func(buf []byte, dst interface{}) error) DecodeFunc {
return func(buf []byte, values Values) error {
return f(buf, values)
}
}

func marshalEncoder(f func(src interface{}) ([]byte, error)) EncodeFunc {
return func(values Values) ([]byte, error) {
return f(values)
}
}

func csvEncoder(values Values) ([]byte, error) {
records := [][]string{make([]string, 0, len(values)), make([]string, 0, len(values))}
keys, vals := records[0], records[1]

for k, v := range values {
keys = append(keys, k)
vals = append(vals, fmt.Sprint(v))
}

var b bytes.Buffer
if err := csv.NewWriter(&b).WriteAll(records); err != nil {
return nil, err
}

return b.Bytes(), nil
}

func csvDecoder(src []byte, values Values) error {
r := csv.NewReader(bytes.NewReader(src))

keys, err := r.Read()
if err != nil {
return fmt.Errorf("csv: failed to read keys: %w", err)
}

r.FieldsPerRecord = len(keys)

vals, err := r.Read()
if err != nil {
return fmt.Errorf("csv: failed to read values: %w", err)
}

for i := 0; i < len(keys); i++ {
values[keys[i]] = vals[i]
}

return nil
}

func gobEncoder(values Values) ([]byte, error) {
var b bytes.Buffer
if err := gob.NewEncoder(&b).Encode(values); err != nil {
return nil, err
}
return b.Bytes(), nil
}

func gobDecoder(src []byte, values Values) error {
return gob.NewDecoder(bytes.NewReader(src)).Decode(&values)
}
38 changes: 0 additions & 38 deletions config.go

This file was deleted.

12 changes: 9 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ module github.com/lithdew/flatend
go 1.14

require (
github.com/spf13/pflag v1.0.5
github.com/valyala/fasthttp v1.9.0
go.starlark.net v0.0.0-20200330013621-be5394c419b6
cuelang.org/go v0.1.2
github.com/chzyer/logex v1.1.10 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
github.com/lithdew/bytesutil v0.0.0-20200409052507-d98389230a59
github.com/stretchr/testify v1.5.1
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86
)
Loading

0 comments on commit a5d210a

Please sign in to comment.