Skip to content

Instantly share code, notes, and snippets.

@hassansin
Last active January 20, 2025 07:26
Show Gist options
  • Select an option

  • Save hassansin/81e6054ff28d5ef4cdbdad9d668df7a0 to your computer and use it in GitHub Desktop.

Select an option

Save hassansin/81e6054ff28d5ef4cdbdad9d668df7a0 to your computer and use it in GitHub Desktop.

Revisions

  1. hassansin revised this gist Oct 8, 2017. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions request-response.go
    Original file line number Diff line number Diff line change
    @@ -43,13 +43,13 @@ type WSClient struct {
    mutex sync.Mutex
    conn *websocket.Conn
    pending map[uint64]*Call
    id uint64
    counter uint64
    }

    func New() *WSClient {
    return &WSClient{
    pending: make(map[uint64]*Call, 1),
    id: 1,
    counter: 1,
    }
    }

    @@ -96,8 +96,8 @@ func (c *WSClient) Connect(url string) error {

    func (c *WSClient) Request(payload interface{}) (interface{}, error) {
    c.mutex.Lock()
    id := c.id
    c.id++
    id := c.counter
    c.counter++
    req := Request{ID: id, Message: payload}
    call := NewCall(req)
    c.pending[id] = call
  2. hassansin created this gist Oct 8, 2017.
    161 changes: 161 additions & 0 deletions request-response.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,161 @@
    package main

    import (
    "errors"
    "fmt"
    "math/rand"
    "net/http"
    "sync"
    "time"

    "github.com/gorilla/websocket"
    )

    // Request represents a request from client
    type Request struct {
    ID uint64 `json:"id"`
    Message interface{} `json:"message"`
    }

    // Response is the reply message from the server
    type Response struct {
    ID uint64 `json:"id"`
    Message interface{} `json:"message"`
    }

    // Call represents an active request
    type Call struct {
    Req Request
    Res Response
    Done chan bool
    Error error
    }

    func NewCall(req Request) *Call {
    done := make(chan bool)
    return &Call{
    Req: req,
    Done: done,
    }
    }

    type WSClient struct {
    mutex sync.Mutex
    conn *websocket.Conn
    pending map[uint64]*Call
    id uint64
    }

    func New() *WSClient {
    return &WSClient{
    pending: make(map[uint64]*Call, 1),
    id: 1,
    }
    }

    func (c *WSClient) read() {
    var err error
    for err == nil {
    var res Response
    err = c.conn.ReadJSON(&res)
    if err != nil {
    err = fmt.Errorf("error reading message: %q", err)
    continue
    }
    // fmt.Printf("received message: %+v\n", res)
    c.mutex.Lock()
    call := c.pending[res.ID]
    delete(c.pending, res.ID)
    c.mutex.Unlock()
    if call == nil {
    err = errors.New("no pending request found")
    continue
    }
    call.Res = res
    call.Done <- true
    }
    c.mutex.Lock()
    for _, call := range c.pending {
    call.Error = err
    call.Done <- true
    }
    c.mutex.Unlock()
    }

    func (c *WSClient) Connect(url string) error {
    conn, _, err := websocket.DefaultDialer.Dial(url, http.Header{
    "User-Agent": []string{"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"},
    })
    if err != nil {
    return err
    }
    c.conn = conn
    go c.read()
    return nil
    }

    func (c *WSClient) Request(payload interface{}) (interface{}, error) {
    c.mutex.Lock()
    id := c.id
    c.id++
    req := Request{ID: id, Message: payload}
    call := NewCall(req)
    c.pending[id] = call
    err := c.conn.WriteJSON(&req)
    if err != nil {
    delete(c.pending, id)
    c.mutex.Unlock()
    return nil, err
    }
    c.mutex.Unlock()
    select {
    case <-call.Done:
    case <-time.After(2 * time.Second):
    call.Error = errors.New("request timeout")
    }

    if call.Error != nil {
    return nil, call.Error
    }
    return call.Res.Message, nil
    }

    func (c *WSClient) Close() error {
    return c.conn.Close()
    }

    func main() {
    client := New()
    err := client.Connect("ws://echo.websocket.org")
    if err != nil {
    panic(err)
    }

    var wg sync.WaitGroup
    wg.Add(20)
    for i := 1; i <= 20; i++ {
    go func() {
    want := rand.Intn(100)
    res, err := client.Request(want)
    if err != nil {
    fmt.Println("error transaction: %d", err)
    wg.Done()
    return
    }
    got := int(res.(float64))
    if got != want {
    panic(fmt.Errorf("got: %d\nwant: %d\n", got, want))
    }
    fmt.Printf("transaction %d : %d\n", want, got)
    wg.Done()
    }()
    }
    wg.Wait()

    defer func() {
    err = client.Close()
    if err != nil {
    panic(err)
    }
    }()
    }