예제 #1
1
파일: server.go 프로젝트: logan/heim
func Serve(ctx scope.Context, addr string) {
	http.Handle("/metrics", prometheus.Handler())

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		ctx.Terminate(err)
	}

	closed := false
	m := sync.Mutex{}
	closeListener := func() {
		m.Lock()
		if !closed {
			listener.Close()
			closed = true
		}
		m.Unlock()
	}

	// Spin off goroutine to watch ctx and close listener if shutdown requested.
	go func() {
		<-ctx.Done()
		closeListener()
	}()

	if err := http.Serve(listener, nil); err != nil {
		fmt.Printf("http[%s]: %s\n", addr, err)
		ctx.Terminate(err)
	}

	closeListener()
	ctx.WaitGroup().Done()
}
예제 #2
1
파일: serve.go 프로젝트: logan/heim
func (cmd *serveEmbedCmd) run(ctx scope.Context, args []string) error {
	listener, err := net.Listen("tcp", cmd.addr)
	if err != nil {
		return err
	}

	closed := false
	m := sync.Mutex{}
	closeListener := func() {
		m.Lock()
		if !closed {
			listener.Close()
			closed = true
		}
		m.Unlock()
	}

	// Spin off goroutine to watch ctx and close listener if shutdown requested.
	go func() {
		<-ctx.Done()
		closeListener()
	}()

	if err := http.Serve(listener, cmd); err != nil {
		fmt.Printf("http[%s]: %s\n", cmd.addr, err)
		return err
	}

	closeListener()
	ctx.WaitGroup().Done()
	return ctx.Err()
}
예제 #3
1
파일: server.go 프로젝트: logan/heim
func (ctrl *Controller) terminal(ctx scope.Context, ch ssh.Channel) {
	defer ch.Close()

	lines := make(chan string)
	term := terminal.NewTerminal(ch, "> ")

	go func() {
		for ctx.Err() == nil {
			line, err := term.ReadLine()
			if err != nil {
				ctx.Terminate(err)
				return
			}
			lines <- line
		}
	}()

	for {
		var line string
		select {
		case <-ctx.Done():
			return
		case line = <-lines:
		}

		cmd := parse(line)
		fmt.Printf("[control] > %v\n", cmd)
		switch cmd[0] {
		case "":
			continue
		case "quit":
			return
		case "shutdown":
			ctrl.ctx.Terminate(fmt.Errorf("shutdown initiated from console"))
		default:
			runCommand(ctx.Fork(), ctrl, cmd[0], term, cmd[1:])
		}
	}
}
예제 #4
0
파일: jobs.go 프로젝트: logan/heim
func (jq *JobQueueBinding) WaitForJob(ctx scope.Context) error {
	ch := make(chan error)

	// background goroutine to wait on condition
	go func() {
		// synchronize with caller
		<-ch
		jq.m.Unlock()
		jq.Backend.jobQueueListener().wait(jq.Name())
		ch <- nil
	}()

	// synchronize with background goroutine
	jq.m.Lock()
	ch <- nil
	jq.m.Lock()
	jq.m.Unlock()

	select {
	case <-ctx.Done():
		jq.Backend.jobQueueListener().wakeAll(jq.Name())
		<-ch
		return ctx.Err()
	case err := <-ch:
		return err
	}
}
예제 #5
0
파일: serve.go 프로젝트: logan/heim
func (cmd *serveCmd) run(ctx scope.Context, args []string) error {
	listener, err := net.Listen("tcp", cmd.addr)
	if err != nil {
		return err
	}

	m := sync.Mutex{}
	closed := false
	closeListener := func() {
		m.Lock()
		if !closed {
			closed = true
			listener.Close()
		}
		m.Unlock()
	}
	defer closeListener()

	heim, err := getHeim(ctx)
	if err != nil {
		return fmt.Errorf("configuration error: %s", err)
	}
	defer heim.Backend.Close()

	if cmd.static != "" {
		heim.StaticPath = cmd.static
	}

	if err := controller(heim, cmd.consoleAddr); err != nil {
		return fmt.Errorf("controller error: %s", err)
	}

	serverDesc := backend.Config.Cluster.DescribeSelf()
	server, err := backend.NewServer(heim, serverDesc.ID, serverDesc.Era)
	if err != nil {
		return fmt.Errorf("server error: %s", err)
	}

	server.SetInsecureCookies(backend.Config.SetInsecureCookies)
	server.AllowRoomCreation(backend.Config.AllowRoomCreation)
	server.NewAccountMinAgentAge(backend.Config.NewAccountMinAgentAge)

	// Spin off goroutine to watch ctx and close listener if shutdown requested.
	go func() {
		<-ctx.Done()
		closeListener()
	}()

	fmt.Printf("serving era %s on %s\n", serverDesc.Era, cmd.addr)
	if err := http.Serve(listener, newVersioningHandler(server)); err != nil {
		if strings.HasSuffix(err.Error(), "use of closed network connection") {
			return nil
		}
		return err
	}

	return nil
}
예제 #6
0
파일: session.go 프로젝트: logan/heim
func (s *session) Send(ctx scope.Context, cmdType proto.PacketType, payload interface{}) error {
	// Special case: certain events have privileged info that may need to be stripped from them
	switch event := payload.(type) {
	case *proto.PresenceEvent:
		switch s.privilegeLevel() {
		case proto.Staff:
		case proto.Host:
			event.RealClientAddress = ""
		default:
			event.RealClientAddress = ""
			event.ClientAddress = ""
		}
	case *proto.Message:
		if s.privilegeLevel() == proto.General {
			event.Sender.ClientAddress = ""
		}
	case *proto.EditMessageEvent:
		if s.privilegeLevel() == proto.General {
			event.Sender.ClientAddress = ""
		}
	}

	var err error
	payload, err = proto.DecryptPayload(payload, &s.client.Authorization, s.privilegeLevel())
	if err != nil {
		return err
	}

	encoded, err := json.Marshal(payload)
	if err != nil {
		return err
	}

	cmd := &proto.Packet{
		Type: cmdType,
		Data: encoded,
	}

	// Add to outgoing channel. If channel is full, defer to goroutine so as not to block
	// the caller (this may result in deliveries coming out of order).
	select {
	case <-ctx.Done():
		// Session is closed, return error.
		return ctx.Err()
	case s.outgoing <- cmd:
		// Packet delivered to queue.
	default:
		// Queue is full.
		logging.Logger(s.ctx).Printf("outgoing channel full, ordering cannot be guaranteed")
		go func() { s.outgoing <- cmd }()
	}

	return nil
}
예제 #7
0
파일: jobs.go 프로젝트: logan/heim
func (jq *JobQueue) WaitForJob(ctx scope.Context) error {
	ch := make(chan error)

	go func() {
		jq.m.Lock()
		jq.c.Wait()
		jq.m.Unlock()
		ch <- nil
	}()

	select {
	case <-ctx.Done():
		jq.m.Lock()
		jq.c.Broadcast()
		jq.m.Unlock()
		<-ch
		return ctx.Err()
	case err := <-ch:
		return err
	}
}
예제 #8
0
파일: scanner.go 프로젝트: logan/heim
func ScanLoop(ctx scope.Context, c cluster.Cluster, pb *psql.Backend, interval time.Duration) {
	defer ctx.WaitGroup().Done()

	errCount := 0
	for {
		t := time.After(interval)
		select {
		case <-ctx.Done():
			return
		case <-t:
			if err := scan(ctx.Fork(), c, pb); err != nil {
				errCount++
				fmt.Printf("scan error [%d/%d]: %s", errCount, maxErrors, err)
				if errCount > maxErrors {
					fmt.Printf("maximum scan errors exceeded, terminating\n")
					ctx.Terminate(fmt.Errorf("maximum scan errors exceeded"))
					return
				}
				continue
			}
			errCount = 0
		}
	}
}
예제 #9
0
파일: scanner.go 프로젝트: logan/heim
func ScanLoop(ctx scope.Context, listener *pq.Listener) {
	defer ctx.WaitGroup().Done()

	logger := logging.Logger(ctx)
	for {
		select {
		case <-ctx.Done():
			logger.Printf("received cancellation signal, shutting down")
			return
		case notice := <-listener.Notify:
			if notice == nil {
				logger.Printf("received nil from listener")
				continue
			}

			var msg psql.BroadcastMessage

			if err := json.Unmarshal([]byte(notice.Extra), &msg); err != nil {
				logger.Printf("error: pq listen: invalid broadcast: %s", err)
				logger.Printf("         payload: %#v", notice.Extra)
				continue
			}

			switch msg.Event.Type {
			case proto.BounceEventType:
				bounceActivity.WithLabelValues(msg.Room).Inc()
			case proto.JoinEventType:
				joinActivity.WithLabelValues(msg.Room).Inc()
			case proto.PartEventType:
				partActivity.WithLabelValues(msg.Room).Inc()
			case proto.SendEventType:
				messageActivity.WithLabelValues(msg.Room).Inc()
			}
		}
	}
}