1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-24 20:14:31 +02:00

Remove socket supoort

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2015-03-02 10:19:30 -08:00
parent 7c9a0b6489
commit 9f6f4e8afb
9 changed files with 32 additions and 784 deletions

View File

@ -1,78 +1,2 @@
# Bolt
Multi transport, multi format REST style network library
## Socket Specification
### Trasport
- WebSocket
- TCP
### Command (*1-byte*)
- INIT (**1**)
- AUTH (**2**)
- HTTP (**3**)
- PUB (**4**)
- MPUB (**5**)
- SUB (**6**)
- USUB (**7**)
### INIT
> Request
```sh
1 # Command (1-byte)
99 # Correlation ID (4-byte)
8 # Config length (2-byte)
{} # Config as JSON (n-byte)
```
> Config
```js
{
"Format":
}
```
> Response
```sh
99 # Correlation ID (4-byte)
200 # Status code (2-byte)
```
### AUTH
> Request
```sh
2 # Command (1-byte)
99 # Correlation ID (4-byte)
30 # Token length (2-byte)
1g42*jMG!a?D3eF>Xwt!dI05]Y9egP # Token (n-byte)
```
> Response
```sh
99 # Correlation ID (4-byte)
200 # Status code (2-byte)
```
### HTTP
> Request
```sh
3 # Command (1-byte)
99 # Correlation ID (4-byte)
GET\n # Method (n-byte)
/users\n # Path (n-byte)
- # Headers
64 # Body length (8-byte)
- # Body (n-byte)
```
> Response
```sh
3 # Command (1-byte)
200 # Status code (2-byte)
# For POST, PUT & PATCH
64 # Body length (8-byte)
- # Body (n-byte)
```
Coming soon...

101
bolt.go
View File

