Example #1
0
func ListenAndServe(l ListenArgs, s ServerConst, o orcas.OrcaConst, h1, h2 handlers.HandlerConst) {
	var listener net.Listener
	var err error

	switch l.Type {
	case ListenTCP:
		listener, err = net.Listen("tcp", fmt.Sprintf(":%d", l.Port))
		if err != nil {
			log.Panicf("Error binding to port %d: %v\n", l.Port, err.Error())
		}

	case ListenUnix:
		err = os.Remove(l.Path)
		if err != nil && !os.IsNotExist(err) {
			log.Panicf("Error removing previous unix socket file at %s\n", l.Path)
		}
		listener, err = net.Listen("unix", l.Path)
		if err != nil {
			log.Panicf("Error binding to unix socket at %s: %v\n", l.Path, err.Error())
		}

	default:
		log.Panicf("Unsupported server listen type: %v", l.Type)
	}

	for {
		remote, err := listener.Accept()
		if err != nil {
			log.Println("Error accepting connection from remote:", err.Error())
			remote.Close()
			continue
		}
		metrics.IncCounter(MetricConnectionsEstablishedExt)

		if l.Type == ListenTCP {
			tcpRemote := remote.(*net.TCPConn)
			tcpRemote.SetKeepAlive(true)
			tcpRemote.SetKeepAlivePeriod(30 * time.Second)
		}

		// construct L1 handler using given constructor
		l1, err := h1()
		if err != nil {
			log.Println("Error opening connection to L1:", err.Error())
			remote.Close()
			continue
		}
		metrics.IncCounter(MetricConnectionsEstablishedL1)

		// construct l2
		l2, err := h2()
		if err != nil {
			log.Println("Error opening connection to L2:", err.Error())
			l1.Close()
			remote.Close()
			continue
		}
		metrics.IncCounter(MetricConnectionsEstablishedL2)

		// spin off a goroutine here to handle determining the protocol used for the connection.
		// The server loop can't be started until the protocol is known. Another goroutine is
		// necessary here because we don't want to block accepting new connections if the current
		// new connection doesn't send data immediately.
		go func(remoteConn net.Conn) {
			remoteReader := bufio.NewReader(remoteConn)
			remoteWriter := bufio.NewWriter(remoteConn)

			var reqParser common.RequestParser
			var responder common.Responder

			// A connection is either binary protocol or text. It cannot switch between the two.
			// This is the way memcached handles protocols, so it can be as strict here.
			binary, err := isBinaryRequest(remoteReader)
			if err != nil {
				// must be an IO error. Abort!
				abort([]io.Closer{remoteConn, l1, l2}, err)
				return
			}

			if binary {
				reqParser = binprot.NewBinaryParser(remoteReader)
				responder = binprot.NewBinaryResponder(remoteWriter)
			} else {
				reqParser = textprot.NewTextParser(remoteReader)
				responder = textprot.NewTextResponder(remoteWriter)
			}

			server := s([]io.Closer{remoteConn, l1, l2}, reqParser, o(l1, l2, responder))

			go server.Loop()
		}(remote)
	}
}
Example #2
0
func handleConnectionReal(remoteConn net.Conn, l1, l2 handlers.Handler) {
	remoteReader := bufio.NewReader(remoteConn)
	remoteWriter := bufio.NewWriter(remoteConn)

	var reqParser common.RequestParser
	var responder common.Responder
	var reqType common.RequestType
	var request interface{}

	binaryParser := binprot.NewBinaryParser(remoteReader)
	binaryResponder := binprot.NewBinaryResponder(remoteWriter)
	textParser := textprot.NewTextParser(remoteReader)
	textResponder := textprot.NewTextResponder(remoteWriter)

	for {
		binary, err := isBinaryRequest(remoteReader)

		if err != nil {
			abort([]io.Closer{remoteConn, l1, l2}, err, binary)
			return
		}

		if binary {
			reqParser = binaryParser
			responder = binaryResponder
		} else {
			reqParser = textParser
			responder = textResponder
		}

		request, reqType, err = reqParser.Parse()

		if err != nil {
			abort([]io.Closer{remoteConn, l1, l2}, err, binary)
			return
		}

		// TODO: handle nil
		switch reqType {
		case common.RequestSet:
			req := request.(common.SetRequest)
			//fmt.Println("set", string(req.Key))
			err = l1.Set(req, remoteReader)

			if err == nil {
				responder.Set()
			}

		case common.RequestDelete:
			req := request.(common.DeleteRequest)
			//fmt.Println("delete", string(req.Key))
			err = l1.Delete(req)

			if err == nil {
				responder.Delete()
			}

		case common.RequestTouch:
			req := request.(common.TouchRequest)
			//fmt.Println("touch", string(req.Key))
			err = l1.Touch(req)

			if err == nil {
				responder.Touch()
			}

		case common.RequestGet:
			req := request.(common.GetRequest)
			//debugString := "get"
			//for _, k := range req.Keys {
			//	debugString += " "
			//	debugString += string(k)
			//}
			//println(debugString)
			resChan, errChan := l1.Get(req)

			for {
				select {
				case res, ok := <-resChan:
					if !ok {
						resChan = nil
					} else {
						if res.Miss {
							responder.GetMiss(res)
						} else {
							responder.Get(res)
						}
					}

				case getErr, ok := <-errChan:
					if !ok {
						errChan = nil
					} else {
						err = getErr
					}
				}

				if resChan == nil && errChan == nil {
					break
				}
			}

			if err == nil {
				responder.GetEnd(req.NoopEnd)
			}

		case common.RequestGat:
			req := request.(common.GATRequest)
			//fmt.Println("gat", string(req.Key))
			res, err := l1.GAT(req)

			if err == nil {
				if res.Miss {
					responder.GATMiss(res)
				} else {
					responder.GAT(res)
					responder.GetEnd(false)
				}
			}

		case common.RequestUnknown:
			err = common.ErrUnknownCmd
		}

		// TODO: distinguish fatal errors from non-fatal
		if err != nil {
			if common.IsAppError(err) {
				responder.Error(err)
			} else {
				abort([]io.Closer{remoteConn, l1, l2}, err, binary)
				return
			}
		}
	}
}