func TestWebsockMux(t *testing.T) { sockReq, _ := req.NewSocket() sockRep, _ := rep.NewSocket() tran := NewTransport() l, e := tran.NewListener("ws://127.0.0.1:3336/mysock", sockReq) if e != nil { t.Errorf("Failed new Listener: %v", e) return } muxi, e := l.GetOption(OptionWebSocketMux) if e != nil { t.Errorf("Failed get mux: %v", e) } mux := muxi.(*http.ServeMux) mux.HandleFunc("/bogus", bogusHandler) d, e := tran.NewDialer("ws://127.0.0.1:3336/bogus", sockRep) if e != nil { t.Errorf("Failed new Dialer: %v", e) return } if e = l.Listen(); e != nil { t.Errorf("Listen failed") return } defer l.Close() p, e := d.Dial() if p != nil { defer p.Close() } if e == nil { t.Errorf("Dial passed, when should not have!") return } t.Logf("Got expected error %v", e) // Now let's try to use http client. resp, err := http.Get("http://127.0.0.1:3336/bogus") if err != nil { t.Errorf("Get of boguspath failed: %v", err) return } if resp.StatusCode != 200 { t.Errorf("Response code wrong: %d", resp.StatusCode) return } body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("ReadAll Failed: %v", err) return } if string(body) != bogusstr { t.Errorf("Results mismatch: %s != %s", string(body), bogusstr) } t.Logf("Got body: %s", string(body)) }
func TestWebsockPath(t *testing.T) { sockReq, _ := req.NewSocket() sockRep, _ := rep.NewSocket() tran := NewTransport() l, e := tran.NewListener("ws://127.0.0.1:3335/mysock", sockReq) if e != nil { t.Errorf("Failed new Listener: %v", e) return } d, e := tran.NewDialer("ws://127.0.0.1:3335/boguspath", sockRep) if e != nil { t.Errorf("Failed new Dialer: %v", e) return } if e = l.Listen(); e != nil { t.Errorf("Listen failed") return } defer l.Close() p, e := d.Dial() if p != nil { defer p.Close() } if e == nil { t.Errorf("Dial passed, when should not have!") return } t.Logf("Got expected error %v", e) }
func server(url string, nworkers int) { var sock mangos.Socket var err error var wg sync.WaitGroup rand.Seed(time.Now().UnixNano()) if sock, err = rep.NewSocket(); err != nil { die("can't get new rep socket: %s", err) } if err = sock.SetOption(mangos.OptionRaw, true); err != nil { die("can't set raw mode: %s", err) } sock.AddTransport(ipc.NewTransport()) sock.AddTransport(tcp.NewTransport()) if err = sock.Listen(url); err != nil { die("can't listen on rep socket: %s", err.Error()) } wg.Add(nworkers) fmt.Printf("Starting %d workers\n", nworkers) for id := 0; id < nworkers; id++ { go func(id int) { defer wg.Done() serverWorker(sock, id) }(id) } wg.Wait() }
func node0(url string) { var sock mangos.Socket var err error var msg []byte if sock, err = rep.NewSocket(); err != nil { die("can't get new rep socket: %s", err) } sock.AddTransport(ipc.NewTransport()) sock.AddTransport(tcp.NewTransport()) if err = sock.Listen(url); err != nil { die("can't listen on rep socket: %s", err.Error()) } for { // Could also use sock.RecvMsg to get header msg, err = sock.Recv() if string(msg) == "DATE" { // no need to terminate fmt.Println("NODE0: RECEIVED DATE REQUEST") d := date() fmt.Printf("NODE0: SENDING DATE %s\n", d) err = sock.Send([]byte(d)) if err != nil { die("can't send reply: %s", err.Error()) } } } }
func TestMaxRxSizeSet(t *testing.T) { srep, err := rep.NewSocket() if err != nil { t.Errorf("Failed to make REP: %v", err) return } defer srep.Close() err = srep.SetOption(mangos.OptionMaxRecvSize, 100) if err != nil { t.Errorf("Failed SetOption: %v", err) return } v, err := srep.GetOption(mangos.OptionMaxRecvSize) if err != nil { t.Errorf("Failed GetOption: %v", err) return } if val, ok := v.(int); !ok { t.Errorf("Returned value not type int") } else if val != 100 { t.Errorf("Returned value %d not %d", val, 100) } }
// NewSocket allocates a new Socket. The Socket is the handle used to // access the underlying library. func NewSocket(d Domain, p Protocol) (*Socket, error) { var s Socket var err error s.proto = p s.dom = d switch p { case PUB: s.sock, err = pub.NewSocket() case SUB: s.sock, err = sub.NewSocket() case PUSH: s.sock, err = push.NewSocket() case PULL: s.sock, err = pull.NewSocket() case REQ: s.sock, err = req.NewSocket() case REP: s.sock, err = rep.NewSocket() case SURVEYOR: s.sock, err = surveyor.NewSocket() case RESPONDENT: s.sock, err = respondent.NewSocket() case PAIR: s.sock, err = pair.NewSocket() case BUS: s.sock, err = bus.NewSocket() default: err = mangos.ErrBadProto } if err != nil { return nil, err } switch d { case AF_SP: case AF_SP_RAW: err = s.sock.SetOption(mangos.OptionRaw, true) default: err = errBadDomain } if err != nil { s.sock.Close() return nil, err } // Compat mode sockets should timeout on send if we don't have any pipes if err = s.sock.SetOption(mangos.OptionWriteQLen, 0); err != nil { s.sock.Close() return nil, err } s.rto = -1 s.sto = -1 all.AddTransports(s.sock) return &s, nil }
// NewDaemon creates and returns a new Daemon from the provided Config. An // error is returned if the Daemon cannot be created. func NewDaemon(config *Config) (*Daemon, error) { rep, err := rep.NewSocket() if err != nil { return nil, err } rep.AddTransport(tcp.NewTransport()) return &Daemon{rep, nil, []*publisher{}, []*subscriber{}, config}, nil }
func (rt *repTest) Init(t *testing.T, addr string) bool { var err error if rt.Sock, err = rep.NewSocket(); err != nil { rt.Errorf("NewSocket(): %v", err) return false } return rt.T.Init(t, addr) }
// NewTranTest creates a TranTest. func NewTranTest(tran mangos.Transport, addr string) *TranTest { tt := &TranTest{addr: addr, tran: tran} if strings.HasPrefix(tt.addr, "tls+tcp://") || strings.HasPrefix(tt.addr, "wss://") { tt.cliCfg, _ = GetTLSConfig(false) tt.srvCfg, _ = GetTLSConfig(true) } tt.sockRep, _ = rep.NewSocket() tt.sockReq, _ = req.NewSocket() return tt }
func TestMaxRxSizeInvalidNotInt(t *testing.T) { srep, err := rep.NewSocket() if err != nil { t.Errorf("Failed to make REP: %v", err) return } defer srep.Close() err = srep.SetOption(mangos.OptionMaxRecvSize, "garbage") switch err { case mangos.ErrBadValue: // expected result case nil: t.Errorf("Negative test fail, permitted non-int value") default: t.Errorf("Negative test fail (garbage), wrong error %v", err) } }
func newSocket(url, protocol string) (sock mangos.Socket, err error) { var str string switch protocol { case "bus": sock, err = bus.NewSocket() str = "bus.NewSocket: " case "pair": sock, err = pair.NewSocket() str = "pair.NewSocket: " case "push": sock, err = push.NewSocket() str = "push.NewSocket: " case "pull": sock, err = pull.NewSocket() str = "pull.NewSocket: " case "pub": sock, err = pub.NewSocket() str = "pub.NewSocket: " case "sub": sock, err = sub.NewSocket() str = "sub.NewSocket: " case "request": sock, err = req.NewSocket() str = "request.NewSocket: " case "reply": sock, err = rep.NewSocket() str = "reply.NewSocket: " case "surveyor": sock, err = surveyor.NewSocket() str = "surveyor.NewSocket: " case "respondent": sock, err = respondent.NewSocket() str = "respondent.NewSocket: " } if err != nil { s := fmt.Sprintf(str+" %s", err) return nil, errors.New(s) } sock.AddTransport(ipc.NewTransport()) sock.AddTransport(tcp.NewTransport()) return sock, nil }
func newCmdServer(addr string) (mangos.Socket, error) { log.Info("sensor: creating cmd server...") socket, err := rep.NewSocket() if err != nil { return nil, err } if err := socket.SetOption(mangos.OptionRecvDeadline, time.Second*3); err != nil { socket.Close() return nil, err } //socket.AddTransport(ipc.NewTransport()) socket.AddTransport(tcp.NewTransport()) if err := socket.Listen(addr); err != nil { socket.Close() return nil, err } return socket, nil }
func TestDeviceReqRep(t *testing.T) { s1, err := req.NewSocket() if err != nil { t.Errorf("Failed to open S1: %v", err) return } defer s1.Close() s2, err := rep.NewSocket() if err != nil { t.Errorf("Failed to open S2: %v", err) return } defer s2.Close() switch err := mangos.Device(s1, s2); err { case nil: t.Logf("Matching req/rep ok!") return default: t.Errorf("Got unexpected err: %v", err) return } }
func benchmarkReq(t *testing.B, url string, size int) { if strings.HasPrefix(url, "ipc://") && runtime.GOOS == "windows" { t.Skip("IPC not supported on Windows") return } srvopts := make(map[string]interface{}) cliopts := make(map[string]interface{}) if strings.HasPrefix(url, "wss://") || strings.HasPrefix(url, "tls+tcp://") { srvopts[mangos.OptionTLSConfig] = srvCfg cliopts[mangos.OptionTLSConfig] = cliCfg } srvrdy := make(chan struct{}) srvsock, err := rep.NewSocket() if err != nil || srvsock == nil { t.Errorf("Failed creating server socket: %v", err) return } defer srvsock.Close() all.AddTransports(srvsock) clisock, err := req.NewSocket() if err != nil || clisock == nil { t.Errorf("Failed creating client socket: %v", err) return } defer clisock.Close() all.AddTransports(clisock) go func() { var err error var msg *mangos.Message if err = srvsock.ListenOptions(url, srvopts); err != nil { t.Errorf("Server listen failed: %v", err) return } close(srvrdy) // echo server for { if msg, err = srvsock.RecvMsg(); err != nil { return } if err = srvsock.SendMsg(msg); err != nil { t.Errorf("Server send failed: %v", err) return } } }() if err = clisock.DialOptions(url, cliopts); err != nil { t.Errorf("Client dial failed: %v", err) return } <-srvrdy time.Sleep(time.Millisecond * 1000) t.ResetTimer() msg := make([]byte, size) for i := 0; i < t.N; i++ { if err = clisock.Send(msg); err != nil { t.Errorf("Client send failed: %v", err) return } if msg, err = clisock.Recv(); err != nil { t.Errorf("Client receive failed: %v", err) return } } if size > 128 { t.SetBytes(int64(size)) } t.StopTimer() }
// Serve is for use by a server executable and makes it start listening on // localhost at the configured port for Connect()ions from clients, and then // handles those clients. It returns a *Server that you will typically call // Block() on to block until until your executable receives a SIGINT or SIGTERM, // or you call Stop(), at which point the queues will be safely closed (you'd // probably just exit at that point). The possible errors from Serve() will be // related to not being able to start up at the supplied address; errors // encountered while dealing with clients are logged but otherwise ignored. If // it creates a db file or recreates one from backup, it will say what it did in // the returned msg string. It also spawns your runner clients as needed, // running them via the configured job scheduler, using the configured shell. It // determines the command line to execute for your runner client from the // configured RunnerCmd string you supplied. func Serve(config ServerConfig) (s *Server, msg string, err error) { // port string, webPort string, schedulerName string, shell string, runnerCmd string, dbFile string, dbBkFile string, deployment string sock, err := rep.NewSocket() if err != nil { return } // we open ourselves up to possible denial-of-service attack if a client // sends us tons of data, but at least the client doesn't silently hang // forever when it legitimately wants to Add() a ton of jobs // unlimited Recv() length if err = sock.SetOption(mangos.OptionMaxRecvSize, 0); err != nil { return } // we use raw mode, allowing us to respond to multiple clients in // parallel if err = sock.SetOption(mangos.OptionRaw, true); err != nil { return } // we'll wait ServerInterruptTime to recv from clients before trying again, // allowing us to check if signals have been passed if err = sock.SetOption(mangos.OptionRecvDeadline, ServerInterruptTime); err != nil { return } sock.AddTransport(tcp.NewTransport()) if err = sock.Listen("tcp://0.0.0.0:" + config.Port); err != nil { return } // serving will happen in a goroutine that will stop on SIGINT or SIGTERM, // of if something is sent on the quit channel sigs := make(chan os.Signal, 2) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) stop := make(chan bool, 1) done := make(chan error, 1) // if we end up spawning clients on other machines, they'll need to know // our non-loopback ip address so they can connect to us ip := CurrentIP() if ip == "" { err = Error{"", "Serve", "", ErrNoHost} return } // to be friendly we also record the hostname, but it's possible this isn't // defined, hence we don't rely on it for anything important host, err := os.Hostname() if err != nil { host = "localhost" } // we will spawn runner clients via the requested job scheduler sch, err := scheduler.New(config.SchedulerName, config.SchedulerConfig) if err != nil { return } // we need to persist stuff to disk, and we do so using boltdb db, msg, err := initDB(config.DBFile, config.DBFileBackup, config.Deployment) if err != nil { return } s = &Server{ ServerInfo: &ServerInfo{Addr: ip + ":" + config.Port, Host: host, Port: config.Port, WebPort: config.WebPort, PID: os.Getpid(), Deployment: config.Deployment, Scheduler: config.SchedulerName, Mode: ServerModeNormal}, sock: sock, ch: new(codec.BincHandle), qs: make(map[string]*queue.Queue), rpl: &rgToKeys{lookup: make(map[string]map[string]bool)}, db: db, stop: stop, done: done, up: true, scheduler: sch, sgroupcounts: make(map[string]int), sgrouptrigs: make(map[string]int), sgtr: make(map[string]*scheduler.Requirements), rc: config.RunnerCmd, statusCaster: bcast.NewGroup(), } // if we're restarting from a state where there were incomplete jobs, we // need to load those in to the appropriate queues now priorJobs, err := db.recoverIncompleteJobs() if err != nil { return } if len(priorJobs) > 0 { jobsByQueue := make(map[string][]*queue.ItemDef) for _, job := range priorJobs { jobsByQueue[job.Queue] = append(jobsByQueue[job.Queue], &queue.ItemDef{Key: job.key(), Data: job, Priority: job.Priority, Delay: 0 * time.Second, TTR: ServerItemTTR, Dependencies: job.Dependencies.incompleteJobKeys(s.db)}) } for qname, itemdefs := range jobsByQueue { q := s.getOrCreateQueue(qname) _, _, err = s.enqueueItems(q, itemdefs) if err != nil { return } } } // set up responding to command-line clients and signals go func() { // log panics and die defer s.logPanic("jobqueue serving", true) for { select { case sig := <-sigs: s.shutdown() var serr error switch sig { case os.Interrupt: serr = Error{"", "Serve", "", ErrClosedInt} case syscall.SIGTERM: serr = Error{"", "Serve", "", ErrClosedTerm} } signal.Stop(sigs) done <- serr return case <-stop: s.shutdown() signal.Stop(sigs) done <- Error{"", "Serve", "", ErrClosedStop} return default: // receive a clientRequest from a client m, rerr := sock.RecvMsg() if rerr != nil { if rerr != mangos.ErrRecvTimeout { log.Println(rerr) } continue } // parse the request, do the desired work and respond to the client go func() { // log panics and continue defer s.logPanic("jobqueue server client handling", false) herr := s.handleRequest(m) if ServerLogClientErrors && herr != nil { log.Println(herr) } }() } } }() // set up the web interface go func() { // log panics and die defer s.logPanic("jobqueue web server", true) mux := http.NewServeMux() mux.HandleFunc("/", webInterfaceStatic) mux.HandleFunc("/status_ws", webInterfaceStatusWS(s)) go http.ListenAndServe("0.0.0.0:"+config.WebPort, mux) // *** should use ListenAndServeTLS, which needs certs (http package has cert creation)... go s.statusCaster.Broadcasting(0) }() return }
// limitations under the License. package tcp import ( "bytes" "testing" "time" "github.com/go-mangos/mangos" "github.com/go-mangos/mangos/protocol/rep" "github.com/go-mangos/mangos/protocol/req" ) var tran = NewTransport() var sockRep, _ = rep.NewSocket() var sockReq, _ = req.NewSocket() func TestTCPListenAndAccept(t *testing.T) { addr := "tcp://127.0.0.1:3333" t.Logf("Establishing accepter") l, err := tran.NewListener(addr, sockRep) if err != nil { t.Errorf("NewListener failed: %v", err) return } defer l.Close() if err = l.Listen(); err != nil { t.Errorf("Listen failed: %v", err) return }
// This test verifies that we can use stock http server instances with // our own websocket handler. func TestWebsockHandler(t *testing.T) { sockReq, _ := req.NewSocket() sockRep, _ := rep.NewSocket() tran := NewTransport() l, e := tran.NewListener("ws://127.0.0.1:3337/mysock", sockReq) if e != nil { t.Errorf("Failed new Listener: %v", e) return } hi, e := l.GetOption(OptionWebSocketHandler) if e != nil { t.Errorf("Failed get WebSocketHandler: %v", e) } handler := hi.(http.Handler) mux := http.NewServeMux() mux.HandleFunc("/bogus", bogusHandler) mux.Handle("/mysock", handler) // Note that we are *counting* on this to die gracefully when our // program exits. There appears to be no way to shutdown http // instances gracefully. go http.ListenAndServe("127.0.0.1:3337", mux) d, e := tran.NewDialer("ws://127.0.0.1:3337/bogus", sockRep) if e != nil { t.Errorf("Failed new Dialer: %v", e) return } defer l.Close() p, e := d.Dial() if p != nil { defer p.Close() } if e == nil { t.Errorf("Dial passed, when should not have!") return } t.Logf("Got expected error %v", e) // Now let's try to use http client. resp, err := http.Get("http://127.0.0.1:3337/bogus") if err != nil { t.Errorf("Get of boguspath failed: %v", err) return } if resp.StatusCode != 200 { t.Errorf("Response code wrong: %d", resp.StatusCode) return } body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("ReadAll Failed: %v", err) return } if string(body) != bogusstr { t.Errorf("Results mismatch: %s != %s", string(body), bogusstr) } t.Logf("Got body: %s", string(body)) }
func testMaxRx(t *testing.T, addr string, tran mangos.Transport) { maxrx := 100 rp, err := rep.NewSocket() if err != nil { t.Errorf("Failed to make REP: %v", err) return } defer rp.Close() rp.AddTransport(tran) // Now try setting the option err = rp.SetOption(mangos.OptionMaxRecvSize, maxrx) if err != nil { t.Errorf("Failed set MaxRecvSize: %v", err) return } // At this point, we can issue requests on rq, and read them from rp. if err = rp.SetOption(mangos.OptionRecvDeadline, time.Millisecond*20); err != nil { t.Errorf("Failed set recv deadline") return } if err = rp.Listen(addr); err != nil { t.Errorf("Failed listen: %v", err) return } rq, err := req.NewSocket() if err != nil { t.Errorf("Failed to make REQ: %v", err) return } defer rq.Close() rq.AddTransport(tran) if err = rq.Dial(addr); err != nil { t.Errorf("Failed dial: %v", err) return } time.Sleep(time.Millisecond * 10) msg1 := make([]byte, maxrx+1) msg2 := make([]byte, maxrx+1) for i := 0; i < len(msg1); i++ { msg1[i] = 'A' + byte(i%26) msg2[i] = 'A' + byte(i%26) } // NB: maxrx *includes* the header. if err = rq.Send(msg1[:maxrx-8]); err != nil { t.Errorf("Failed first send: %v", err) return } v, err := rp.Recv() if err != nil { t.Errorf("Failed first recv: %v", err) return } else if !bytes.Equal(v, msg2[:maxrx-8]) { t.Errorf("Got wrong message: %v", v) return } else { t.Logf("Got good message") } if err = rq.Send(msg1); err != nil { t.Errorf("Failed send drop: %v", err) return } v, err = rp.Recv() switch err { case mangos.ErrRecvTimeout: // expected t.Logf("MaxRx honored") case nil: t.Errorf("Message not dropped: %v", v) default: t.Errorf("Got unexpected error: %v", err) } }
func TestPortHook(t *testing.T) { t.Logf("Testing Add Hook") srvtest := &hooktest{allow: true, t: t} clitest := &hooktest{allow: true, t: t} addr := AddrTestTCP srvtest.expect = []hookinfo{{ action: mangos.PortActionAdd, addr: addr, server: true, isopen: true, }, { action: mangos.PortActionRemove, addr: addr, server: true, isopen: false, }} clitest.expect = []hookinfo{{ action: mangos.PortActionAdd, addr: addr, server: false, isopen: true, }, { action: mangos.PortActionRemove, addr: addr, server: false, isopen: false, }} sockreq, err := req.NewSocket() if err != nil { t.Errorf("NewSocket failed: %v", err) return } defer sockreq.Close() sockreq.AddTransport(tcp.NewTransport()) if sockreq.SetPortHook(clitest.Hook) != nil { t.Errorf("SetPortHook result not nil!") return } d, err := sockreq.NewDialer(addr, nil) if err != nil { t.Errorf("NewDialer failed: %v", err) return } sockrep, err := rep.NewSocket() if err != nil { t.Errorf("NewSocket failed: %v", err) return } defer sockrep.Close() sockrep.AddTransport(tcp.NewTransport()) if sockrep.SetPortHook(srvtest.Hook) != nil { t.Errorf("SetPortHook result not nil!") return } l, err := sockrep.NewListener(addr, nil) if err != nil { t.Errorf("NewListener failed: %v", err) return } if err := l.Listen(); err != nil { t.Errorf("Listen failed: %v", err) return } if err := d.Dial(); err != nil { t.Errorf("Dial failed: %v", err) return } // wait a second for connection to establish // could also issue a req/rep... t.Logf("Waiting a bit...") time.Sleep(100 * time.Millisecond) d.Close() l.Close() // shut down the server sockrep.Close() sockreq.Close() time.Sleep(100 * time.Millisecond) clitest.Lock() defer clitest.Unlock() srvtest.Lock() defer srvtest.Unlock() for i, info := range clitest.expect { t.Logf("Exp C[%d]: %s", i, info.String()) } for i, info := range clitest.calls { t.Logf("Got C[%d]: %s", i, info.String()) } for i, info := range srvtest.expect { t.Logf("Exp S[%d]: %s", i, info.String()) } for i, info := range srvtest.calls { t.Logf("Got S[%d]: %s", i, info.String()) } if len(srvtest.calls) != len(srvtest.expect) { t.Errorf("Server got wrong # calls, %d != %d", len(srvtest.calls), len(srvtest.expect)) return } for i := range srvtest.calls { if srvtest.calls[i].String() != srvtest.expect[i].String() { t.Errorf("Server hook %d wrong: %s != %s", i, srvtest.calls[i].String(), srvtest.expect[i].String()) } } if len(clitest.calls) != len(clitest.expect) { t.Errorf("Client got wrong # calls, %d != %d", len(clitest.calls), len(clitest.expect)) return } for i := range clitest.calls { if clitest.calls[i].String() != clitest.expect[i].String() { t.Errorf("Server hook %d wrong: %s != %s", i, clitest.calls[i].String(), clitest.expect[i].String()) } } }