Example #1
0
func connectionHandler(
	server *RlServer,
	toCoreBuffer *chan protocol.CoreMsg,
	connection net.Conn,
	label string,
	dispatcher *AnswerDispatcher,
	stat *RlStatistic) {
	// server and service connections
	defer connection.Close()
	options := server.GetOption()
	connectionContext := helpers.ConnectionContext{}
	buffer := bufio.NewReader(connection)
	wait := true
	answerWriterActive := false
	var answer *string
	answerBuffer := make(chan protocol.ServerCmd, options.BufferSize)
	localConnectionInfo := newConnectionInfo(label)
	for wait {
		line, _, err := buffer.ReadLine()
		answer = nil
		if err != nil {
			if err == io.EOF {
				// client break connection
				localConnectionInfo.SetActive(false)
				if cid, exists := connectionContext.GetCid(); exists {
					dispatcher.RemoveChannel(cid, true)
				}
			}
			wait = false
		} else {
			stat.Append(StatIncomeDataSize, len(line))
			cmd, err := protocol.NewClientCmd(&line)
			if err != nil {
				wait = false
				rllogger.Outputf(
					rllogger.LogWarn,
					"Incorrect command from %s error: %s",
					label, err)
				answer = protocol.FastErrorAnswer(err, protocol.ErrCodeFormatProblem)
			} else {
				if cmd.RequiredClose() {
					wait = false
					rllogger.Outputf(rllogger.LogInfo, "close command from %s", label)
				} else {
					// check auth
					if cmd.RequiredAuth() && !connectionContext.IsAuth() {
						answer = protocol.FastErrorAnswer(
							"Access denied!", protocol.ErrCodeAccessDenied)
					} else {
						// try get fast handler
						if exists, handler := cmd.GetFastHandler(); exists {
							if newAnswer, err := handler(cmd, &connectionContext, options); err != nil {
								rllogger.Outputf(
									rllogger.LogWarn,
									"Client: %s target: %d Handler error: %s",
									label, cmd.Target, err)
								answer = protocol.FastErrorAnswer(err, protocol.ErrCodeProtocolProblem)
							} else {
								if dataPtr, dumpErr := newAnswer.Dump(); dumpErr != nil {
									answer = protocol.FastErrorAnswer(err, protocol.ErrCodeImplementationProblem)
								} else {
									answer = dataPtr
									if exists, contextUpdater := newAnswer.GetContextUpdater(); exists {
										contextUpdater.Update(&connectionContext)
										rllogger.Outputf(
											rllogger.LogDebug, "-> %s context updated: %s", label, connectionContext)
									}
								}
							}
						} else {
							clientGroup := connectionContext.GetClientGroup()
							msg, convertError := protocol.NewCoreMsg(clientGroup, cmd)
							if convertError != nil {
								rllogger.Outputf(
									rllogger.LogWarn,
									"Msg create error: %s from %s",
									convertError, label)

								answer = protocol.FastErrorAnswer(
									convertError, protocol.ErrCodeProtocolProblem)
							} else {
								*toCoreBuffer <- *msg
								if !answerWriterActive {
									answerWriterActive = true
									// this channel to dispatcher
									dispatcher.AddChannel((*msg).Cid, &answerBuffer)
									// run async answer writer
									go conntectionWriteAnswer(
										connection,
										&answerBuffer,
										localConnectionInfo,
										stat)
								}
							}
						}
					}
				}
			}
			// write sync handler answer
			if answer != nil {
				writen, err := connection.Write([]byte(*answer))
				if err != nil {
					rllogger.Outputf(
						rllogger.LogError,
						"Can't send answer to %s error: %s",
						label, err)
				} else {
					stat.Append(StatOutcomeDataSize, writen)
				}
			}
		}
	}
	//not need "close(answerBuffer)" dispatcher do it
	stat.Append(StatConnectionCount, -1)
	server.ChangeConnectionCount(-1)
}