Example #1
0
func newConnectionHandler(ctx context.Context, newDisp dispatcherInit) (*ConnectionHandler, error) {
	connID := getID(ctx)
	ctx = apexctx.WithLogger(ctx, apexctx.GetLogger(ctx).WithField("conn.id", connID))

	return &ConnectionHandler{
		ctx:            ctx,
		sessions:       newSessions(),
		highestChannel: 0,

		newDispatcher: newDisp,

		connID: connID,
	}, nil
}
Example #2
0
// HandleConn decodes commands from Cocaine runtime and calls dispatchers
func (h *ConnectionHandler) HandleConn(conn io.ReadWriteCloser) {
	defer func() {
		conn.Close()
		apexctx.GetLogger(h.ctx).Errorf("Connection has been closed")
	}()

	ctx, cancel := context.WithCancel(h.ctx)
	defer cancel()
	logger := apexctx.GetLogger(h.ctx)

	r := msgp.NewReader(conn)
LOOP:
	for {
		hasHeaders, channel, c, err := h.next(r)
		if err != nil {
			if err == io.EOF {
				return
			}
			apexctx.GetLogger(h.ctx).WithError(err).Errorf("next(): unable to read message")
			return
		}
		logger.Infof("channel %d, number %d", channel, c)

		dispatcher, ok := h.sessions.Get(channel)
		if !ok {
			if channel <= h.highestChannel {
				// dispatcher was detached from ResponseStream.OnClose
				// This message must be `close` message.
				// `channel`, `number` are parsed, skip `args` and probably `headers`
				logger.Infof("dispatcher for channel %d was detached", channel)
				r.Skip()
				if hasHeaders {
					r.Skip()
				}
				continue LOOP
			}

			h.highestChannel = channel

			ctx = apexctx.WithLogger(ctx, logger.WithField("channel", fmt.Sprintf("%s.%d", h.connID, channel)))
			rs := newResponseStream(ctx, conn, channel)
			rs.OnClose(func(ctx context.Context) {
				h.sessions.Detach(channel)
			})
			dispatcher = h.newDispatcher(ctx, rs)
		}

		dispatcher, err = dispatcher.Handle(c, r)
		// NOTE: remove it when the headers are being handling properly
		if hasHeaders {
			r.Skip()
		}

		if err != nil {
			if err == ErrInvalidArgsNum {
				logger.WithError(err).Errorf("channel %d, number %d", channel, c)
				return
			}

			logger.WithError(err).Errorf("Handle returned an error")
			h.sessions.Detach(channel)
			continue LOOP
		}
		if dispatcher == nil {
			h.sessions.Detach(channel)
			continue LOOP
		}

		h.sessions.Attach(channel, dispatcher)
	}
}
Example #3
0
func main() {
	if showVersion {
		printVersion()
		return
	}

	data, err := ioutil.ReadFile(configpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "unable to read config: %v\n", err)
		os.Exit(1)
	}

	config, err := config.Parse(data)
	if err != nil {
		fmt.Fprintf(os.Stderr, "config is invalid: %v\n", err)
		os.Exit(1)
	}

	if config.Version != requiredConfigVersion {
		fmt.Fprintf(os.Stderr, "invalid config version (%d). %d is required\n", config.Version, requiredConfigVersion)
		os.Exit(1)
	}

	output, err := logutils.NewLogFileOutput(config.Logger.Output)
	if err != nil {
		fmt.Fprintf(os.Stderr, "unable to open logfile output: %v\n", err)
		os.Exit(1)
	}
	defer output.Close()

	logger := &log.Logger{
		Level:   log.Level(config.Logger.Level),
		Handler: logutils.NewLogHandler(output),
	}

	ctx := apexctx.WithLogger(apexctx.Background(), log.NewEntry(logger))
	ctx, cancelFunc := context.WithCancel(ctx)
	defer cancelFunc()

	switch name := config.Metrics.Type; name {
	case "graphite":
		var cfg exportmetrics.GraphiteConfig
		if err = json.Unmarshal(config.Metrics.Args, &cfg); err != nil {
			logger.WithError(err).WithField("name", name).Fatal("unable to decode graphite exporter config")
		}

		sender, err := exportmetrics.NewGraphiteExporter(&cfg)
		if err != nil {
			logger.WithError(err).WithField("name", name).Fatal("unable to create GraphiteExporter")
		}

		minimalPeriod := 5 * time.Second
		period := time.Duration(config.Metrics.Period)
		if period < minimalPeriod {
			logger.Warnf("metrics: specified period is too low. Set %s", minimalPeriod)
			period = minimalPeriod
		}

		go func(ctx context.Context, p time.Duration) {
			for {
				select {
				case <-time.After(p):
					if err := sender.Send(ctx, metrics.DefaultRegistry); err != nil {
						logger.WithError(err).WithField("name", name).Error("unable to send metrics")
					}
				case <-ctx.Done():
					return
				}
			}
		}(ctx, period)
	case "":
		logger.Warn("metrics: exporter is not specified")
	default:
		logger.WithError(err).WithField("exporter", name).Fatal("unknown exporter")
	}
	go func() {
		collect(ctx)
		for range time.Tick(30 * time.Second) {
			collect(ctx)
		}
	}()

	checkLimits(ctx)

	boxTypes := map[string]struct{}{}
	boxes := isolate.Boxes{}
	for name, cfg := range config.Isolate {
		if _, ok := boxTypes[cfg.Type]; ok {
			logger.WithField("box", name).WithField("type", cfg.Type).Fatal("dublicated box type")
		}
		boxCtx := apexctx.WithLogger(ctx, logger.WithField("box", name))
		box, err := isolate.ConstructBox(boxCtx, cfg.Type, cfg.Args)
		if err != nil {
			logger.WithError(err).WithField("box", name).WithField("type", cfg.Type).Fatal("unable to create box")
		}
		boxes[name] = box
		boxTypes[cfg.Type] = struct{}{}
	}

	ctx = context.WithValue(ctx, isolate.BoxesTag, boxes)

	if config.DebugServer != "" {
		logger.WithField("endpoint", config.DebugServer).Info("start debug HTTP-server")
		go func() {
			logger.WithError(http.ListenAndServe(config.DebugServer, nil)).Error("debug server is listening")
		}()
	}

	var wg sync.WaitGroup
	for _, endpoint := range config.Endpoints {
		logger.WithField("endpoint", endpoint).Info("start TCP server")
		ln, err := net.Listen("tcp", endpoint)
		if err != nil {
			logger.WithError(err).WithField("endpoint", endpoint).Fatal("unable to listen to")
		}
		defer ln.Close()

		wg.Add(1)
		func(ln net.Listener) {
			defer wg.Done()
			lnLogger := logger.WithField("listener", ln.Addr())
			for {
				conn, err := ln.Accept()
				if err != nil {
					lnLogger.WithError(err).Error("Accept")
					continue
				}

				// TODO: more optimal way
				connID := fmt.Sprintf("%.4x", md5.Sum([]byte(fmt.Sprintf("%s.%d", conn.RemoteAddr().String(), time.Now().Unix()))))
				lnLogger.WithFields(log.Fields{"remote.addr": conn.RemoteAddr(), "conn.id": connID}).Info("accepted new connection")

				connHandler, err := isolate.NewConnectionHandler(context.WithValue(ctx, "conn.id", connID))
				if err != nil {
					lnLogger.WithError(err).Fatal("unable to create connection handler")
				}

				go func() {
					conns.Inc(1)
					defer conns.Dec(1)
					connHandler.HandleConn(conn)
				}()
			}
		}(ln)
	}

	wg.Wait()
}