func (resp *Response) Read(b *bufio.Reader) error { resp.items = make(map[string]*Item, 1) for { s, e := b.ReadString('\n') if e != nil { log.Print("read response line failed", e) return e } parts := strings.Fields(s) if len(parts) < 1 { return errors.New("invalid response") } resp.status = parts[0] switch resp.status { case "VALUE": if len(parts) < 4 { return errors.New("invalid response") } key := parts[1] // check key length flag, e1 := strconv.Atoi(parts[2]) if e1 != nil { return errors.New("invalid response") } length, e2 := strconv.Atoi(parts[3]) if e2 != nil { return errors.New("invalid response") } if length > MaxBodyLength { return errors.New("body too large") } item := &Item{Flag: flag} if len(parts) == 5 { cas, e := strconv.Atoi(parts[4]) if e != nil { return errors.New("invalid response") } item.Cas = cas } // FIXME if length > AllocLimit { item.alloc = cmem.Alloc(uintptr(length)) item.Body = (*[1 << 30]byte)(unsafe.Pointer(item.alloc))[:length] (*reflect.SliceHeader)(unsafe.Pointer(&item.Body)).Cap = length runtime.SetFinalizer(item, func(item *Item) { if item.alloc != nil { //log.Print("free by finalizer: ", cap(item.Body)) cmem.Free(item.alloc, uintptr(cap(item.Body))) item.Body = nil item.alloc = nil } }) } else { item.Body = make([]byte, length) } if _, e = io.ReadFull(b, item.Body); e != nil { return e } b.ReadByte() // \r b.ReadByte() // \n resp.items[key] = item continue case "STAT": if len(parts) != 3 { return errors.New("invalid response") } var item Item item.Body = []byte(parts[2]) resp.items[parts[1]] = &item continue case "END": case "STORED", "NOT_STORED", "DELETED", "NOT_FOUND": case "OK": case "ERROR", "SERVER_ERROR", "CLIENT_ERROR": if len(parts) > 1 { resp.msg = parts[1] } log.Print("error:", resp) default: // try to convert to int _, err := strconv.Atoi(resp.status) if err == nil { // response from incr,decr resp.msg = resp.status resp.status = "INCR" } else { log.Print("unknown status:", s, resp.status) return errors.New("unknown response:" + resp.status) } } break } return nil }
func (req *Request) Read(b *bufio.Reader) (e error) { var s string if s, e = b.ReadString('\n'); e != nil { return e } if !strings.HasSuffix(s, "\r\n") { return errors.New("not completed command") } parts := strings.Fields(s) if len(parts) < 1 { return errors.New("invalid cmd") } req.Cmd = parts[0] switch req.Cmd { case "get", "gets": if len(parts) < 2 { return errors.New("invalid cmd") } req.Keys = parts[1:] case "set", "add", "replace", "cas", "append", "prepend": if len(parts) < 5 || len(parts) > 7 { return errors.New("invalid cmd") } req.Keys = parts[1:2] req.Item = &Item{} item := req.Item item.Flag, e = strconv.Atoi(parts[2]) if e != nil { return e } item.Exptime, e = strconv.Atoi(parts[3]) if e != nil { return e } length, e := strconv.Atoi(parts[4]) if e != nil { return e } if length > MaxBodyLength { return errors.New("body too large") } if req.Cmd == "cas" { if len(parts) < 6 { return errors.New("invalid cmd") } item.Cas, e = strconv.Atoi(parts[5]) if len(parts) > 6 && parts[6] != "noreply" { return errors.New("invalid cmd") } req.NoReply = len(parts) > 6 && parts[6] == "noreply" } else { if len(parts) > 5 && parts[5] != "noreply" { return errors.New("invalid cmd") } req.NoReply = len(parts) > 5 && parts[5] == "noreply" } // FIXME if length > AllocLimit { item.alloc = cmem.Alloc(uintptr(length)) item.Body = (*[1 << 30]byte)(unsafe.Pointer(item.alloc))[:length] (*reflect.SliceHeader)(unsafe.Pointer(&item.Body)).Cap = length runtime.SetFinalizer(item, func(item *Item) { if item.alloc != nil { //log.Print("free by finalizer: ", cap(item.Body)) cmem.Free(item.alloc, uintptr(cap(item.Body))) item.Body = nil item.alloc = nil } }) } else { item.Body = make([]byte, length) } if _, e = io.ReadFull(b, item.Body); e != nil { return e } b.ReadByte() // \r b.ReadByte() // \n case "delete": if len(parts) < 2 || len(parts) > 4 { return errors.New("invalid cmd") } req.Keys = parts[1:2] req.NoReply = len(parts) > 2 && parts[len(parts)-1] == "noreply" case "incr", "decr": if len(parts) < 3 || len(parts) > 4 { return errors.New("invalid cmd") } req.Keys = parts[1:2] req.Item = &Item{Body: []byte(parts[2])} req.NoReply = len(parts) > 3 && parts[3] == "noreply" case "stats": req.Keys = parts[1:] case "quit", "version", "flush_all": case "verbosity": if len(parts) >= 2 { req.Keys = parts[1:] } default: log.Print("unknown command", req.Cmd) return errors.New("unknown command: " + req.Cmd) } return }