@ -1,14 +1,9 @@
package bolt
import (
"bufio"
"io"
"log"
"net"
"net/http"
"sync"
"golang.org/x/net/websocket"
)
type (
@ -18,37 +13,15 @@ type (
maxParam byte
notFoundHandler HandlerFunc
methodNotAllowedHandler HandlerFunc
tcpListener *net.TCPListener
// udpConn *net.UDPConn
pool sync.Pool
pool sync.Pool
}
command byte
format byte
transport byte
HandlerFunc func(*Context)
Format byte
)
const (
CmdINIT command = 1 + iota
CmdAUTH
CmdHTTP
CmdPUB
CmdSUB
CmdUSUB
)
const (
TrnspHTTP transport = 1 + iota
TrnspWS
TrnspTCP
)
const (
FmtJSON format = 1 + iota
FmtJSON Format = 1 + iota
FmtMsgPack
FmtBinary = 20
)
const (
@ -87,7 +60,6 @@ func New(opts ...func(*Bolt)) (b *Bolt) {
b.pool.New = func() interface{} {
return &Context{
Writer: NewResponse(nil),
Socket: new(Socket),
params: make(Params, b.maxParam),
store: make(store),
i: -1,
@ -176,7 +148,6 @@ func (b *Bolt) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
// Find and execute handler
h, c, s := b.Router.Find(r.Method, r.URL.Path)
if h != nil {
c.Transport = TrnspHTTP
c.Writer.ResponseWriter = rw
c.Request = r
h(c)
@ -190,70 +161,6 @@ func (b *Bolt) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
b.pool.Put(c)
}
func (b *Bolt) handleSocket(conn io.ReadWriteCloser, tp transport) {
// TODO: From pool?
defer conn.Close()
s := &Socket{
Transport: tp,
config: Config{},
conn: conn,
Reader: bufio.NewReader(conn),
Writer: bufio.NewWriter(conn),
bolt: b,
}
Loop:
for {
c, err := s.Reader.ReadByte() // Command
if err != nil {
log.Println(err, 222, c)
}
cmd := command(c)
println(cmd)
switch cmd {
case CmdINIT:
s.Init()
case CmdHTTP:
s.HTTP()
default:
break Loop
}
}
}
func (b *Bolt) RunHTTP(addr string) {
func (b *Bolt) Run(addr string) {
log.Fatal(http.ListenAndServe(addr, b))
}
func (b *Bolt) RunWS(addr string) {
http.Handle("/", websocket.Handler(func(ws *websocket.Conn) {
b.handleSocket(ws, TrnspWS)
}))
log.Fatal(http.ListenAndServe(addr, nil))
}
func (b *Bolt) RunTCP(addr string) {
a, _ := net.ResolveTCPAddr("tcp", addr)
l, err := net.ListenTCP("tcp", a)
if err != nil {
log.Fatalf("bolt: %v", err)
}
b.tcpListener = l
go b.serve("tcp")
}
func (b *Bolt) serve(net string) {
switch net {
case "ws":
case "tcp":
for {
conn, err := b.tcpListener.Accept()
if err != nil {
log.Print(err)
return
}
go b.handleSocket(conn, TrnspTCP)
}
default:
// TODO: handle it!
}
}

View File

@ -1,14 +1,17 @@
package bolt
import (
"bytes"
"encoding/binary"
"encoding/json"
"io"
"net"
"sync"
"testing"
"time"
)
type (
user struct {
Id string
Name string
}
)
var u = user{
@ -16,113 +19,6 @@ var u = user{
Name: "Joe",
}
func startTCPServer() (b *Bolt, addr string) {
var wg sync.WaitGroup
b = New()
a, _ := net.ResolveTCPAddr("tcp", "localhost:0")
wg.Add(1)
go func() {
defer wg.Done()
b.RunTCP(a.String())
}()
wg.Wait()
addr = b.tcpListener.Addr().String()
return
}
func connectTCPServer(addr string) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", addr, time.Second)
return
}
func TestSocketInit(t *testing.T) {
b, addr := startTCPServer()
conn, err := connectTCPServer(addr)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
defer b.tcpListener.Close()
// Request
buf := new(bytes.Buffer)
buf.WriteByte(byte(CmdINIT)) // Command
cfg := &Config{
Format: FmtJSON,
}
bt, err := json.Marshal(cfg)
if err != nil {
t.Fatal(err)
}
binary.Write(buf, binary.BigEndian, uint16(len(bt))) // Config length
buf.Write(bt) // Config
buf.WriteTo(conn)
// Response
var n uint16
err = binary.Read(conn, binary.BigEndian, &n) // Status code
if err != nil {
t.Fatal(err)
}
if n != 200 {
t.Errorf("status code should be 200, found %d", n)
}
}
func TestSocketHTTP(t *testing.T) {
b, addr := startTCPServer()
conn, err := connectTCPServer(addr)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
defer b.tcpListener.Close()
// GET
b.Get("/users", func(c *Context) {
c.Render(200, FmtJSON, u)
})
buf := new(bytes.Buffer)
buf.WriteByte(byte(CmdHTTP)) // Command
buf.WriteString("GET\n") // Method
buf.WriteString("/users\n") // Path
buf.WriteTo(conn)
var n uint16
err = binary.Read(conn, binary.BigEndian, &n) // Status code
if err != nil {
t.Fatal(err)
}
if n != 200 {
t.Errorf("status code should be 200, found %d", n)
}
verifyUser(conn, t)
// POST
b.Post("/users", func(c *Context) {
c.Bind(c.Socket.config.Format, &user{})
c.Render(201, FmtJSON, u)
})
buf.Reset()
buf.WriteByte(byte(CmdHTTP)) // Command
buf.WriteString("POST\n") // Method
buf.WriteString("/users\n") // Path
bt, err := json.Marshal(u)
if err != nil {
t.Fatal(err)
}
binary.Write(buf, binary.BigEndian, int64(len(bt))) // Body length
buf.Write(bt) // Body
buf.WriteTo(conn)
err = binary.Read(conn, binary.BigEndian, &n) // Status code
if err != nil {
t.Fatal(err)
}
if n != 201 {
t.Errorf("status code should be 201, found %d", n)
}
verifyUser(conn, t)
}
func verifyUser(rd io.Reader, t *testing.T) {
var l int64
err := binary.Read(rd, binary.BigEndian, &l) // Body length

171
client.go
View File

@ -1,171 +0,0 @@
package bolt
import (
"bufio"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"sync"
)
type (
Client struct {
host string
port string
transport transport
socket io.ReadWriteCloser
reader *bufio.Reader
writer *bufio.Writer
httpClient HttpClient
pool sync.Pool
connected bool
}
HttpClient interface {
Do(*http.Request) (*http.Response, error)
}
)
func NewClient(opts ...func(*Client)) (c *Client) {
c = &Client{
host: "localhost",
port: "80",
transport: TrnspHTTP,
httpClient: &http.Client{},
}
c.pool.New = func() interface{} {
return &Context{
Request: &http.Request{
URL: new(url.URL),
},
// Response: new(http.Response),
// Socket: &Socket{
// Header: SocketHeader{},
// },
client: true,
}
}
// Set options
for _, o := range opts {
o(c)
}
return
}
func Transport(t transport) func(*Client) {
return func(c *Client) {
c.transport = t
}
}
func Host(h string) func(*Client) {
return func(c *Client) {
c.host = h
}
}
func Port(p string) func(*Client) {
return func(c *Client) {
c.port = p
}
}
func (c *Client) Open(cfg *Config) (err error) {
switch c.transport {
case TrnspWS:
case TrnspTCP:
c.socket, err = net.Dial("tcp", net.JoinHostPort(c.host, c.port))
if err != nil {
return fmt.Errorf("bolt: %v", err)
}
default:
return errors.New("bolt: transport not supported")
}
// Request
c.writer = bufio.NewWriter(c.socket)
c.reader = bufio.NewReader(c.socket)
c.writer.WriteByte(byte(CmdINIT)) // Command
var cid uint32 = 98
if err = binary.Write(c.writer, binary.BigEndian, cid); err != nil { // Correlation ID
log.Println(err)
}
b, err := json.Marshal(cfg)
if err != nil {
return fmt.Errorf("bolt: %v", err)
}
if err = binary.Write(c.writer, binary.BigEndian, uint16(len(b))); err != nil { // Config length
}
c.writer.Write(b) // Config
c.writer.Flush()
for {
var cid uint32
binary.Read(c.reader, binary.BigEndian, &cid) // Correlation ID
if err != nil {
return fmt.Errorf("bolt: %v", err)
}
println(cid)
break
}
// Response
var n uint16
err = binary.Read(c.reader, binary.BigEndian, &n) // Status code
if err != nil {
return fmt.Errorf("bolt: %v", err)
}
if n != 200 {
return fmt.Errorf("bolt: status=%d", n)
}
return
}
func (c *Client) Auth() {
}
func (c *Client) Connect(path string) {
}
func (c *Client) Delete() {
}
func (c *Client) Get(path string, hdr Header, hl HandlerFunc) error {
return c.Request("GET", path, hdr, hl)
}
func (c *Client) Request(method, path string, hd Header, hl HandlerFunc) (err error) {
ctx := c.pool.Get().(*Context)
ctx.Transport = c.transport
switch c.transport {
case TrnspHTTP:
ctx.Request.Method = method
// ctx.Request.Header = hd
ctx.Request.URL.Scheme = "http"
ctx.Request.URL.Host = net.JoinHostPort(c.host, c.port)
ctx.Request.URL.Path = path
ctx.Response, err = c.httpClient.Do(ctx.Request)
if err != nil {
return
}
// ctx.Header = ctx.Response.Header
hl(ctx)
case TrnspWS, TrnspTCP:
c.writer.WriteByte(byte(CmdHTTP)) // Command
c.writer.WriteString(method + "\n") // Method
c.writer.WriteString(path + "\n") // Path
if method == "POST" || method == "PUT" || method == "PATCH" {
// binary.Write(c.writer, binary.BigEndian, uint16(len(b))) // Header length
// wt.Write(b) // Header
}
c.writer.Flush()
hl(ctx)
}
return
}

View File

@ -1,102 +0,0 @@
package bolt
import (
"bytes"
"encoding/json"
"io/ioutil"
"net"
"net/http"
"os"
"testing"
"time"
)
type (
httpClient struct{}
user struct {
Id string
Name string
}
)
func mockTcpServer(method, path string) (b *Bolt) {
b = New()
b.Handle(method, path, nil)
b.RunTCP(":9999")
return
}
func mockTcpReq(status uint16, hd Header, body []byte) (c *Client) {
c = NewClient()
c.transport = TrnspTCP
// c.socket = new(bytes.Buffer)
c.socket = os.Stdout
// l := int64(len(body))
// hd.Set("Content-Length", strconv.FormatInt(l, 10))
c.socket.Write(body)
return
}
func (c *httpClient) Do(req *http.Request) (res *http.Response, err error) {
res = &http.Response{}
if req.Method == "GET" {
res.StatusCode = 200
u := &user{"1", "Joe"}
d, _ := json.Marshal(u)
res.Body = ioutil.NopCloser(bytes.NewBuffer(d))
res.Header = make(http.Header)
res.Header.Add("Accept", MIME_JSON)
}
return
}
func TestClientPost(t *testing.T) {
}
func TestClientHttpGet(t *testing.T) {
c := NewClient()
c.httpClient = &httpClient{}
hd := make(Header)
// hd.Set("Accept", MIME_JSON)
if err := c.Get("/users/1", hd, func(ctx *Context) {
u := new(user)
ctx.Decode(u)
if u.Id != "1" {
t.Error()
}
}); err != nil {
t.Error(err)
}
}
func TestTCPClient(t *testing.T) {
b, addr := startTCPServer()
defer b.tcpListener.Close()
// Open
host, port, err := net.SplitHostPort(addr)
if err != nil {
t.Fatal(err)
}
c := NewClient(Transport(TrnspTCP), Host(host), Port(port))
go func() {
err = c.Open(&Config{
Format: FmtJSON,
})
if err != nil {
t.Fatal(err)
}
}()
time.Sleep(32 * time.Millisecond)
// Get
// b.Get("/users", func(c *Context) {
// c.Render(200, FmtJSON, u)
// })
// err = c.Get("/users", nil, func(c *Context) {
// })
// time.Sleep(32 * time.Millisecond)
// if err != nil {
// t.Fatal(err)
// }
}

View File

@ -1,27 +1,22 @@
package bolt
import (
"encoding/binary"
"encoding/json"
"io"
"fmt"
"log"
"net/http"
"strconv"
)
type (
Context struct {
Transport transport
Request *http.Request
Writer *response
Response *http.Response
Socket *Socket
params Params
handlers []HandlerFunc
store map[string]interface{}
l int // Handlers' length
i int // Current handler index
client bool
Request *http.Request
Writer *response
Response *http.Response
params Params
handlers []HandlerFunc
store map[string]interface{}
l int // Handlers' length
i int // Current handler index
}
store map[string]interface{}
)
@ -34,17 +29,10 @@ func (c *Context) Param(n string) string {
return c.params.Get(n)
}
func (c *Context) Bind(f format, i interface{}) (err error) {
var bd io.ReadCloser
switch c.Transport {
case TrnspHTTP:
bd = c.Request.Body
case TrnspWS, TrnspTCP:
bd = c.Socket.Body
}
func (c *Context) Bind(f Format, i interface{}) (err error) {
switch f {
case FmtJSON:
dec := json.NewDecoder(bd)
dec := json.NewDecoder(c.Request.Body)
if err = dec.Decode(i); err != nil {
log.Printf("bolt: %s", err)
}
@ -52,69 +40,20 @@ func (c *Context) Bind(f format, i interface{}) (err error) {
return
}
func (c *Context) Decode(i interface{}) (err error) {
var rd io.Reader
switch c.Transport {
case TrnspHTTP:
rd = c.Request.Body
if c.client {
rd = c.Response.Body
defer rd.(io.Closer).Close()
}
case TrnspWS, TrnspTCP:
var cl int64
cl, err = strconv.ParseInt(c.Request.Header.Get(HdrContentLength), 10, 64)
if err != nil {
return
}
rd = io.LimitReader(c.Socket.Reader, cl)
}
t := c.Request.Header.Get("Content-Type")
if c.client {
t = c.Request.Header.Get("Accept")
}
switch t {
case MIME_MP:
default: // JSON
dec := json.NewDecoder(rd)
if err = dec.Decode(i); err != nil {
log.Println(err)
}
}
return
}
// TODO: Streaming?
func (c *Context) Encode(f format, i interface{}) (b []byte, err error) {
// TODO: return error, streaming?
func (c *Context) Render(n int, f Format, i interface{}) (err error) {
var body []byte
switch f {
case FmtJSON:
b, err = json.Marshal(i)
body, err = json.Marshal(i)
}
return
}
// TODO: return error
func (c *Context) Render(n int, f format, i interface{}) {
bd, err := c.Encode(f, i)
if err != nil {
log.Printf("bolt: %s", err)
}
switch c.Transport {
default:
// c.Writer.Header().Set(HEADER_CONTENT_TYPE, MIME_JSON)
// c.Writer.WriteHeader(int(n))
// c.Writer.Write(body)
case TrnspWS, TrnspTCP:
binary.Write(c.Socket.Writer, binary.BigEndian, uint16(n)) // Status code
binary.Write(c.Socket.Writer, binary.BigEndian, int64(len(bd))) // Body length
c.Socket.Writer.Write(bd) // Body
c.Socket.Writer.Flush()
return fmt.Errorf("bolt: %s", err)
}
// c.Writer.Header().Set(HEADER_CONTENT_TYPE, MIME_JSON)
c.Writer.WriteHeader(n)
_, err = c.Writer.Write(body)
return
}
// Next executes the next handler in the chain.

View File

@ -1,24 +0,0 @@
# to find out the configuration commands, run: h2o --help
listen: 8080
listen:
port: 8081
ssl:
certificate-file: examples/h2o/server.crt
key-file: examples/h2o/server.key
hosts:
"127.0.0.1.xip.io:8080":
paths:
/:
file.dir: examples/doc_root
access-log: /dev/stdout
"alternate.127.0.0.1.xip.io:8081":
listen:
port: 8081
ssl:
certificate-file: examples/h2o/alternate.crt
key-file: examples/h2o/alternate.key
paths:
/:
file.dir: examples/doc_root.alternate
access-log: /dev/stdout

101
socket.go
View File

@ -1,101 +0,0 @@
package bolt
import (
"bufio"
"encoding/binary"
"encoding/json"
"io"
"io/ioutil"
"log"
)
type (
Socket struct {
Transport transport
Header Header
Body io.ReadCloser
config Config
conn io.ReadWriteCloser
Reader *bufio.Reader
Writer *bufio.Writer
bolt *Bolt
initialized bool
}
Config struct {
Format format `json:"format,omitempty"`
}
Header map[string]string
)
func (s *Socket) Init() {
// Request
var cid uint32 // Correlation ID
err := binary.Read(s.Reader, binary.BigEndian, &cid)
if err != nil {
log.Println(err)
}
var l uint16 // Config length
err = binary.Read(s.Reader, binary.BigEndian, &l)
if err != nil {
log.Println(err)
}
rd := io.LimitReader(s.Reader, int64(l)) // Config
dec := json.NewDecoder(rd)
if err = dec.Decode(&s.config); err != nil {
log.Println(err)
}
// Response
if err = binary.Write(s.Writer, binary.BigEndian, cid); err != nil { // Correlation ID
log.Println(err)
}
if err = binary.Write(s.Writer, binary.BigEndian, uint16(200)); err != nil { // Status code
log.Println(err)
}
s.Writer.Flush()
s.initialized = true
}
func (s *Socket) Auth() {
}
func (s *Socket) HTTP() {
// Method
m, err := s.Reader.ReadString('\n')
if err != nil {
log.Println(err)
}
m = m[:len(m)-1]
// Path
p, err := s.Reader.ReadString('\n')
if err != nil {
log.Println(err)
}
p = p[:len(p)-1]
if m == "POST" || m == "PUT" || m == "PATCH" {
var l int64
err = binary.Read(s.Reader, binary.BigEndian, &l) // Body length
if err != nil {
log.Println(err)
}
s.Body = ioutil.NopCloser(io.LimitReader(s.Reader, l)) // Body
}
h, c, st := s.bolt.Router.Find(m, p)
c.Socket = s
c.Transport = s.Transport
if h != nil {
h(c)
} else {
if st == NotFound {
s.bolt.notFoundHandler(c)
} else if st == NotAllowed {
s.bolt.methodNotAllowedHandler(c)
}
}
s.bolt.pool.Put(c)
}

View File

@ -1,20 +0,0 @@
package bolt
import (
"bytes"
"io"
)
type nopCloser struct {
*bytes.Buffer
}
func (nopCloser) Close() error {
return nil
}
// NopCloser returns a ReadWriteCloser with a no-op Close method wrapping
// the provided Buffer.
func NopCloser(b *bytes.Buffer) io.ReadWriteCloser {
return nopCloser{b}
}