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) } }
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 } } } }