func simpleCmdLocal(rw *bufio.ReadWriter) error { if err := rw.Flush(); err != nil { return err } resHeader, err := binprot.ReadResponseHeader(rw) if err != nil { return err } defer binprot.PutResponseHeader(resHeader) err = binprot.DecodeError(resHeader) if err != nil { n, ioerr := rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { return ioerr } return err } // Read in the message bytes from the body n, err := rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) return err }
func (t TextResponder) Get(response common.GetResponse) error { if response.Miss { // A miss is a no-op in the text world return nil } // Write data out to client // [VALUE <key> <flags> <bytes>\r\n // <data block>\r\n]* // END\r\n n, err := fmt.Fprintf(t.writer, "VALUE %s %d %d\r\n", response.Key, response.Flags, len(response.Data)) metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n)) if err != nil { return err } n, err = t.writer.Write(response.Data) metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n)) if err != nil { return err } n, err = t.writer.WriteString("\r\n") metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n)) if err != nil { return err } t.writer.Flush() return nil }
func (h Handler) handleSetCommon(cmd common.SetRequest) error { // TODO: should there be a unique flags value for regular data? // Write value h.rw.Write(cmd.Data) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(len(cmd.Data))) if err := h.rw.Flush(); err != nil { return err } // Read server's response resHeader, err := readResponseHeader(h.rw.Reader) if err != nil { // Discard response body n, ioerr := h.rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { return ioerr } // For Add and Replace, the error here will be common.ErrKeyExists or common.ErrKeyNotFound // respectively. For each, this is the right response to send to the requestor. The error // here is overloaded because it would signal a true error for sets, but a normal "error" // response for Add and Replace. return err } return nil }
func (l *L1OnlyOrca) Get(req common.GetRequest) error { metrics.IncCounter(MetricCmdGet) metrics.IncCounterBy(MetricCmdGetKeys, uint64(len(req.Keys))) //debugString := "get" //for _, k := range req.Keys { // debugString += " " // debugString += string(k) //} //println(debugString) metrics.IncCounter(MetricCmdGetL1) metrics.IncCounterBy(MetricCmdGetKeysL1, uint64(len(req.Keys))) resChan, errChan := l.l1.Get(req) var err error // Read all the responses back from l.l1. // The contract is that the resChan will have GetResponse's for get hits and misses, // and the errChan will have any other errors, such as an out of memory error from // memcached. If any receive happens from errChan, there will be no more responses // from resChan. for { select { case res, ok := <-resChan: if !ok { resChan = nil } else { if res.Miss { metrics.IncCounter(MetricCmdGetMissesL1) metrics.IncCounter(MetricCmdGetMisses) } else { metrics.IncCounter(MetricCmdGetHits) metrics.IncCounter(MetricCmdGetHitsL1) } l.res.Get(res) } case getErr, ok := <-errChan: if !ok { errChan = nil } else { metrics.IncCounter(MetricCmdGetErrors) metrics.IncCounter(MetricCmdGetErrorsL1) err = getErr } } if resChan == nil && errChan == nil { break } } if err == nil { l.res.GetEnd(req.NoopOpaque, req.NoopEnd) } return err }
func (h Handler) realHandleSet(cmd common.SetRequest, reqType common.RequestType) error { // TODO: should there be a unique flags value for regular data? // Write command header switch reqType { case common.RequestSet: if err := binprot.WriteSetCmd(h.rw.Writer, cmd.Key, cmd.Flags, cmd.Exptime, uint32(len(cmd.Data))); err != nil { return err } case common.RequestAdd: if err := binprot.WriteAddCmd(h.rw.Writer, cmd.Key, cmd.Flags, cmd.Exptime, uint32(len(cmd.Data))); err != nil { return err } case common.RequestReplace: if err := binprot.WriteReplaceCmd(h.rw.Writer, cmd.Key, cmd.Flags, cmd.Exptime, uint32(len(cmd.Data))); err != nil { return err } default: // I know. It's all wrong. By rights we shouldn't even be here. But we are. panic("Unrecognized request type in realHandleSet!") } // Write value h.rw.Write(cmd.Data) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(len(cmd.Data))) if err := h.rw.Flush(); err != nil { return err } // Read server's response resHeader, err := readResponseHeader(h.rw.Reader) if err != nil { // Discard response body n, ioerr := h.rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { return ioerr } // For Add and Replace, the error here will be common.ErrKeyExists or common.ErrKeyNotFound // respectively. For each, this is the right response to send to the requestor. The error // here is overloaded because it would signal a true error for sets, but a normal "error" // response for Add and Replace. return err } return nil }
func writeSuccessResponseHeader(w *bufio.Writer, opcode uint8, keyLength, extraLength, totalBodyLength int, opaque uint32, flush bool) error { header := resHeadPool.Get().(ResponseHeader) header.Magic = MagicResponse header.Opcode = opcode header.KeyLength = uint16(keyLength) header.ExtraLength = uint8(extraLength) header.DataType = uint8(0) header.Status = StatusSuccess header.TotalBodyLength = uint32(totalBodyLength) header.OpaqueToken = opaque header.CASToken = uint64(0) if err := writeResponseHeader(w, header); err != nil { resHeadPool.Put(header) return err } if flush { if err := w.Flush(); err != nil { resHeadPool.Put(header) return err } } metrics.IncCounterBy(common.MetricBytesWrittenRemote, resHeaderLen) resHeadPool.Put(header) return nil }
func writeErrorResponseHeader(w *bufio.Writer, opcode uint8, status uint16, opaque uint32) error { header := resHeadPool.Get().(ResponseHeader) header.Magic = MagicResponse header.Opcode = opcode header.KeyLength = uint16(0) header.ExtraLength = uint8(0) header.DataType = uint8(0) header.Status = status header.TotalBodyLength = uint32(0) header.OpaqueToken = opaque header.CASToken = uint64(0) if err := writeResponseHeader(w, header); err != nil { resHeadPool.Put(header) return err } if err := w.Flush(); err != nil { resHeadPool.Put(header) return err } metrics.IncCounterBy(common.MetricBytesWrittenRemote, resHeaderLen) resHeadPool.Put(header) return nil }
func appendPrependRequest(r io.Reader, reqHeader RequestHeader, reqType common.RequestType, quiet bool, start uint64) (common.SetRequest, common.RequestType, uint64, error) { // key, value key, err := readString(r, reqHeader.KeyLength) if err != nil { log.Println("Error reading key") return common.SetRequest{}, reqType, start, err } realLength := reqHeader.TotalBodyLength - uint32(reqHeader.KeyLength) // Read in the body of the set request dataBuf := make([]byte, realLength) n, err := io.ReadAtLeast(r, dataBuf, int(realLength)) metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n)) if err != nil { return common.SetRequest{}, reqType, start, err } return common.SetRequest{ Quiet: quiet, Key: key, Flags: 0, Exptime: 0, Opaque: reqHeader.OpaqueToken, Data: dataBuf, }, reqType, start, nil }
func (b BinaryResponder) Version(opaque uint32) error { if err := writeSuccessResponseHeader(b.writer, OpcodeVersion, 0, 0, len(common.VersionString), opaque, false); err != nil { return err } n, _ := b.writer.WriteString(common.VersionString) metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n)) return b.writer.Flush() }
func setRequest(r *bufio.Reader, clParts []string, reqType common.RequestType, start uint64) (common.SetRequest, common.RequestType, uint64, error) { // sanity check if len(clParts) != 5 { return common.SetRequest{}, reqType, start, common.ErrBadRequest } key := []byte(clParts[1]) flags, err := strconv.ParseUint(strings.TrimSpace(clParts[2]), 10, 32) if err != nil { log.Printf("Error parsing flags for set/add/replace command: %s\n", err.Error()) return common.SetRequest{}, reqType, start, common.ErrBadFlags } exptime, err := strconv.ParseUint(strings.TrimSpace(clParts[3]), 10, 32) if err != nil { log.Printf("Error parsing ttl for set/add/replace command: %s\n", err.Error()) return common.SetRequest{}, reqType, start, common.ErrBadExptime } length, err := strconv.ParseUint(strings.TrimSpace(clParts[4]), 10, 32) if err != nil { log.Printf("Error parsing length for set/add/replace command: %s\n", err.Error()) return common.SetRequest{}, reqType, start, common.ErrBadLength } // Read in data dataBuf := make([]byte, length) n, err := io.ReadAtLeast(r, dataBuf, int(length)) metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n)) if err != nil { return common.SetRequest{}, reqType, start, common.ErrInternal } // Consume the last two bytes "\r\n" r.ReadString(byte('\n')) metrics.IncCounterBy(common.MetricBytesReadRemote, 2) return common.SetRequest{ Key: key, Flags: uint32(flags), Exptime: uint32(exptime), Opaque: uint32(0), Data: dataBuf, }, reqType, start, nil }
func writeMetadata(w io.Writer, md metadata) error { buf := make([]byte, metadataSize-tokenSize) binary.BigEndian.PutUint32(buf[0:4], md.Length) binary.BigEndian.PutUint32(buf[4:8], md.OrigFlags) binary.BigEndian.PutUint32(buf[8:12], md.NumChunks) binary.BigEndian.PutUint32(buf[12:16], md.ChunkSize) n, err := w.Write(buf) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n)) if err != nil { return err } n, err = w.Write(md.Token[:]) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n)) return err }
func (t TextResponder) resp(s string) error { n, err := fmt.Fprintf(t.writer, s+"\r\n") metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n)) if err != nil { return err } return t.writer.Flush() }
func getLocal(rw *bufio.ReadWriter, readExp bool) (data []byte, flags, exp uint32, err error) { if err := rw.Flush(); err != nil { return nil, 0, 0, err } resHeader, err := binprot.ReadResponseHeader(rw) if err != nil { return nil, 0, 0, err } defer binprot.PutResponseHeader(resHeader) err = binprot.DecodeError(resHeader) if err != nil { n, ioerr := rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { return nil, 0, 0, ioerr } return nil, 0, 0, err } var serverFlags uint32 binary.Read(rw, binary.BigEndian, &serverFlags) metrics.IncCounterBy(common.MetricBytesReadLocal, 4) var serverExp uint32 if readExp { binary.Read(rw, binary.BigEndian, &serverExp) metrics.IncCounterBy(common.MetricBytesReadLocal, 4) } // total body - key - extra dataLen := resHeader.TotalBodyLength - uint32(resHeader.KeyLength) - uint32(resHeader.ExtraLength) buf := make([]byte, dataLen) // Read in value n, err := io.ReadAtLeast(rw, buf, int(dataLen)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if err != nil { return nil, 0, 0, err } return buf, serverFlags, serverExp, nil }
func readString(r io.Reader, l uint16) ([]byte, error) { buf := make([]byte, l) n, err := io.ReadAtLeast(r, buf, int(l)) metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n)) if err != nil { return nil, err } return buf, nil }
func getMetadataCommon(rw *bufio.ReadWriter) (metadata, error) { if err := rw.Flush(); err != nil { return emptyMeta, err } resHeader, err := binprot.ReadResponseHeader(rw) if err != nil { return emptyMeta, err } defer binprot.PutResponseHeader(resHeader) err = binprot.DecodeError(resHeader) if err != nil { // read in the message "Not found" after a miss n, ioerr := rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { return emptyMeta, ioerr } return emptyMeta, err } // we currently do nothing with the flags //buf := make([]byte, 4) //n, err := io.ReadAtLeast(rw, buf, 4) //metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) //if err != nil { // return emptyMeta, err //} //serverFlags := binary.BigEndian.Uint32(buf) // instead of reading and parsing flags, just discard rw.Discard(4) metrics.IncCounterBy(common.MetricBytesReadLocal, 4) metaData, err := readMetadata(rw) if err != nil { return emptyMeta, err } return metaData, nil }
func readUInt32(r io.Reader) (uint32, error) { buf := make([]byte, 4) n, err := io.ReadAtLeast(r, buf, 4) metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n)) if err != nil { return uint32(0), err } return binary.BigEndian.Uint32(buf), nil }
// Key commands send the header and key only func writeKeyCmd(w io.Writer, opcode uint8, key []byte) error { // opcode, keyLength, extraLength, totalBodyLength header := makeRequestHeader(opcode, len(key), 0, len(key)) writeRequestHeader(w, header) n, err := w.Write(key) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(ReqHeaderLen+n)) reqHeadPool.Put(header) return err }
// And the noop command is just a header func WriteNoopCmd(w io.Writer) error { // opcode, keyLength, extraLength, totalBodyLength header := makeRequestHeader(OpcodeNoop, 0, 0, 0) //fmt.Printf("Delete: key: %v | totalBodyLength: %v\n", string(key), len(key)) err := writeRequestHeader(w, header) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(ReqHeaderLen)) reqHeadPool.Put(header) return err }
func getCommon(w *bufio.Writer, response common.GetResponse, opcode uint8) error { // total body length = extras (flags, 4 bytes) + data length totalBodyLength := len(response.Data) + 4 writeSuccessResponseHeader(w, opcode, 0, 4, totalBodyLength, response.Opaque, false) buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, response.Flags) w.Write(buf) w.Write(response.Data) if err := w.Flush(); err != nil { return err } metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(totalBodyLength)) return nil }
func writeAppendPrependCmdCommon(w io.Writer, opcode uint8, key []byte, flags, exptime, dataSize uint32) error { // opcode, keyLength, extraLength, totalBodyLength // key + body totalBodyLength := len(key) + int(dataSize) header := makeRequestHeader(opcode, len(key), 0, totalBodyLength) writeRequestHeader(w, header) n, err := w.Write(key) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n)) reqHeadPool.Put(header) return err }
func readMetadata(r io.Reader) (metadata, error) { buf := make([]byte, metadataSize) n, err := io.ReadAtLeast(r, buf, metadataSize) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if err != nil { return emptyMeta, nil } m := metadata{} m.Length = binary.BigEndian.Uint32(buf[0:4]) m.OrigFlags = binary.BigEndian.Uint32(buf[4:8]) m.NumChunks = binary.BigEndian.Uint32(buf[8:12]) m.ChunkSize = binary.BigEndian.Uint32(buf[12:16]) copy(m.Token[:], buf[16:]) return m, nil }
// Key Exptime commands send the header, key, and an exptime func writeKeyExptimeCmd(w io.Writer, opcode uint8, key []byte, exptime uint32) error { // opcode, keyLength, extraLength, totalBodyLength // key + extras + body extrasLen := 4 totalBodyLength := len(key) + extrasLen header := makeRequestHeader(opcode, len(key), extrasLen, totalBodyLength) writeRequestHeader(w, header) buf := make([]byte, len(key)+4) binary.BigEndian.PutUint32(buf[0:4], exptime) copy(buf[4:], key) n, err := w.Write(buf) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n)) reqHeadPool.Put(header) return err }
func setRequest(r io.Reader, reqHeader RequestHeader, reqType common.RequestType, quiet bool) (common.SetRequest, common.RequestType, error) { // flags, exptime, key, value flags, err := readUInt32(r) if err != nil { log.Println("Error reading flags") return common.SetRequest{}, reqType, err } exptime, err := readUInt32(r) if err != nil { log.Println("Error reading exptime") return common.SetRequest{}, reqType, err } key, err := readString(r, reqHeader.KeyLength) if err != nil { log.Println("Error reading key") return common.SetRequest{}, reqType, err } realLength := reqHeader.TotalBodyLength - uint32(reqHeader.ExtraLength) - uint32(reqHeader.KeyLength) // Read in the body of the set request dataBuf := make([]byte, realLength) n, err := io.ReadAtLeast(r, dataBuf, int(realLength)) metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n)) if err != nil { return common.SetRequest{}, reqType, err } return common.SetRequest{ Quiet: quiet, Key: key, Flags: flags, Exptime: exptime, Opaque: reqHeader.OpaqueToken, Data: dataBuf, }, reqType, nil }
// Data commands are those that send a header, key, exptime, and data func writeDataCmdCommon(w io.Writer, opcode uint8, key []byte, flags, exptime, dataSize uint32) error { // opcode, keyLength, extraLength, totalBodyLength // key + extras + body extrasLen := 8 totalBodyLength := len(key) + extrasLen + int(dataSize) header := makeRequestHeader(opcode, len(key), extrasLen, totalBodyLength) writeRequestHeader(w, header) buf := make([]byte, len(key)+8) binary.BigEndian.PutUint32(buf[0:4], flags) binary.BigEndian.PutUint32(buf[4:8], exptime) copy(buf[8:], key) n, err := w.Write(buf) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n)) reqHeadPool.Put(header) return err }
func (b BinaryResponder) GetE(response common.GetEResponse) error { if response.Miss { if !response.Quiet { return b.Error(response.Opaque, common.RequestGetE, common.ErrKeyNotFound, false) } return nil } // total body length = extras (flags & exptime, 8 bytes) + data length totalBodyLength := len(response.Data) + 8 writeSuccessResponseHeader(b.writer, OpcodeGetE, 0, 8, totalBodyLength, response.Opaque, false) binary.Write(b.writer, binary.BigEndian, response.Flags) binary.Write(b.writer, binary.BigEndian, response.Exptime) b.writer.Write(response.Data) if err := b.writer.Flush(); err != nil { return err } metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(totalBodyLength)) return nil }
func simpleCmdLocal(rw *bufio.ReadWriter, flush bool) error { if flush { if err := rw.Flush(); err != nil { return err } } resHeader, err := binprot.ReadResponseHeader(rw) if err != nil { return err } n, ioerr := rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { binprot.PutResponseHeader(resHeader) return ioerr } binprot.PutResponseHeader(resHeader) return binprot.DecodeError(resHeader) }
func readRequestHeader(r io.Reader) (RequestHeader, error) { buf := bufPool.Get().([]byte) br, err := io.ReadAtLeast(r, buf, ReqHeaderLen) metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(br)) if err != nil { bufPool.Put(buf) return emptyReqHeader, err } if buf[0] != MagicRequest { bufPool.Put(buf) metrics.IncCounter(MetricBinaryRequestHeadersBadMagic) return emptyReqHeader, ErrBadMagic } rh := reqHeadPool.Get().(RequestHeader) rh.Magic = buf[0] rh.Opcode = buf[1] rh.KeyLength = binary.BigEndian.Uint16(buf[2:4]) rh.ExtraLength = buf[4] // ignore DataType, unused //rh.DataType = buf[5] rh.DataType = 0 // ignore VBucket, unused //rh.VBucket = binary.BigEndian.Uint16(buf[6:8]) rh.VBucket = 0 rh.TotalBodyLength = binary.BigEndian.Uint32(buf[8:12]) rh.OpaqueToken = binary.BigEndian.Uint32(buf[12:16]) // ignore CAS, unused in rend //rh.CASToken = binary.BigEndian.Uint64(buf[16:24]) rh.CASToken = 0 bufPool.Put(buf) metrics.IncCounter(MetricBinaryRequestHeadersParsed) return rh, nil }
func writeResponseHeader(w io.Writer, rh ResponseHeader) error { buf := bufPool.Get().([]byte) buf[0] = rh.Magic buf[1] = rh.Opcode binary.BigEndian.PutUint16(buf[2:4], rh.KeyLength) buf[4] = rh.ExtraLength // DataType is unused buf[5] = 0 binary.BigEndian.PutUint16(buf[6:8], rh.Status) binary.BigEndian.PutUint32(buf[8:12], rh.TotalBodyLength) binary.BigEndian.PutUint32(buf[12:16], rh.OpaqueToken) // zero CAS region for i := 16; i < 24; i++ { buf[i] = 0 } n, err := w.Write(buf) metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n)) bufPool.Put(buf) return err }
func getLocalIntoBuf(rw *bufio.Reader, metaData metadata, tokenBuf, dataBuf []byte, chunkNum, totalDataLength int) (opcodeNoop bool, err error) { resHeader, err := binprot.ReadResponseHeader(rw) if err != nil { return false, err } defer binprot.PutResponseHeader(resHeader) // it feels a bit dirty knowing about batch gets here, but it's the most logical place to put // a check for an opcode that signals the end of a batch get or GAT. This code is a bit too big // to copy-paste in multiple places. if resHeader.Opcode == binprot.OpcodeNoop { return true, nil } err = binprot.DecodeError(resHeader) if err != nil { // read in the message "Not found" after a miss n, ioerr := rw.Discard(int(resHeader.TotalBodyLength)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { return false, ioerr } return false, err } // we currently do nothing with the flags //buf := make([]byte, 4) //n, err := io.ReadAtLeast(rw, buf, 4) //metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) //if err != nil { // return emptyMeta, err //} //serverFlags := binary.BigEndian.Uint32(buf) // instead of reading and parsing flags, just discard rw.Discard(4) metrics.IncCounterBy(common.MetricBytesReadLocal, 4) // Read in token if requested if tokenBuf != nil { n, err := io.ReadAtLeast(rw, tokenBuf, tokenSize) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if err != nil { return false, err } } // indices for slicing, end exclusive start, end := chunkSliceIndices(int(metaData.ChunkSize), chunkNum, int(metaData.Length)) // read data directly into buf chunkBuf := dataBuf[start:end] // Read in value n, err := io.ReadAtLeast(rw, chunkBuf, len(chunkBuf)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if err != nil { return false, err } // consume padding at end of chunk if needed if len(chunkBuf) < totalDataLength { n, ioerr := rw.Discard(totalDataLength - len(chunkBuf)) metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n)) if ioerr != nil { return false, ioerr } } return false, nil }
func (t TextParser) Parse() (common.Request, common.RequestType, uint64, error) { data, err := t.reader.ReadString('\n') start := timer.Now() metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(len(data))) if err != nil { if err == io.EOF { log.Println("Connection closed") } else { log.Printf("Error while reading text command line: %s\n", err.Error()) } return nil, common.RequestUnknown, start, err } clParts := strings.Split(strings.TrimSpace(data), " ") switch clParts[0] { case "set": return setRequest(t.reader, clParts, common.RequestSet, start) case "add": return setRequest(t.reader, clParts, common.RequestAdd, start) case "replace": return setRequest(t.reader, clParts, common.RequestReplace, start) case "append": return setRequest(t.reader, clParts, common.RequestAppend, start) case "prepend": return setRequest(t.reader, clParts, common.RequestPrepend, start) case "get": if len(clParts) < 2 { return nil, common.RequestGet, start, common.ErrBadRequest } var keys [][]byte for _, key := range clParts[1:] { keys = append(keys, []byte(key)) } opaques := make([]uint32, len(keys)) quiet := make([]bool, len(keys)) return common.GetRequest{ Keys: keys, Opaques: opaques, Quiet: quiet, NoopEnd: false, }, common.RequestGet, start, nil case "delete": if len(clParts) != 2 { return nil, common.RequestDelete, start, common.ErrBadRequest } return common.DeleteRequest{ Key: []byte(clParts[1]), Opaque: uint32(0), }, common.RequestDelete, start, nil // TODO: Error handling for invalid cmd line case "touch": if len(clParts) != 3 { return nil, common.RequestTouch, start, common.ErrBadRequest } key := []byte(clParts[1]) exptime, err := strconv.ParseUint(strings.TrimSpace(clParts[2]), 10, 32) if err != nil { log.Printf("Error parsing ttl for touch command: %s\n", err.Error()) return nil, common.RequestSet, start, common.ErrBadRequest } return common.TouchRequest{ Key: key, Exptime: uint32(exptime), Opaque: uint32(0), }, common.RequestTouch, start, nil case "noop": if len(clParts) != 1 { return nil, common.RequestNoop, start, common.ErrBadRequest } return common.NoopRequest{ Opaque: 0, }, common.RequestNoop, start, nil case "quit": if len(clParts) != 1 { return nil, common.RequestQuit, start, common.ErrBadRequest } return common.QuitRequest{ Opaque: 0, Quiet: false, }, common.RequestQuit, start, nil case "version": if len(clParts) != 1 { return nil, common.RequestQuit, start, common.ErrBadRequest } return common.VersionRequest{ Opaque: 0, }, common.RequestVersion, start, nil default: return nil, common.RequestUnknown, start, nil } }