예제 #1
0
func TestAddAndRemoveRoutes(t *testing.T) {
	a := assert.New(t)

	// Given a Multiplexer
	router := NewPubSubRouter().Go()

	// when i add two routes in the same path
	channel := make(chan MsgAndRoute, chanSize)
	routeBlah1 := router.Subscribe(NewRoute("/blah", channel, "appid01", "user01"))
	routeBlah2 := router.Subscribe(NewRoute("/blah", channel, "appid02", "user01"))

	// and one route in another path
	routeFoo := router.Subscribe(NewRoute("/foo", channel, "appid01", "user01"))

	// then

	// the routes are stored
	a.Equal(2, len(router.routes[guble.Path("/blah")]))
	a.True(routeBlah1.equals(router.routes[guble.Path("/blah")][0]))
	a.True(routeBlah2.equals(router.routes[guble.Path("/blah")][1]))

	a.Equal(1, len(router.routes[guble.Path("/foo")]))
	a.True(routeFoo.equals(router.routes[guble.Path("/foo")][0]))

	// WHEN i remove routes
	router.Unsubscribe(routeBlah1)
	router.Unsubscribe(routeFoo)

	// then they are gone
	a.Equal(1, len(router.routes[guble.Path("/blah")]))
	a.True(routeBlah2.equals(router.routes[guble.Path("/blah")][0]))

	a.Nil(router.routes[guble.Path("/foo")])
}
예제 #2
0
func TestAnIncommingMessageIsNotAllowed(t *testing.T) {
	defer initCtrl(t)()

	wsconn, pubSubSource, messageSink, messageStore := createDefaultMocks([]string{})

	tam := NewTestAccessManager()

	handler := NewWSHandler(pubSubSource, messageSink, messageStore, wsconn, "testuser", AccessManager(tam))
	go func() {
		handler.Start()
	}()
	time.Sleep(time.Millisecond * 2)

	handler.sendChannel <- aTestMessage.Bytes()
	time.Sleep(time.Millisecond * 2)
	//nothing shall have been sent

	//now allow
	tam.allow("testuser", guble.Path("/foo"))

	wsconn.EXPECT().Send(aTestMessage.Bytes())

	time.Sleep(time.Millisecond * 2)

	handler.sendChannel <- aTestMessage.Bytes()
	time.Sleep(time.Millisecond * 2)

}
예제 #3
0
파일: receiver.go 프로젝트: tkrille/guble
// Parses the info in the command
func NewReceiverFromCmd(applicationId string, cmd *guble.Cmd, sendChannel chan []byte, messageSouce PubSubSource, messageStore store.MessageStore) (*Receiver, error) {
	var err error
	rec := &Receiver{
		applicationId:       applicationId,
		sendChannel:         sendChannel,
		messageSouce:        messageSouce,
		messageStore:        messageStore,
		cancelChannel:       make(chan bool, 1),
		enableNotifications: true,
	}
	if len(cmd.Arg) == 0 || cmd.Arg[0] != '/' {
		return nil, fmt.Errorf("command requires at least a path argument, but non given")
	}
	args := strings.SplitN(cmd.Arg, " ", 3)
	rec.path = guble.Path(args[0])

	if len(args) > 1 {
		rec.doFetch = true
		rec.startId, err = strconv.ParseInt(args[1], 10, 64)
		if err != nil {
			return nil, fmt.Errorf("startid has to be empty or int, but was %q: %v", args[1], err)
		}
	}

	rec.doSubscription = true
	if len(args) > 2 {
		rec.doSubscription = false
		rec.maxCount, err = strconv.Atoi(args[2])
		if err != nil {
			return nil, fmt.Errorf("maxCount has to be empty or int, but was %q: %v", args[1], err)
		}
	}
	return rec, nil
}
예제 #4
0
func Test_MessageEntry_MessagesIsStored_And_GetsCorrectParameters(t *testing.T) {
	defer initCtrl(t)()
	a := assert.New(t)

	startTime := time.Now()

	msg := &guble.Message{Path: guble.Path("/topic1")}
	var storedMsg []byte
	var routedMsg *guble.Message
	routerMock := NewMockMessageSink(ctrl)
	messageEntry := NewMessageEntry(routerMock)
	messageStoreMock := NewMockMessageStore(ctrl)
	messageEntry.SetMessageStore(messageStoreMock)

	messageStoreMock.EXPECT().StoreTx("topic1", gomock.Any()).
		Do(func(topic string, callback func(msgId uint64) []byte) {
			storedMsg = callback(uint64(42))
		})

	routerMock.EXPECT().HandleMessage(gomock.Any()).Do(func(msg *guble.Message) {
		routedMsg = msg
		a.Equal(uint64(42), msg.Id)
		t, e := time.Parse(time.RFC3339, msg.PublishingTime) // publishing time
		a.NoError(e)
		a.True(t.After(startTime.Add(-1 * time.Second)))
		a.True(t.Before(time.Now().Add(time.Second)))
	})

	messageEntry.HandleMessage(msg)

	a.Equal(routedMsg.Bytes(), storedMsg)
}
예제 #5
0
파일: interfaces.go 프로젝트: mley/guble
func NewRoute(path string, channel chan MsgAndRoute, applicationId string, userId string) *Route {
	return &Route{
		Path:          guble.Path(path),
		C:             channel,
		UserId:        userId,
		ApplicationId: applicationId,
	}
}
예제 #6
0
func Test_WSHandler_SubscribeAndUnsubscribe(t *testing.T) {
	defer initCtrl(t)()
	a := assert.New(t)

	messages := []string{"+ /foo", "+ /bar", "- /foo"}
	wsconn, pubSubSource, messageSink, messageStore := createDefaultMocks(messages)

	pubSubSource.EXPECT().Subscribe(routeMatcher{"/foo"}).Return(nil)
	wsconn.EXPECT().Send([]byte("#" + guble.SUCCESS_SUBSCRIBED_TO + " /foo"))

	pubSubSource.EXPECT().Subscribe(routeMatcher{"/bar"}).Return(nil)
	wsconn.EXPECT().Send([]byte("#" + guble.SUCCESS_SUBSCRIBED_TO + " /bar"))

	pubSubSource.EXPECT().Unsubscribe(routeMatcher{"/foo"})
	wsconn.EXPECT().Send([]byte("#" + guble.SUCCESS_CANCELED + " /foo"))

	wshandler := runNewWsHandler(wsconn, pubSubSource, messageSink, messageStore)

	a.Equal(1, len(wshandler.receiver))
	a.Equal(guble.Path("/bar"), wshandler.receiver[guble.Path("/bar")].path)
}
예제 #7
0
func (srv *WSHandler) handleCancelCmd(cmd *guble.Cmd) {
	if len(cmd.Arg) == 0 {
		srv.sendError(guble.ERROR_BAD_REQUEST, "- command requires a path argument, but non given")
		return
	}
	path := guble.Path(cmd.Arg)
	rec, exist := srv.receiver[path]
	if exist {
		rec.Stop()
		delete(srv.receiver, path)
	}
}
예제 #8
0
func TestSendMessageWirthPublisherMessageId(t *testing.T) {
	defer initCtrl(t)()

	// given: a send command with PublisherMessageId
	commands := []string{"> /path 42"}
	wsconn, pubSubSource, messageSink, messageStore := createDefaultMocks(commands)

	messageSink.EXPECT().HandleMessage(gomock.Any()).Do(func(msg *guble.Message) {
		assert.Equal(t, guble.Path("/path"), msg.Path)
		assert.Equal(t, "42", msg.PublisherMessageId)
	})

	wsconn.EXPECT().Send([]byte("#send 42"))

	runNewWsHandler(wsconn, pubSubSource, messageSink, messageStore)
}
예제 #9
0
func (api *RestMessageApi) PostMessage(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, `Can not read body`, http.StatusBadRequest)
		return
	}

	msg := &guble.Message{
		Path:                   guble.Path(params.ByName(`topic`)),
		Body:                   body,
		PublisherUserId:        q(r, `userId`),
		PublisherApplicationId: xid.New().String(),
		PublisherMessageId:     q(r, `messageId`),
		HeaderJson:             headersToJson(r.Header),
	}

	api.MessageSink.HandleMessage(msg)
}
예제 #10
0
func (srv *WSHandler) handleSendCmd(cmd *guble.Cmd) {
	guble.Debug("sending %v", string(cmd.Bytes()))
	if len(cmd.Arg) == 0 {
		srv.sendError(guble.ERROR_BAD_REQUEST, "send command requires a path argument, but non given")
		return
	}

	args := strings.SplitN(cmd.Arg, " ", 2)
	msg := &guble.Message{
		Path: guble.Path(args[0]),
		PublisherApplicationId: srv.applicationId,
		PublisherUserId:        srv.userId,
		HeaderJson:             cmd.HeaderJson,
		Body:                   cmd.Body,
	}
	if len(args) == 2 {
		msg.PublisherMessageId = args[1]
	}

	srv.messageSink.HandleMessage(msg)

	srv.sendOK(guble.SUCCESS_SEND, msg.PublisherMessageId)
}
예제 #11
0
func Test_Receiver_Fetch_Subscribe_Fetch_Subscribe(t *testing.T) {
	defer initCtrl(t)()
	a := assert.New(t)

	rec, msgChannel, routerMock, messageStore, err := aMockedReceiver("/foo 0")
	a.NoError(err)

	// fetch first, starting at 0
	fetch_first1 := messageStore.EXPECT().Fetch(gomock.Any()).Do(func(r store.FetchRequest) {
		go func() {
			a.Equal("foo", r.Partition)
			a.Equal(1, r.Direction)
			a.Equal(uint64(0), r.StartId)
			a.Equal(int(math.MaxInt32), r.Count)

			r.StartCallback <- 2

			r.MessageC <- store.MessageAndId{Id: uint64(1), Message: []byte("fetch_first1-a")}
			r.MessageC <- store.MessageAndId{Id: uint64(2), Message: []byte("fetch_first1-b")}
			close(r.MessageC)
		}()
	})

	// there is a gap between fetched and max id
	messageId1 := messageStore.EXPECT().DoInTx(gomock.Any(), gomock.Any()).
		Do(func(partition string, callback func(maxMessageId uint64) error) {
			callback(uint64(3))
		}).Return(unread_messages_available)
	messageId1.After(fetch_first1)

	// fetch again, starting at 3, because, there is still a gap
	fetch_first2 := messageStore.EXPECT().Fetch(gomock.Any()).Do(func(r store.FetchRequest) {
		go func() {
			a.Equal("foo", r.Partition)
			a.Equal(1, r.Direction)
			a.Equal(uint64(3), r.StartId)
			a.Equal(int(math.MaxInt32), r.Count)

			r.StartCallback <- 1
			r.MessageC <- store.MessageAndId{Id: uint64(3), Message: []byte("fetch_first2-a")}
			close(r.MessageC)
		}()
	})
	fetch_first2.After(messageId1)

	// the gap is closed
	messageId2 := messageStore.EXPECT().DoInTx(gomock.Any(), gomock.Any()).
		Do(func(partition string, callback func(maxMessageId uint64) error) {
			callback(uint64(3))
		})
	messageId2.After(fetch_first2)

	// subscribe
	subscribe := routerMock.EXPECT().Subscribe(gomock.Any()).Do(func(r *Route) {
		a.Equal(r.Path, guble.Path("/foo"))
		r.C <- MsgAndRoute{Message: &guble.Message{Id: uint64(4), Body: []byte("router-a")}, Route: r}
		r.C <- MsgAndRoute{Message: &guble.Message{Id: uint64(5), Body: []byte("router-b")}, Route: r}
		close(r.C) // emulate router close
	})
	subscribe.After(messageId2)

	// router closed, so we fetch again, starting at 6 (after meesages from subscribe)
	fetch_after := messageStore.EXPECT().Fetch(gomock.Any()).Do(func(r store.FetchRequest) {
		go func() {
			a.Equal(uint64(6), r.StartId)
			a.Equal(int(math.MaxInt32), r.Count)

			r.StartCallback <- 1
			r.MessageC <- store.MessageAndId{Id: uint64(6), Message: []byte("fetch_after-a")}
			close(r.MessageC)
		}()
	})
	fetch_after.After(subscribe)

	// no gap
	messageId3 := messageStore.EXPECT().DoInTx(gomock.Any(), gomock.Any()).
		Do(func(partition string, callback func(maxMessageId uint64) error) {
			callback(uint64(6))
		})

	messageId3.After(fetch_after)

	// subscribe and don't send messages,
	// so the client has to wait until we stop
	subscribe2 := routerMock.EXPECT().Subscribe(gomock.Any())
	subscribe2.After(messageId3)

	subscriptionLoopDone := make(chan bool)
	go func() {
		rec.subscriptionLoop()
		subscriptionLoopDone <- true
	}()

	expectMessages(a, msgChannel,
		"#"+guble.SUCCESS_FETCH_START+" /foo 2",
		"fetch_first1-a",
		"fetch_first1-b",
		"#"+guble.SUCCESS_FETCH_END+" /foo",
		"#"+guble.SUCCESS_FETCH_START+" /foo 1",
		"fetch_first2-a",
		"#"+guble.SUCCESS_FETCH_END+" /foo",
		"#"+guble.SUCCESS_SUBSCRIBED_TO+" /foo",
		",4,,,,\n\nrouter-a",
		",5,,,,\n\nrouter-b",
		"#"+guble.SUCCESS_FETCH_START+" /foo 1",
		"fetch_after-a",
		"#"+guble.SUCCESS_FETCH_END+" /foo",
		"#"+guble.SUCCESS_SUBSCRIBED_TO+" /foo",
	)

	time.Sleep(time.Millisecond)
	routerMock.EXPECT().Unsubscribe(gomock.Any())
	rec.Stop()

	expectMessages(a, msgChannel,
		"#"+guble.SUCCESS_CANCELED+" /foo",
	)

	expectDone(a, subscriptionLoopDone)
}
예제 #12
0
func getPathFromRawMessage(raw []byte) guble.Path {
	i := strings.Index(string(raw), ",")
	return guble.Path(raw[:i])
}