mirror of
				https://github.com/labstack/echo.git
				synced 2025-10-30 23:57:38 +02:00 
			
		
		
		
	
							
								
								
									
										78
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								README.md
									
									
									
									
									
								
							| @@ -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
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								bolt.go
									
									
									
									
									
								
							| @@ -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! | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										118
									
								
								bolt_test.go
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								bolt_test.go
									
									
									
									
									
								
							| @@ -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
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								client.go
									
									
									
									
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										102
									
								
								client_test.go
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								client_test.go
									
									
									
									
									
								
							| @@ -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) | ||||
| 	// } | ||||
| } | ||||
							
								
								
									
										101
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								context.go
									
									
									
									
									
								
							| @@ -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. | ||||
|   | ||||
| @@ -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
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								socket.go
									
									
									
									
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										20
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								utils.go
									
									
									
									
									
								
							| @@ -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} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user