func (p *protocolV2) TOUCH(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { state := atomic.LoadInt32(&client.State) if state != stateSubscribed && state != stateClosing { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot TOUCH in current state") } if len(params) < 2 { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "TOUCH insufficient number of params") } id, err := getFullMessageID(params[1]) if err != nil { return nil, protocol.NewFatalClientErr(nil, E_INVALID, err.Error()) } client.LockRead() msgTimeout := client.MsgTimeout client.UnlockRead() if client.Channel == nil { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "No channel") } err = client.Channel.TouchMessage(client.ID, nsqd.GetMessageIDFromFullMsgID(*id), msgTimeout) if err != nil { return nil, protocol.NewClientErr(err, "E_TOUCH_FAILED", fmt.Sprintf("TOUCH %v failed %s", *id, err.Error())) } return nil, nil }
func getTopicChan(command string, params []string) (string, string, string, error) { if len(params) <= 1 { return "", "", "", protocol.NewFatalClientErr(nil, "E_INVALID", fmt.Sprintf("%s insufficient number of params", command)) } topicName := params[0] partitionID := params[1] var channelName string if len(params) >= 3 { channelName = params[2] } if !protocol.IsValidTopicName(topicName) { return "", "", "", protocol.NewFatalClientErr(nil, "E_BAD_TOPIC", fmt.Sprintf("%s topic name '%s' is not valid", command, topicName)) } if channelName != "" && !protocol.IsValidChannelName(channelName) { return "", "", "", protocol.NewFatalClientErr(nil, "E_BAD_CHANNEL", fmt.Sprintf("%s channel name '%s' is not valid", command, channelName)) } if _, err := GetValidPartitionID(partitionID); err != nil { return "", "", "", protocol.NewFatalClientErr(nil, "E_BAD_PARTITIONID", fmt.Sprintf("%s partition id '%s' is not valid", command, partitionID)) } return topicName, channelName, partitionID, nil }
func enforceTLSPolicy(client *nsqd.ClientV2, p *protocolV2, command []byte) error { if p.ctx.getOpts().TLSRequired != TLSNotRequired && atomic.LoadInt32(&client.TLS) != 1 { return protocol.NewFatalClientErr(nil, E_INVALID, fmt.Sprintf("cannot %s in current state (TLS required)", command)) } return nil }
func (p *LookupProtocolV1) UNREGISTER(client *ClientV1, reader *bufio.Reader, params []string) ([]byte, error) { if client.peerInfo == nil { return nil, protocol.NewFatalClientErr(nil, "E_INVALID", "client must IDENTIFY") } topic, channel, pid, err := getTopicChan("UNREGISTER", params) if err != nil { // check if old nsqd if client.peerInfo.IsOldPeer() { nsqlookupLog.Logf("client %v is old node trying unregister", client) pid = strconv.Itoa(OLD_VERSION_PID) topic, channel, err = getTopicChanFromOld("UNREGISTER", params) if err != nil { return nil, err } } else { return nil, err } } atomic.StoreInt64(&client.peerInfo.lastUpdate, time.Now().UnixNano()) if channel != "" { key := ChannelReg{ PartitionID: pid, PeerId: client.peerInfo.Id, Channel: channel, } removed := p.ctx.nsqlookupd.DB.RemoveChannelReg(topic, key) if removed { nsqlookupLog.Logf("DB: client(%s) UNREGISTER channel %v on topic:%s-%v", client, channel, topic, pid) } } else { // no channel was specified so this is a topic unregistration // remove all of the channel registrations... // normally this shouldn't happen which is why we print a warning message // if anything is actually removed key := ChannelReg{ PartitionID: pid, PeerId: client.peerInfo.Id, Channel: "*", } removed := p.ctx.nsqlookupd.DB.RemoveChannelReg(topic, key) if removed { nsqlookupLog.LogWarningf(" client(%s) unexpected UNREGISTER all channels under topic:%s pid:%s", client, topic, pid) } if removed := p.ctx.nsqlookupd.DB.RemoveTopicProducer(topic, pid, client.peerInfo.Id); removed { nsqlookupLog.Logf("DB: client(%s) UNREGISTER topic :%s pid:%s", client, topic, pid) } } return []byte("OK"), nil }
func readMPUB(r io.Reader, tmp []byte, topic *nsqd.Topic, maxMessageSize int64, traceEnable bool) ([]*nsqd.Message, []*bytes.Buffer, error) { numMessages, err := readLen(r, tmp) if err != nil { return nil, nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "MPUB failed to read message count") } if numMessages <= 0 { return nil, nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", fmt.Sprintf("MPUB invalid message count %d", numMessages)) } messages := make([]*nsqd.Message, 0, numMessages) buffers := make([]*bytes.Buffer, 0, numMessages) for i := int32(0); i < numMessages; i++ { messageSize, err := readLen(r, tmp) if err != nil { return nil, buffers, protocol.NewFatalClientErr(err, "E_BAD_MESSAGE", fmt.Sprintf("MPUB failed to read message(%d) body size", i)) } if messageSize <= 0 { return nil, buffers, protocol.NewFatalClientErr(nil, "E_BAD_MESSAGE", fmt.Sprintf("MPUB invalid message(%d) body size %d", i, messageSize)) } if int64(messageSize) > maxMessageSize { return nil, buffers, protocol.NewFatalClientErr(nil, "E_BAD_MESSAGE", fmt.Sprintf("MPUB message too big %d > %d", messageSize, maxMessageSize)) } b := topic.BufferPoolGet(int(messageSize)) buffers = append(buffers, b) _, err = io.CopyN(b, r, int64(messageSize)) if err != nil { return nil, buffers, protocol.NewFatalClientErr(err, "E_BAD_MESSAGE", "MPUB failed to read message body") } msgBody := b.Bytes()[:messageSize] traceID := uint64(0) var realBody []byte if traceEnable { if messageSize <= nsqd.MsgTraceIDLength { return nil, buffers, protocol.NewFatalClientErr(nil, "E_BAD_MESSAGE", fmt.Sprintf("MPUB invalid message(%d) body size %d for tracing", i, messageSize)) } traceID = binary.BigEndian.Uint64(msgBody[:nsqd.MsgTraceIDLength]) realBody = msgBody[nsqd.MsgTraceIDLength:] } else { realBody = msgBody } msg := nsqd.NewMessage(0, realBody) msg.TraceID = traceID messages = append(messages, msg) topic.GetDetailStats().UpdateTopicMsgStats(int64(len(realBody)), 0) } return messages, buffers, nil }
func (p *protocolV2) CLS(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { state := atomic.LoadInt32(&client.State) if state != stateSubscribed { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot CLS in current state") } client.StartClose() return []byte("CLOSE_WAIT"), nil }
// params: [command topic channel partition consume_start] func (p *protocolV2) SUBADVANCED(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { var consumeStart *ConsumeOffset if len(params) > 4 && len(params[4]) > 0 { consumeStart = &ConsumeOffset{} err := consumeStart.FromBytes(params[4]) if err != nil { return nil, protocol.NewFatalClientErr(nil, E_INVALID, err.Error()) } } return p.internalSUB(client, params, true, false, consumeStart) }
func getTopicChanFromOld(command string, params []string) (string, string, error) { if len(params) <= 0 { return "", "", protocol.NewFatalClientErr(nil, "E_INVALID", fmt.Sprintf("%s insufficient number of params", command)) } topicName := params[0] var channelName string if len(params) >= 2 { channelName = params[1] } if !protocol.IsValidTopicName(topicName) { return "", "", protocol.NewFatalClientErr(nil, "E_BAD_TOPIC", fmt.Sprintf("%s topic name '%s' is not valid", command, topicName)) } if channelName != "" && !protocol.IsValidChannelName(channelName) { return "", "", protocol.NewFatalClientErr(nil, "E_BAD_CHANNEL", fmt.Sprintf("%s channel name '%s' is not valid", command, channelName)) } return topicName, channelName, nil }
func (p *protocolV2) CheckAuth(client *nsqd.ClientV2, cmd, topicName, channelName string) error { // if auth is enabled, the client must have authorized already // compare topic/channel against cached authorization data (refetching if expired) if p.ctx.isAuthEnabled() { if !client.HasAuthorizations() { return protocol.NewFatalClientErr(nil, "E_AUTH_FIRST", fmt.Sprintf("AUTH required before %s", cmd)) } ok, err := client.IsAuthorized(topicName, channelName) if err != nil { // we don't want to leak errors contacting the auth server to untrusted clients nsqd.NsqLogger().Logf("PROTOCOL(V2): [%s] Auth Failed %s", client, err) return protocol.NewFatalClientErr(nil, "E_AUTH_FAILED", "AUTH failed") } if !ok { return protocol.NewFatalClientErr(nil, "E_UNAUTHORIZED", fmt.Sprintf("AUTH failed for %s on %q %q", cmd, topicName, channelName)) } } return nil }
func (p *LookupProtocolV1) Exec(client *ClientV1, reader *bufio.Reader, params []string) ([]byte, error) { switch params[0] { case "PING": return p.PING(client, params) case "IDENTIFY": return p.IDENTIFY(client, reader, params[1:]) case "REGISTER": return p.REGISTER(client, reader, params[1:]) case "UNREGISTER": return p.UNREGISTER(client, reader, params[1:]) } return nil, protocol.NewFatalClientErr(nil, "E_INVALID", fmt.Sprintf("invalid command %s", params[0])) }
func (p *protocolV2) internalMPUBAndTrace(client *nsqd.ClientV2, params [][]byte, traceEnable bool) ([]byte, error) { startPub := time.Now().UnixNano() _, topic, preErr := p.preparePub(client, params, p.ctx.getOpts().MaxBodySize) if preErr != nil { return nil, preErr } messages, buffers, preErr := readMPUB(client.Reader, client.LenSlice, topic, p.ctx.getOpts().MaxMsgSize, traceEnable) defer func() { for _, b := range buffers { topic.BufferPoolPut(b) } }() if preErr != nil { return nil, preErr } topicName := topic.GetTopicName() partition := topic.GetTopicPart() if p.ctx.checkForMasterWrite(topicName, partition) { id, offset, rawSize, err := p.ctx.PutMessages(topic, messages) //p.ctx.setHealth(err) if err != nil { topic.GetDetailStats().UpdatePubClientStats(client.RemoteAddr().String(), client.UserAgent, "tcp", int64(len(messages)), true) nsqd.NsqLogger().LogErrorf("topic %v put message failed: %v", topic.GetFullName(), err) if clusterErr, ok := err.(*consistence.CommonCoordErr); ok { if !clusterErr.IsLocalErr() { return nil, protocol.NewClientErr(err, FailedOnNotWritable, "") } } return nil, protocol.NewFatalClientErr(err, "E_MPUB_FAILED", err.Error()) } topic.GetDetailStats().UpdatePubClientStats(client.RemoteAddr().String(), client.UserAgent, "tcp", int64(len(messages)), false) cost := time.Now().UnixNano() - startPub topic.GetDetailStats().UpdateTopicMsgStats(0, cost/1000/int64(len(messages))) if !traceEnable { return okBytes, nil } return getTracedReponse(buffers[0], id, 0, offset, rawSize) } else { topic.GetDetailStats().UpdatePubClientStats(client.RemoteAddr().String(), client.UserAgent, "tcp", int64(len(messages)), true) //forward to master of topic nsqd.NsqLogger().LogDebugf("should put to master: %v, from %v", topic.GetFullName(), client.RemoteAddr) topic.DisableForSlave() return nil, protocol.NewClientErr(preErr, FailedOnNotLeader, "") } }
func (p *protocolV2) internalCreateTopic(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { var err error if len(params) < 3 { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "CREATE_TOPIC insufficient number of parameters") } topicName := string(params[1]) if !protocol.IsValidTopicName(topicName) { return nil, protocol.NewFatalClientErr(nil, "E_BAD_TOPIC", fmt.Sprintf("topic name %q is not valid", topicName)) } partition, err := strconv.Atoi(string(params[2])) if err != nil { return nil, protocol.NewFatalClientErr(nil, "E_BAD_PARTITION", fmt.Sprintf("topic partition is not valid: %v", err)) } if err = p.CheckAuth(client, "CREATE_TOPIC", topicName, ""); err != nil { return nil, err } if partition < 0 { return nil, protocol.NewFatalClientErr(nil, "E_BAD_PARTITION", "partition should not less than 0") } if p.ctx.nsqdCoord != nil { return nil, protocol.NewClientErr(err, "E_CREATE_TOPIC_FAILED", fmt.Sprintf("CREATE_TOPIC is not allowed here while cluster feature enabled.")) } topic := p.ctx.getTopic(topicName, partition) if topic == nil { return nil, protocol.NewClientErr(err, "E_CREATE_TOPIC_FAILED", fmt.Sprintf("CREATE_TOPIC %v failed", topicName)) } return okBytes, nil }
func (p *protocolV2) RDY(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { state := atomic.LoadInt32(&client.State) if state == stateClosing { // just ignore ready changes on a closing channel nsqd.NsqLogger().Logf( "PROTOCOL(V2): [%s] ignoring RDY after CLS in state ClientStateV2Closing", client) return nil, nil } if state != stateSubscribed { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot RDY in current state") } count := int64(1) if len(params) > 1 { b10, err := protocol.ByteToBase10(params[1]) if err != nil { return nil, protocol.NewFatalClientErr(err, E_INVALID, fmt.Sprintf("RDY could not parse count %s", params[1])) } count = int64(b10) } if count < 0 || count > p.ctx.getOpts().MaxRdyCount { // this needs to be a fatal error otherwise clients would have // inconsistent state return nil, protocol.NewFatalClientErr(nil, E_INVALID, fmt.Sprintf("RDY count %d out of range 0-%d", count, p.ctx.getOpts().MaxRdyCount)) } client.SetReadyCount(count) return nil, nil }
func (p *protocolV2) preparePub(client *nsqd.ClientV2, params [][]byte, maxBody int64) (int32, *nsqd.Topic, error) { var err error if len(params) < 2 { return 0, nil, protocol.NewFatalClientErr(nil, E_INVALID, "insufficient number of parameters") } topicName := string(params[1]) if !protocol.IsValidTopicName(topicName) { return 0, nil, protocol.NewFatalClientErr(nil, "E_BAD_TOPIC", fmt.Sprintf("topic name %q is not valid", topicName)) } partition := -1 if len(params) == 3 { partition, err = strconv.Atoi(string(params[2])) if err != nil { return 0, nil, protocol.NewFatalClientErr(nil, "E_BAD_PARTITION", fmt.Sprintf("topic partition is not valid: %v", err)) } } if partition == -1 { partition = p.ctx.getDefaultPartition(topicName) } bodyLen, err := readLen(client.Reader, client.LenSlice) if err != nil { return 0, nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "failed to read body size") } if bodyLen <= 0 { return bodyLen, nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("invalid body size %d", bodyLen)) } if int64(bodyLen) > maxBody { return bodyLen, nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("body too big %d > %d", bodyLen, maxBody)) } topic, err := p.ctx.getExistingTopic(topicName, partition) if err != nil { nsqd.NsqLogger().Logf("not existing topic: %v-%v, err:%v", topicName, partition, err.Error()) return bodyLen, nil, protocol.NewFatalClientErr(nil, E_TOPIC_NOT_EXIST, "") } if err := p.CheckAuth(client, "PUB", topicName, ""); err != nil { return bodyLen, nil, err } // mpub return bodyLen, topic, nil }
func (p *protocolV2) FIN(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { state := atomic.LoadInt32(&client.State) if state != stateSubscribed && state != stateClosing { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot FIN in current state") } if len(params) < 2 { nsqd.NsqLogger().LogDebugf("FIN error params: %v", params) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "FIN insufficient number of params") } id, err := getFullMessageID(params[1]) if err != nil { nsqd.NsqLogger().LogDebugf("FIN error: %v, %v", params[1], err) return nil, protocol.NewFatalClientErr(nil, E_INVALID, err.Error()) } msgID := nsqd.GetMessageIDFromFullMsgID(*id) if int64(msgID) <= 0 { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "Invalid Message ID") } if client.Channel == nil { nsqd.NsqLogger().LogDebugf("FIN error no channel: %v", msgID) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "No channel") } if !p.ctx.checkForMasterWrite(client.Channel.GetTopicName(), client.Channel.GetTopicPart()) { nsqd.NsqLogger().Logf("topic %v fin message failed for not leader", client.Channel.GetTopicName()) return nil, protocol.NewFatalClientErr(nil, FailedOnNotLeader, "") } err = p.ctx.FinishMessage(client.Channel, client.ID, client.String(), msgID) if err != nil { client.IncrSubError(int64(1)) nsqd.NsqLogger().LogDebugf("FIN error : %v, err: %v, channel: %v, topic: %v", msgID, err, client.Channel.GetName(), client.Channel.GetTopicName()) if clusterErr, ok := err.(*consistence.CommonCoordErr); ok { if !clusterErr.IsLocalErr() { return nil, protocol.NewFatalClientErr(err, FailedOnNotWritable, "") } } return nil, protocol.NewClientErr(err, "E_FIN_FAILED", fmt.Sprintf("FIN %v failed %s", *id, err.Error())) } client.FinishedMessage() return nil, nil }
func (p *protocolV2) Exec(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { if bytes.Equal(params[0], []byte("IDENTIFY")) { return p.IDENTIFY(client, params) } err := enforceTLSPolicy(client, p, params[0]) if err != nil { return nil, err } switch { case bytes.Equal(params[0], []byte("FIN")): return p.FIN(client, params) case bytes.Equal(params[0], []byte("RDY")): return p.RDY(client, params) case bytes.Equal(params[0], []byte("REQ")): return p.REQ(client, params) case bytes.Equal(params[0], []byte("PUB")): return p.PUB(client, params) case bytes.Equal(params[0], []byte("PUB_TRACE")): return p.PUBTRACE(client, params) case bytes.Equal(params[0], []byte("MPUB")): return p.MPUB(client, params) case bytes.Equal(params[0], []byte("MPUB_TRACE")): return p.MPUBTRACE(client, params) case bytes.Equal(params[0], []byte("NOP")): return p.NOP(client, params) case bytes.Equal(params[0], []byte("TOUCH")): return p.TOUCH(client, params) case bytes.Equal(params[0], []byte("SUB")): return p.SUB(client, params) case bytes.Equal(params[0], []byte("SUB_ADVANCED")): return p.SUBADVANCED(client, params) case bytes.Equal(params[0], []byte("SUB_ORDERED")): return p.SUBORDERED(client, params) case bytes.Equal(params[0], []byte("CLS")): return p.CLS(client, params) case bytes.Equal(params[0], []byte("AUTH")): return p.AUTH(client, params) case bytes.Equal(params[0], []byte("INTERNAL_CREATE_TOPIC")): return p.internalCreateTopic(client, params) } return nil, protocol.NewFatalClientErr(nil, E_INVALID, fmt.Sprintf("invalid command %v", params[0])) }
func (p *LookupProtocolV1) REGISTER(client *ClientV1, reader *bufio.Reader, params []string) ([]byte, error) { if client.peerInfo == nil { return nil, protocol.NewFatalClientErr(nil, "E_INVALID", "client must IDENTIFY") } topic, channel, pid, err := getTopicChan("REGISTER", params) if err != nil { // check if old nsqd if client.peerInfo.IsOldPeer() { nsqlookupLog.Logf("client %v is old node trying register", client) topic, channel, err = getTopicChanFromOld("REGISTER", params) pid = strconv.Itoa(OLD_VERSION_PID) if err != nil { return nil, err } } else { return nil, err } } atomic.StoreInt64(&client.peerInfo.lastUpdate, time.Now().UnixNano()) if channel != "" { key := ChannelReg{ PartitionID: pid, PeerId: client.peerInfo.Id, Channel: channel, } if p.ctx.nsqlookupd.DB.AddChannelReg(topic, key) { nsqlookupLog.Logf("DB: client(%s) REGISTER new channel: topic:%s channel:%s pid:%s", client, topic, channel, pid) } } if p.ctx.nsqlookupd.DB.AddTopicProducer(topic, pid, &Producer{peerInfo: client.peerInfo}) { nsqlookupLog.Logf("DB: client(%s) REGISTER new topic:%s pid:%s", client, topic, pid) } return []byte("OK"), nil }
func (p *protocolV2) REQ(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { state := atomic.LoadInt32(&client.State) if state != stateSubscribed && state != stateClosing { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot REQ in current state") } if len(params) < 3 { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "REQ insufficient number of params") } id, err := getFullMessageID(params[1]) if err != nil { return nil, protocol.NewFatalClientErr(nil, E_INVALID, err.Error()) } timeoutMs, err := protocol.ByteToBase10(params[2]) if err != nil { return nil, protocol.NewFatalClientErr(err, E_INVALID, fmt.Sprintf("REQ could not parse timeout %s", params[2])) } timeoutDuration := time.Duration(timeoutMs) * time.Millisecond if timeoutDuration < 0 || timeoutDuration > p.ctx.getOpts().MaxReqTimeout { return nil, protocol.NewFatalClientErr(nil, E_INVALID, fmt.Sprintf("REQ timeout %v out of range 0-%v", timeoutDuration, p.ctx.getOpts().MaxReqTimeout)) } if client.Channel == nil { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "No channel") } err = client.Channel.RequeueMessage(client.ID, client.String(), nsqd.GetMessageIDFromFullMsgID(*id), timeoutDuration, true) if err != nil { client.IncrSubError(int64(1)) return nil, protocol.NewClientErr(err, "E_REQ_FAILED", fmt.Sprintf("REQ %v failed %s", *id, err.Error())) } client.RequeuedMessage(timeoutDuration > 0) return nil, nil }
func (p *protocolV2) internalSUB(client *nsqd.ClientV2, params [][]byte, enableTrace bool, ordered bool, startFrom *ConsumeOffset) ([]byte, error) { state := atomic.LoadInt32(&client.State) if state != stateInit { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot SUB in current state") } if client.HeartbeatInterval <= 0 { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot SUB with heartbeats disabled") } if len(params) < 3 { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "SUB insufficient number of parameters") } topicName := string(params[1]) if !protocol.IsValidTopicName(topicName) { return nil, protocol.NewFatalClientErr(nil, "E_BAD_TOPIC", fmt.Sprintf("SUB topic name %q is not valid", topicName)) } partition := -1 channelName := "" var err error channelName = string(params[2]) if !protocol.IsValidChannelName(channelName) { return nil, protocol.NewFatalClientErr(nil, "E_BAD_CHANNEL", fmt.Sprintf("SUB channel name %q is not valid", channelName)) } if len(params) == 4 { partition, err = strconv.Atoi(string(params[3])) if err != nil { return nil, protocol.NewFatalClientErr(nil, "E_BAD_PARTITION", fmt.Sprintf("topic partition is not valid: %v", err)) } } if err = p.CheckAuth(client, "SUB", topicName, channelName); err != nil { return nil, err } if partition == -1 { partition = p.ctx.getDefaultPartition(topicName) } topic, err := p.ctx.getExistingTopic(topicName, partition) if err != nil { nsqd.NsqLogger().Logf("sub to not existing topic: %v, err:%v", topicName, err.Error()) return nil, protocol.NewFatalClientErr(nil, E_TOPIC_NOT_EXIST, "") } if !p.ctx.checkForMasterWrite(topicName, partition) { nsqd.NsqLogger().Logf("sub failed on not leader: %v-%v, remote is : %v", topicName, partition, client.RemoteAddr()) // we need disable topic here to trigger a notify, maybe we failed to notify lookup last time. topic.DisableForSlave() return nil, protocol.NewFatalClientErr(nil, FailedOnNotLeader, "") } channel := topic.GetChannel(channelName) err = channel.AddClient(client.ID, client) if err != nil { nsqd.NsqLogger().Logf("sub failed to add client: %v, %v", client, err) return nil, protocol.NewFatalClientErr(nil, FailedOnNotWritable, "") } atomic.StoreInt32(&client.State, stateSubscribed) client.Channel = channel if enableTrace { nsqd.NsqLogger().Logf("sub channel %v with trace enabled, remote is : %v", channelName, client.RemoteAddr()) } if ordered { if atomic.LoadInt32(&client.SampleRate) != 0 { nsqd.NsqLogger().Errorf("%v", ErrOrderChannelOnSampleRate) return nil, protocol.NewFatalClientErr(nil, E_INVALID, ErrOrderChannelOnSampleRate.Error()) } channel.SetOrdered(true) } if startFrom != nil { cnt := channel.GetClientsCount() if cnt > 1 { nsqd.NsqLogger().LogDebugf("the consume offset: %v can only be set by the first client: %v", startFrom, cnt) } else { queueOffset, cnt, err := p.ctx.SetChannelOffset(channel, startFrom, false) if err != nil { return nil, protocol.NewFatalClientErr(nil, E_INVALID, err.Error()) } nsqd.NsqLogger().Logf("set the channel offset: %v (actual set : %v:%v), by client:%v, %v", startFrom, queueOffset, cnt, client.String(), client.UserAgent) } } client.EnableTrace = enableTrace // update message pump client.SubEventChan <- channel return okBytes, nil }
func (p *LookupProtocolV1) IDENTIFY(client *ClientV1, reader *bufio.Reader, params []string) ([]byte, error) { var err error if client.peerInfo != nil { return nil, protocol.NewFatalClientErr(err, "E_INVALID", "cannot IDENTIFY again") } var bodyLen int32 err = binary.Read(reader, binary.BigEndian, &bodyLen) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "IDENTIFY failed to read body size") } body := make([]byte, bodyLen) _, err = io.ReadFull(reader, body) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "IDENTIFY failed to read body") } // body is a json structure with producer information // Id should be identified by the client. peerInfo := PeerInfo{Id: ""} err = json.Unmarshal(body, &peerInfo) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "IDENTIFY failed to decode JSON body") } peerInfo.RemoteAddress = client.RemoteAddr().String() peerInfo.Id = peerInfo.RemoteAddress // require all fields if peerInfo.Id == "" || peerInfo.BroadcastAddress == "" || peerInfo.TCPPort == 0 || peerInfo.HTTPPort == 0 || peerInfo.Version == "" { nsqlookupLog.Logf("identify info missing: %v", peerInfo) return nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", "IDENTIFY missing fields") } if p.ctx.nsqlookupd.RealTCPAddr() == nil { nsqlookupLog.Logf("client(%s) register before the server is ready", client) return nil, protocol.NewFatalClientErr(nil, "E_NOT_READY", "The server is not ready for use") } atomic.StoreInt64(&peerInfo.lastUpdate, time.Now().UnixNano()) nsqlookupLog.Logf("CLIENT(%s): IDENTIFY peer: %v ", client, peerInfo) client.peerInfo = &peerInfo if p.ctx.nsqlookupd.DB.addPeerClient(peerInfo.Id, client.peerInfo) { nsqlookupLog.Logf("DB: client(%s) REGISTER new peer", client) } // build a response data := make(map[string]interface{}) data["tcp_port"] = p.ctx.nsqlookupd.RealTCPAddr().Port _, httpPortStr, _ := net.SplitHostPort(p.ctx.nsqlookupd.opts.HTTPAddress) httpPort, _ := strconv.Atoi(httpPortStr) if p.ctx.nsqlookupd.RealHTTPAddr() != nil { httpPort = p.ctx.nsqlookupd.RealHTTPAddr().Port } data["http_port"] = httpPort data["version"] = version.Binary hostname, err := os.Hostname() if err != nil { nsqlookupLog.LogErrorf("ERROR: unable to get hostname %s", err) } data["broadcast_address"] = p.ctx.nsqlookupd.opts.BroadcastAddress data["hostname"] = hostname response, err := json.Marshal(data) if err != nil { nsqlookupLog.LogErrorf(" marshaling %v", data) return []byte("OK"), nil } return response, nil }
func (p *protocolV2) AUTH(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { state := atomic.LoadInt32(&client.State) if state != stateInit { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot AUTH in current state") } if len(params) != 1 { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "AUTH invalid number of parameters") } bodyLen, err := readLen(client.Reader, client.LenSlice) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "AUTH failed to read body size") } if int64(bodyLen) > p.ctx.getOpts().MaxBodySize { return nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("AUTH body too big %d > %d", bodyLen, p.ctx.getOpts().MaxBodySize)) } if bodyLen <= 0 { return nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("AUTH invalid body size %d", bodyLen)) } body := make([]byte, bodyLen) _, err = io.ReadFull(client.Reader, body) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "AUTH failed to read body") } if client.HasAuthorizations() { return nil, protocol.NewFatalClientErr(nil, E_INVALID, "AUTH Already set") } if !p.ctx.isAuthEnabled() { return nil, protocol.NewFatalClientErr(err, "E_AUTH_DISABLED", "AUTH Disabled") } if err = client.Auth(string(body)); err != nil { // we don't want to leak errors contacting the auth server to untrusted clients nsqd.NsqLogger().Logf("PROTOCOL(V2): [%s] Auth Failed %s", client, err) return nil, protocol.NewFatalClientErr(err, "E_AUTH_FAILED", "AUTH failed") } if !client.HasAuthorizations() { return nil, protocol.NewFatalClientErr(nil, "E_UNAUTHORIZED", "AUTH No authorizations found") } var resp []byte resp, err = json.Marshal(struct { Identity string `json:"identity"` IdentityURL string `json:"identity_url"` PermissionCount int `json:"permission_count"` }{ Identity: client.AuthState.Identity, IdentityURL: client.AuthState.IdentityURL, PermissionCount: len(client.AuthState.Authorizations), }) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_AUTH_ERROR", "AUTH error "+err.Error()) } err = Send(client, frameTypeResponse, resp) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_AUTH_ERROR", "AUTH error "+err.Error()) } return nil, nil }
func (p *protocolV2) IDENTIFY(client *nsqd.ClientV2, params [][]byte) ([]byte, error) { var err error state := atomic.LoadInt32(&client.State) if state != stateInit { nsqd.NsqLogger().LogWarningf("[%s] command in wrong state: %v", client, state) return nil, protocol.NewFatalClientErr(nil, E_INVALID, "cannot IDENTIFY in current state") } bodyLen, err := readLen(client.Reader, client.LenSlice) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "IDENTIFY failed to read body size") } if int64(bodyLen) > p.ctx.getOpts().MaxBodySize { return nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("IDENTIFY body too big %d > %d", bodyLen, p.ctx.getOpts().MaxBodySize)) } if bodyLen <= 0 { return nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("IDENTIFY invalid body size %d", bodyLen)) } body := make([]byte, bodyLen) _, err = io.ReadFull(client.Reader, body) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "IDENTIFY failed to read body") } // body is a json structure with producer information var identifyData nsqd.IdentifyDataV2 err = json.Unmarshal(body, &identifyData) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "IDENTIFY failed to decode JSON body") } nsqd.NsqLogger().LogDebugf("PROTOCOL(V2): [%s] %+v", client, identifyData) err = client.Identify(identifyData) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_BODY", "IDENTIFY "+err.Error()) } // bail out early if we're not negotiating features if !identifyData.FeatureNegotiation { return okBytes, nil } tlsv1 := p.ctx.GetTlsConfig() != nil && identifyData.TLSv1 deflate := p.ctx.getOpts().DeflateEnabled && identifyData.Deflate deflateLevel := 0 if deflate { if identifyData.DeflateLevel <= 0 { deflateLevel = 6 } deflateLevel = int(math.Min(float64(deflateLevel), float64(p.ctx.getOpts().MaxDeflateLevel))) } snappy := p.ctx.getOpts().SnappyEnabled && identifyData.Snappy if deflate && snappy { return nil, protocol.NewFatalClientErr(nil, "E_IDENTIFY_FAILED", "cannot enable both deflate and snappy compression") } resp, err := json.Marshal(struct { MaxRdyCount int64 `json:"max_rdy_count"` Version string `json:"version"` MaxMsgTimeout int64 `json:"max_msg_timeout"` MsgTimeout int64 `json:"msg_timeout"` TLSv1 bool `json:"tls_v1"` Deflate bool `json:"deflate"` DeflateLevel int `json:"deflate_level"` MaxDeflateLevel int `json:"max_deflate_level"` Snappy bool `json:"snappy"` SampleRate int32 `json:"sample_rate"` AuthRequired bool `json:"auth_required"` OutputBufferSize int `json:"output_buffer_size"` OutputBufferTimeout int64 `json:"output_buffer_timeout"` }{ MaxRdyCount: p.ctx.getOpts().MaxRdyCount, Version: version.Binary, MaxMsgTimeout: int64(p.ctx.getOpts().MaxMsgTimeout / time.Millisecond), MsgTimeout: int64(client.MsgTimeout / time.Millisecond), TLSv1: tlsv1, Deflate: deflate, DeflateLevel: deflateLevel, MaxDeflateLevel: p.ctx.getOpts().MaxDeflateLevel, Snappy: snappy, SampleRate: client.SampleRate, AuthRequired: p.ctx.isAuthEnabled(), OutputBufferSize: client.OutputBufferSize, OutputBufferTimeout: int64(client.OutputBufferTimeout / time.Millisecond), }) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } err = Send(client, frameTypeResponse, resp) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } if tlsv1 { nsqd.NsqLogger().Logf("PROTOCOL(V2): [%s] upgrading connection to TLS", client) err = client.UpgradeTLS() if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } err = Send(client, frameTypeResponse, okBytes) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } } if snappy { nsqd.NsqLogger().Logf("PROTOCOL(V2): [%s] upgrading connection to snappy", client) err = client.UpgradeSnappy() if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } err = Send(client, frameTypeResponse, okBytes) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } } if deflate { nsqd.NsqLogger().Logf("PROTOCOL(V2): [%s] upgrading connection to deflate", client) err = client.UpgradeDeflate(deflateLevel) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } err = Send(client, frameTypeResponse, okBytes) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_IDENTIFY_FAILED", "IDENTIFY failed "+err.Error()) } } return nil, nil }
func (p *protocolV2) internalPubAndTrace(client *nsqd.ClientV2, params [][]byte, traceEnable bool) ([]byte, error) { startPub := time.Now().UnixNano() bodyLen, topic, err := p.preparePub(client, params, p.ctx.getOpts().MaxMsgSize) if err != nil { return nil, err } if traceEnable && bodyLen <= nsqd.MsgTraceIDLength { return nil, protocol.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("invalid body size %d with trace id enabled", bodyLen)) } messageBodyBuffer := topic.BufferPoolGet(int(bodyLen)) defer topic.BufferPoolPut(messageBodyBuffer) asyncAction := shouldHandleAsync(client, params) _, err = io.CopyN(messageBodyBuffer, client.Reader, int64(bodyLen)) if err != nil { return nil, protocol.NewFatalClientErr(err, "E_BAD_MESSAGE", "failed to read message body") } messageBody := messageBodyBuffer.Bytes()[:bodyLen] topicName := topic.GetTopicName() partition := topic.GetTopicPart() var traceID uint64 var realBody []byte if traceEnable { traceID = binary.BigEndian.Uint64(messageBody[:nsqd.MsgTraceIDLength]) realBody = messageBody[nsqd.MsgTraceIDLength:] } else { realBody = messageBody } if p.ctx.checkForMasterWrite(topicName, partition) { id := nsqd.MessageID(0) offset := nsqd.BackendOffset(0) rawSize := int32(0) if asyncAction { err = internalPubAsync(client.PubTimeout, messageBodyBuffer, topic) } else { id, offset, rawSize, _, err = p.ctx.PutMessage(topic, realBody, traceID) } //p.ctx.setHealth(err) if err != nil { topic.GetDetailStats().UpdatePubClientStats(client.RemoteAddr().String(), client.UserAgent, "tcp", 1, true) nsqd.NsqLogger().LogErrorf("topic %v put message failed: %v", topic.GetFullName(), err) if clusterErr, ok := err.(*consistence.CommonCoordErr); ok { if !clusterErr.IsLocalErr() { return nil, protocol.NewClientErr(err, FailedOnNotWritable, "") } } return nil, protocol.NewClientErr(err, "E_PUB_FAILED", err.Error()) } topic.GetDetailStats().UpdatePubClientStats(client.RemoteAddr().String(), client.UserAgent, "tcp", 1, false) cost := time.Now().UnixNano() - startPub topic.GetDetailStats().UpdateTopicMsgStats(int64(len(realBody)), cost/1000) if !traceEnable { return okBytes, nil } return getTracedReponse(messageBodyBuffer, id, traceID, offset, rawSize) } else { topic.GetDetailStats().UpdatePubClientStats(client.RemoteAddr().String(), client.UserAgent, "tcp", 1, true) //forward to master of topic nsqd.NsqLogger().LogDebugf("should put to master: %v, from %v", topic.GetFullName(), client.RemoteAddr) topic.DisableForSlave() return nil, protocol.NewClientErr(err, FailedOnNotLeader, "") } }