// Perform a B2 API request with the provided request and response objects
func (c *B2) apiRequest(apiPath string, request interface{}, response interface{}) error {
	body, err := ffjson.Marshal(request)
	if err != nil {
		return err
	}
	defer ffjson.Pool(body)

	if c.Debug {
		log.Println("----")
		log.Printf("apiRequest: %s %s", apiPath, body)
	}

	err = c.tryAPIRequest(apiPath, body, response)

	// Retry after non-fatal errors
	if b2err, ok := err.(*B2Error); ok {
		if !b2err.IsFatal() && !c.NoRetry {
			if c.Debug {
				log.Printf("Retrying request %q due to error: %v", apiPath, err)
			}

			return c.tryAPIRequest(apiPath, body, response)
		}
	}
	return err
}
Exemple #2
0
func StartMQTTSender(MQTTServerAddress string, MQTTSendChan chan *lib.PostMessage, channel_name string) chan bool {
	close_chan := make(chan bool)
	go func() {
		defer func() {
			if err := recover(); err != nil {
				utils.Log.Println(err)
				debug.PrintStack()
			}
		}()

		var post_message *lib.PostMessage
		var cli *client.Client
		var err error

		cli, err = ConnectToMQTTServer(MQTTServerAddress)
		if err != nil {
			utils.Log.Println("Connect to MQTT Server failed:", err)
			return
		}

		for {
			select {
			case <-close_chan:
				close_chan <- true
				utils.Log.Printf("Quit MQTT Sender worker.")
				return

			case post_message = <-MQTTSendChan:
				message_json_buffer, err := ffjson.Marshal(post_message)
				if err != nil {
					utils.Log.Println()
				}

				err = cli.Publish(&client.PublishOptions{
					QoS:       mqtt.QoS0,
					TopicName: []byte(channel_name),
					Message:   []byte(message_json_buffer),
				})

				if err != nil {
					utils.Log.Println("Publish to MQTT Failed:", err)
					utils.Log.Println("Try to connect MQTT Server.")
					cli, err = ConnectToMQTTServer(MQTTServerAddress)
					if err != nil {
						utils.Log.Println("Connect to MQTT Server failed:", err)
					}
				}

				utils.Log.Printf("Send message to MQTT Server: %s, Length: %d\n", MQTTServerAddress, len(message_json_buffer))

				ffjson.Pool(message_json_buffer)
			}
		}
	}()

	return close_chan
}
Exemple #3
0
func BenchmarkFF_Marshal_L_Pool_Parallel(b *testing.B) {
	var l int64
	for i := 0; i < b.N; i++ {
		data, err := ffjson.MarshalFast(&xlStructData)
		if err != nil {
			b.Error(err)
		}
		l = int64(len(data))
		ffjson.Pool(data)
	}
	b.SetBytes(l)
}
Exemple #4
0
// Enqueue adds job to the queue.
func (c *Client) Enqueue(queue string, data ...interface{}) error {
	buf, err := ffjson.Marshal(backJob{Class: queue, Args: data})
	if err != nil {
		return err
	}
	rc := c.pool.Get()
	defer rc.Close()
	defer ffjson.Pool(buf)

	_, err = rc.Do("RPUSH", fmt.Sprintf("%vqueue:%v", c.prefix, queue), buf)
	return err

}
Exemple #5
0
func BenchmarkFF_Marshal_S_Pool_Parallel(b *testing.B) {
	var l int64
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			data, err := ffjson.MarshalFast(&smallStructData)
			if err != nil {
				b.Error(err)
			}
			l = int64(len(data))
			ffjson.Pool(data)
		}
	})
	b.SetBytes(l)
}
Exemple #6
0
// send a million documents
func ffmillion(w http.ResponseWriter, r *http.Request) {

	for i := 0; i < 10000000; i++ {
		docId := i % docs.number
		item := docs.docMap[docs.docList[docId]]
		bytes, err := ffjson.Marshal(item)
		if err != nil {
			fmt.Fprintf(w, "Failed to marshal document %v", err)
			return
		}
		fmt.Fprintf(w, string(bytes)) // send data to client side
		fmt.Fprintf(w, "\n\n")
		ffjson.Pool(bytes)
	}
}
Exemple #7
0
func BenchmarkMarshalJSONNativePool(b *testing.B) {
	record := newLogFFRecord()

	buf, err := json.Marshal(&record)
	if err != nil {
		b.Fatalf("Marshal: %v", err)
	}
	b.SetBytes(int64(len(buf)))

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		bytes, err := ffjson.MarshalFast(record)
		if err != nil {
			b.Fatalf("Marshal: %v", err)
		}
		ffjson.Pool(bytes)
	}
}
Exemple #8
0
func OnlineUsersSimpleHandler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "channel, tourid, tag")

	var online_users_simple lib.OnlineUsersSimple
	var channel_name string

	all_channel.RLock.RLock()
	defer all_channel.RLock.RUnlock()

	channel_name = req.Header.Get("channel")
	channel_name = strings.Trim(channel_name, " ")
	if channel_name == "" {
		utils.Log.Printf("[%s] channel name not in header\n", req.RemoteAddr)
		http.Error(w, "channel name not in header", 400)
		return
	}

	channel := GetChannel(channel_name)

	online_users_simple.Length = atomic.LoadUint64(&channel.RealUserCount)
	online_users_simple.Result = 0

	buf, err := ffjson.Marshal(online_users_simple)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(buf)

	ffjson.Pool(buf)
}
Exemple #9
0
func SysStatusHandler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	var key string
	var channel *Channel
	var channel_status lib.ChannelStatus
	var channel_status_reply lib.ChannelStatusReply

	all_channel.RLock.RLock()
	defer all_channel.RLock.RUnlock()

	for key = range all_channel.Channels {
		channel = all_channel.Channels[key]
		channel_status.Name = channel.Name
		channel_status.UserCount = atomic.LoadUint64(&channel.UserCount)
		channel_status.RealUserCount = atomic.LoadUint64(&channel.RealUserCount)
		channel_status_reply.Data = append(channel_status_reply.Data, channel_status)
	}

	if len(channel_status_reply.Data) == 0 {
		channel_status_reply.Result = 0
		channel_status_reply.Data = []lib.ChannelStatus{}
	}

	buf, err := ffjson.Marshal(channel_status_reply)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s]\n", req.RemoteAddr, err)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(buf)

	ffjson.Pool(buf)
}
Exemple #10
0
func OnlineUsersHandlerWithTag(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "channel, tourid, tag")

	var channel_name string
	var user_state_tag_map *lib.OnlineUsersWithTag
	var ok bool

	channel_name = req.Header.Get("channel")
	if channel_name == "" {
		utils.Log.Printf("[%s] channel name not in header\n", req.RemoteAddr)
		http.Error(w, "channel name not in header", 400)
		return
	}

	channel := GetChannel(channel_name)

	channel.OnlineUsersLock.RLock()

	general_online_users := channel.GeneralOnlineUsersPool.Get().(*lib.GeneralOnlineUsers)
	temp_tag_map := general_online_users.UserTags

	for username := range channel.OnlineUsers {
		state := channel.OnlineUsers[username]

		if ServerDebug {
			utils.Log.Printf("/api/users/tags: got state: %v\n", state)
		}

		if user_state_tag_map, ok = temp_tag_map[state.Tag]; !ok {
			user_state_tag_map = channel.OnlineUsersTagPool.Get().(*lib.OnlineUsersWithTag)
			temp_tag_map[state.Tag] = user_state_tag_map
		}

		temp_tag_map[state.Tag].Length += 1
		temp_tag_map[state.Tag].UserList = append(temp_tag_map[state.Tag].UserList, username)
	}
	channel.OnlineUsersLock.RUnlock()

	general_online_users.Result = 0
	general_online_users.UserTags = temp_tag_map
	general_online_users.Timestamp = time.Now().Unix()

	buf, err := ffjson.Marshal(general_online_users)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(buf)

	ffjson.Pool(buf)

	for key := range general_online_users.UserTags {
		general_online_users.UserTags[key].Length = 0
		general_online_users.UserTags[key].UserList = make([]string, 0)
		channel.OnlineUsersTagPool.Put(general_online_users.UserTags[key])
		// delete is safe
		// because we put general_online_users into pool in the below
		delete(general_online_users.UserTags, key)
	}

	channel.GeneralOnlineUsersPool.Put(general_online_users)
}
Exemple #11
0
func OnlineUsersHandler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	var online_users lib.OnlineUsers
	var channel_name string
	var user *User
	var key string
	var now int64

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "channel, tourid, tag")

	channel_name = req.Header.Get("channel")
	channel_name = strings.Trim(channel_name, " ")
	if channel_name == "" {
		utils.Log.Printf("[%s] channel name not in header\n", req.RemoteAddr)
		http.Error(w, "channel name not in header", 400)
		return
	}

	channel := GetChannel(channel_name)

	if USE_FASE_ONLINE_MAP == false {
		channel.ChannelRLock.RLock()

		channel_user_length := len(channel.Users)
		if channel_user_length > 0 {

			for key = range channel.Users {
				now = time.Now().Unix()
				user = channel.Users[key]
				if now-user.LastUpdate < DELAY_USER_ONLINE {
					online_users.UserList = append(online_users.UserList, key)
				}
			}

			if len(online_users.UserList) == 0 {
				online_users.UserList = []string{}
			}

		} else {
			online_users.UserList = []string{}
		}

		channel.ChannelRLock.RUnlock()

	} else {
		channel.OnlineUsersLock.RLock()
		for key = range channel.OnlineUsers {
			online_users.UserList = append(online_users.UserList, key)
		}
		channel.OnlineUsersLock.RUnlock()
	}

	online_users.Result = 0
	online_users.Length = len(online_users.UserList)

	if online_users.Length == 0 {
		online_users.UserList = []string{}
	}

	buf, err := ffjson.Marshal(online_users)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(buf)

	ffjson.Pool(buf)
}
Exemple #12
0
func OnlineUsersSimpleHandler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "channel, tourid, tag")

	var online_users_simple lib.OnlineUsersSimple
	var channel_name string
	var mqtt_users lib.MQTTSingleTopicUsers

	all_channel.RLock.RLock()
	defer all_channel.RLock.RUnlock()

	channel_name = req.Header.Get("channel")
	channel_name = strings.Trim(channel_name, " ")
	if channel_name == "" {
		utils.Log.Printf("[%s] channel name not in header\n", req.RemoteAddr)
		http.Error(w, "channel name not in header", 400)
		return
	}

	if Config.MQTTServerEnable == true {
		resp, err := http.Get("http://" + Config.MQTTServerAddress + ":18088/api/simple_topic/" + channel_name)
		if resp != nil {
			defer resp.Body.Close()
		}

		if err != nil {
			utils.Log.Printf("[%s] Get MQTT online users failed: %s, channel: [%s]\n", req.RemoteAddr, err, channel_name)
			http.Error(w, "failed to get MQTT users", 500)
			return
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			utils.Log.Printf("[%s] Read MQTT response failed: %s, channel: [%s]\n", req.RemoteAddr, err, channel_name)
			http.Error(w, "read MQTT users failed", 500)
			return
		}

		err = json.Unmarshal(body, &mqtt_users)
		if err != nil {
			utils.Log.Printf("[%s] Unmarshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
			http.Error(w, "Unmarshal json failed", 500)
			return
		}

		online_users_simple.Length = mqtt_users.Length
	}

	channel := GetChannel(channel_name)

	online_users_simple.Length += atomic.LoadUint64(&channel.RealUserCount)
	online_users_simple.Result = 0

	buf, err := ffjson.Marshal(online_users_simple)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(buf)

	ffjson.Pool(buf)
}
Exemple #13
0
// 处理POST消息
func MessagePostHandler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	var channel_name string
	var channel *Channel
	var err error
	var buf []byte

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "channel, tourid, tag")
	w.Header().Set("Access-Control-Allow-Credentials", "true")

	channel_name = req.Header.Get("channel")
	if channel_name == "" {
		utils.Log.Printf("[%s] channel name not in header\n", req.RemoteAddr)
		http.Error(w, "channel name not in header", 400)
		return
	}

	// use byte pool
	buffer := byte_pool.Checkout()
	defer buffer.Close()
	buffer.ReadFrom(req.Body)
	body := buffer.Bytes()

	channel = GetChannel(channel_name)

	//post_message := channel.PostMessagePool.Get().(*lib.PostMessage)
	post_message := new(lib.PostMessage)

	err = ffjson.Unmarshal(body, post_message)
	if err != nil {
		utils.Log.Printf("[%s] Unmarshal json failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Unmarshal json failed", 500)
		return
	}

	if channel.PrepareClose == true {
		utils.Log.Printf("[%s] Channel: [%s] will be closed.\n", req.RemoteAddr, channel_name)
		http.Error(w, "channel will be closed or not exists", 500)
		return
	}

	message_id := utils.MakeRandomID()
	post_message.MessageID = message_id

	send_finished := false
	if post_message.Delay == 0 {
		send_finished = PostMessage(channel, post_message)
	} else {
		go PostMessage(channel, post_message)
		send_finished = true
	}

	post_reply := channel.PostReplyPool.Get().(*lib.PostReply)
	if send_finished {
		post_reply.Result = 0
		post_reply.MessageID = message_id
	} else {
		post_reply.Result = 1
		post_reply.MessageID = "message buffer of channel is full."
	}

	buf, err = ffjson.Marshal(*post_reply)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	if ServerDebug {
		utils.Log.Printf("Got message from [%s], message: [%s], message_id: [%s], channel: [%s]\n", req.RemoteAddr, string(body), message_id, channel_name)
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(buf)

	channel.PostReplyPool.Put(post_reply)
	ffjson.Pool(buf)
}
Exemple #14
0
// 处理Poll消息
func MessagePollHandler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	var channel_name string
	var user_id string
	var err error

	var message_list []*lib.PostMessage

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "channel, tourid, tag, token, versionCode, appId")

	channel_name = req.Header.Get("channel")
	if channel_name == "" {
		utils.Log.Printf("[%s] channel name not in header\n", req.RemoteAddr)
		http.Error(w, "channel name not in header", 400)
		return
	}

	user_id = req.Header.Get("tourid")
	user_id = strings.Trim(user_id, " ")
	if user_id == "" {
		utils.Log.Printf("[%s] user_id not in header\n", req.RemoteAddr)
		http.Error(w, "user_id name not in header", 400)
		return
	}

	// support for multipoint login
	//user_id = user_id + ":" + utils.MakeRandomID()

	user_tag := req.Header.Get("tag")
	user_tag = strings.Trim(user_tag, " ")
	if user_tag == "" {
		user_tag = "UNKNOW"
		utils.Log.Printf("[%s] user_tag not in header\n", req.RemoteAddr)
	}

	if ServerDebug {
		utils.Log.Printf("Client [%s]: User [%s] with tag [%s]\n", req.RemoteAddr, user_id, user_tag)
	}

	wait := req.Header.Get("wait")
	wait = strings.Trim(wait, " ")

	channel := GetChannel(channel_name)

	if channel.PrepareClose == true {
		utils.Log.Printf("[%s] Channel: [%s] will be closed.\n", req.RemoteAddr, channel_name)
		http.Error(w, "channel will be closed", 500)
		return
	}

	user, err := channel.GetUser(user_id)
	if err != nil {
		user, err = channel.AddUser(user_id, user_tag)
		if err != nil {
			utils.Log.Printf("[%s] AddUser failed: [%s]\n", req.RemoteAddr, err)
		}
	}

	// if no new message, we wait for it
	if wait == "yes" {
		if user.NotifyChan != nil {
			select {
			case <-wheel_seconds.After(time.Duration(POLL_WAIT_TIME) * time.Second):
			case <-user.NotifyChan:
			}
		}
	}

	message_list_size := 0
	user.SpinLock.Lock()

	if user.MessageBuffer != nil {
		for i := 0; i < Config.PollMessageSize; i++ {
			if user.MessageBuffer.Len() == 0 {
				break
			}
			e := user.MessageBuffer.Front()
			if e != nil {
				if post_message, ok := user.MessageBuffer.Remove(e).(*lib.PostMessage); ok {
					if atomic.LoadUint64(&post_message.Count) > 0 {
						// got message, decrement message reference count
						message_list = append(message_list, post_message)
						message_list_size += 1
						atomic.AddUint64(&post_message.Count, ^uint64(0))

						if atomic.LoadUint64(&post_message.Count) == 0 {
							// delete message in channel's global message cache
							post_message.Lock.Lock()
							delete(channel.MessageCache, post_message.MessageID)
							post_message.Lock.Unlock()
						}
					}
				}
			}
		}
	}

	// update user's tag when tag was changed
	if CHANGE_USER_STATE_IN_REAL_TIME && user_tag != "" {
		if user_tag != user.Tag || user.Tag == "" {
			if ServerDebug {
				utils.Log.Printf("Change user [%s] tag %s->%s\n", user_id, user.Tag, user_tag)
			}
			state := channel.UserStatePool.Get().(*UserState)
			state.ID = user_id
			state.Tag = user_tag
			state.State = true
			state.From = 1
			channel.UserStateChan <- state
		}

		user.Tag = user_tag
	}

	user.LastUpdate = time.Now().Unix()
	user.SpinLock.Unlock()

	poll_message := channel.PollMessagePool.Get().(*lib.PollMessage)
	poll_message.Result = 0
	poll_message.MessageLength = len(message_list)
	if len(message_list) == 0 {
		poll_message.MessageList = []*lib.PostMessage{}
	} else {
		poll_message.MessageList = message_list
	}

	buf, err := ffjson.Marshal(*poll_message)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	if ServerDebug == true {
		utils.Log.Printf("Send message to [%s], message: [%s], channel: [%s], user_id: [%s]\n", req.RemoteAddr, string(buf), channel_name, user_id)
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(buf)

	/*
		for idx := range message_list {
			channel.PostMessagePool.Put(message_list[idx])
		}
	*/

	channel.PollMessagePool.Put(poll_message)
	ffjson.Pool(buf)
}
Exemple #15
0
func MessageDeleteHandler(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			utils.Log.Println(err)
			debug.PrintStack()
		}
	}()

	var channel_name string
	var user_id string
	var delete_message_reply lib.DeleteMessageReply

	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
	w.Header().Set("Access-Control-Allow-Headers", "channel, tourid, tag, token, versionCode, appId")

	channel_name = req.Header.Get("channel")
	if channel_name == "" {
		utils.Log.Printf("[%s] channel name not in header\n", req.RemoteAddr)
		http.Error(w, "channel name not in header", 400)
		return
	}

	user_id = req.Header.Get("tourid")
	if user_id == "" {
		utils.Log.Printf("[%s] user_id not in header\n", req.RemoteAddr)
		http.Error(w, "user_id name not in header", 400)
		return
	}

	user_tag := req.Header.Get("tag")
	user_tag = strings.Trim(user_tag, " ")
	if user_tag == "" {
		user_tag = "UNKNOW"
		utils.Log.Printf("[%s] user: [%s] user_tag not in header\n", req.RemoteAddr, user_id)
	}

	if ServerDebug {
		utils.Log.Printf("Client [%s]: User [%s] with tag [%s]\n", req.RemoteAddr, user_id, user_tag)
	}

	channel := GetChannel(channel_name)

	user, err := channel.GetUser(user_id)
	if err != nil {
		user, err = channel.AddUser(user_id, user_tag)
		if err != nil {
			utils.Log.Printf("[%s] AddUser failed: [%s]\n", req.RemoteAddr, err)
		}
	}

	user.SpinLock.Lock()
	if user.MessageBuffer != nil {
		decr_message_ref(channel, user.MessageBuffer)
		user.MessageBuffer = user.MessageBuffer.Init()
	}

	// update user's tag when tag was changed
	if CHANGE_USER_STATE_IN_REAL_TIME && user_tag != "" {
		if user_tag != user.Tag || user.Tag == "" {
			if ServerDebug {
				utils.Log.Printf("Change user [%s] tag %s->%s\n", user_id, user.Tag, user_tag)
			}
			state := channel.UserStatePool.Get().(*UserState)
			state.ID = user_id
			state.Tag = user_tag
			state.State = true
			state.From = 1
			channel.UserStateChan <- state
		}

		user.Tag = user_tag
	}

	user.SpinLock.Unlock()

	delete_message_reply.Result = 0

	buf, err := ffjson.Marshal(delete_message_reply)
	if err != nil {
		utils.Log.Printf("[%s] Marshal JSON failed: [%s], channel: [%s]\n", req.RemoteAddr, err, channel_name)
		http.Error(w, "Marshal json failed", 500)
		return
	}

	message_buffer_len := user.MessageBuffer.Len()
	if ServerDebug == true {
		utils.Log.Printf("Delete message for [%s], channel: [%s], user_id: [%s], length: [%d]\n", req.RemoteAddr, channel_name, user_id, message_buffer_len)
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(buf)

	ffjson.Pool(buf)
}