Exemplo n.º 1
0
Arquivo: http.go Projeto: dulumao/goim
func (server *Server) serveHTTP(w http.ResponseWriter, r *http.Request, tr *itime.Timer) {
	var (
		b    *Bucket
		ok   bool
		hb   time.Duration // heartbeat
		key  string
		cb   string
		err  error
		trd  *itime.TimerData
		conn net.Conn
		rwr  *bufio.ReadWriter
		hj   http.Hijacker
		// no client send
		ch = NewChannel(1, define.NoRoom)
	)
	if key, cb, hb, err = server.authHTTP(r, ch); err != nil {
		http.Error(w, "auth failed", http.StatusForbidden)
		return
	}
	if hj, ok = w.(http.Hijacker); !ok {
		log.Error("w.(http.Hijacker) type assection failed")
		http.Error(w, "not support", http.StatusInternalServerError)
		return
	}
	if conn, rwr, err = hj.Hijack(); err != nil {
		log.Error("hj.Hijack() error(%v)", err)
		http.Error(w, "not support", http.StatusInternalServerError)
		return
	}
	// register key->channel
	b = server.Bucket(key)
	b.Put(key, ch, tr)
	// hanshake ok start dispatch goroutine
	server.dispatchHTTP(rwr, cb, ch, hb)
	tr.Del(trd)
	conn.Close()
	b.Del(key)
	// don't use close chan, Signal can be reused
	// if chan full, writer goroutine next fetch from chan will exit
	// if chan empty, send a 0(close) let the writer exit
	if err = server.operator.Disconnect(key, ch.RoomId); err != nil {
		log.Error("%s operator do disconnect error(%v)", key, err)
	}
	if Debug {
		log.Debug("%s serverconn goroutine exit", key)
	}
	return
}
Exemplo n.º 2
0
// push merge proto and push msgs in batch.
func (r *Room) push(timer *itime.Timer, batch int, sigTime time.Duration) {
	var (
		done  bool
		last  time.Time
		least time.Duration
		ch    *Channel
		td    *itime.TimerData
	)
	if Debug {
		log.Debug("start room: %d goroutine", r.id)
	}
	td = timer.Add(sigTime, func() {
		select {
		case r.signal <- ProtoReady:
		default:
		}
	})
	for {
		if r.n > 0 {
			if least = sigTime - time.Now().Sub(last); least > 0 {
				timer.Set(td, least)
			} else {
				// timeout
				done = true
			}
		} else {
			last = time.Now()
		}
		if !done {
			if <-r.signal != ProtoReady {
				break
			}
			// try merge msg hard
			if r.n < batch {
				continue
			}
		}
		r.rLock.RLock()
		for ch, _ = range r.chs {
			ch.Pushs(r.vers[:r.n], r.ops[:r.n], r.msgs[:r.n])
		}
		// r.n set here and Push, so RLock is exclusive with Lock
		r.n = 0
		r.rLock.RUnlock()
		done = false
		ch = nil // avoid gc memory leak
	}
	timer.Del(td)
	if Debug {
		log.Debug("room: %d goroutine exit", r.id)
	}
}
Exemplo n.º 3
0
Arquivo: tcp.go Projeto: dulumao/goim
// TODO linger close?
func (server *Server) serveTCP(conn *net.TCPConn, rp, wp *bytes.Pool, tr *itime.Timer) {
	var (
		b   *Bucket
		key string
		hb  time.Duration // heartbeat
		err error
		trd *itime.TimerData
		rb  = rp.Get()
		wb  = wp.Get()
		ch  = NewChannel(server.Options.Proto, define.NoRoom)
		rr  = &ch.Reader
		wr  = &ch.Writer
		p   = &ch.CliProto
	)
	ch.Reader.ResetBuffer(conn, rb.Bytes())
	ch.Writer.ResetBuffer(conn, wb.Bytes())
	// handshake
	trd = tr.Add(server.Options.HandshakeTimeout, func() {
		conn.Close()
	})
	if key, ch.RoomId, hb, err = server.authTCP(rr, wr, p); err != nil {
		conn.Close()
		rp.Put(rb)
		wp.Put(wb)
		tr.Del(trd)
		log.Error("key: %s handshake failed error(%v)", key, err)
		return
	}
	trd.Key = key
	tr.Set(trd, hb)
	b = server.Bucket(key)
	b.Put(key, ch, tr)
	// hanshake ok start dispatch goroutine
	go server.dispatchTCP(key, conn, wr, wp, wb, ch)
	for {
		if err = server.readTCPRequest(rr, p); err != nil {
			break
		}
		if p.Operation == define.OP_HEARTBEAT {
			tr.Set(trd, hb)
			p.Body = nil
			p.Operation = define.OP_HEARTBEAT_REPLY
			if Debug {
				log.Debug("key: %s receive heartbeat", key)
			}
		} else {
			if err = server.operator.Operate(p); err != nil {
				break
			}
		}
		if err = ch.Reply(); err != nil {
			break
		}
	}
	log.Error("key: %s server tcp failed error(%v)", key, err)
	conn.Close()
	ch.Close()
	rp.Put(rb)
	b.Del(key)
	tr.Del(trd)
	if err = server.operator.Disconnect(key, ch.RoomId); err != nil {
		log.Error("key: %s operator do disconnect error(%v)", key, err)
	}
	if Debug {
		log.Debug("key: %s server tcp goroutine exit", key)
	}
	return
}
Exemplo n.º 4
0
func (server *Server) serveWebsocket(conn *websocket.Conn, tr *itime.Timer) {
	var (
		b   *Bucket
		hb  time.Duration // heartbeat
		key string
		err error
		trd *itime.TimerData
		ch  = NewChannel(server.Options.Proto, define.NoRoom)
		p   = &ch.CliProto
	)
	// handshake
	trd = tr.Add(server.Options.HandshakeTimeout, func() {
		conn.Close()
	})
	if key, ch.RoomId, hb, err = server.authWebsocket(conn, p); err == nil {
		trd.Key = key
		tr.Set(trd, hb)
	}
	if err != nil {
		tr.Del(trd)
		conn.Close()
		log.Error("handshake failed error(%v)", err)
		return
	}
	// register key->channel
	b = server.Bucket(key)
	b.Put(key, ch, tr)
	// hanshake ok start dispatch goroutine
	go server.dispatchWebsocket(key, conn, ch)
	for {
		// parse request protocol
		if err = server.readWebsocketRequest(conn, p); err != nil {
			break
		}
		if p.Operation == define.OP_HEARTBEAT {
			// heartbeat
			tr.Set(trd, hb)
			p.Body = nil
			p.Operation = define.OP_HEARTBEAT_REPLY
		} else {
			// process message
			if err = server.operator.Operate(p); err != nil {
				break
			}
		}
		if err = ch.Reply(); err != nil {
			break
		}
	}
	log.Error("key: %s server websocket failed error(%v)", key, err)
	conn.Close()
	ch.Close()
	b.Del(key)
	if err = server.operator.Disconnect(key, ch.RoomId); err != nil {
		log.Error("key: %s operator do disconnect error(%v)", key, err)
	}
	if Debug {
		log.Debug("key: %s server websocket goroutine exit", key)
	}
	return
}