// // PUT /values/{key} // // If {key} already exists, wait until it's available (ignore cases where the client times out) then acquire the lock on it. // If it doesn't already exist, create it and immediately acquire the lock on it (that operation should never block). // func (s *Server) PutAndLock(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) // handle key var rawKey, exists := vars["key"] if !exists { w.WriteHeader(http.StatusBadRequest) return } key := memdb.Key(rawKey) // handle POST body rawValue, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } value := memdb.Value(rawValue) // store value into the memdb lockid := s.mdb.Put(key, value) // create response jsonResponse := &LockResponse{LockId: string(lockid)} body, err := json.Marshal(&jsonResponse) if err != nil { w.WriteHeader(http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") w.Write(body) }
// // POST /values/{key}/{lock_id}?release={true, false} // // Attempt to update the value of {key} to the value given in the POST body according to the following rules: // // If {key} doesn't exist, return 404 Not Found // If {key} exists but {lock_id} doesn't identify the currently held lock, do no action and respond immediately with 401 Unauthorized. // If {key} exists, {lock_id} identifies the currently held lock and release=true, set the new value, release the lock and invalidate {lock_id}. Return 204 No Content // If {key} exists, {lock_id} identifies the currently held lock and release=false, set the new value but don't release the lock and keep {lock_id} value. Return 204 No Content // func (s *Server) Update(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) // handle key var rawKey, exists := vars["key"] if !exists { w.WriteHeader(http.StatusBadRequest) return } key := memdb.Key(rawKey) // handle lock_id var rawLockId, exists := vars["lock_id"] if !exists { w.WriteHeader(http.StatusBadRequest) return } lockId := memdb.LockID(rawLockId) // handle release query param release, err := strconv.ParseBool(vars["release"]) if err != nil { w.WriteHeader(http.StatusBadRequest) return } // read POST Body body, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } // try to update... err = s.mdb.Update(lockId, key, memdb.Value(string(body)), release) if err == memdb.ErrKeyNotFound { w.WriteHeader(http.StatusNotFound) return } else if err == memdb.ErrLockIdNotFound { w.WriteHeader(http.StatusUnauthorized) return } else if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) }
func TestRestServerUpdate(t *testing.T) { server := NewRestServer() // log := log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) // server := NewRestServerWithLogger(log) // try to release unexists lock for unexists key rec0 := httptest.NewRecorder() req, err := http.NewRequest("POST", "http://memdb.devel/values/key0/1?release=true", strings.NewReader("")) assert.Nil(t, err) server.Router().ServeHTTP(rec0, req) assert.Equal(t, http.StatusNotFound, rec0.Code) // put new value and get lock rec2 := httptest.NewRecorder() req2, err2 := http.NewRequest("PUT", "http://memdb.devel/values/key0", strings.NewReader("value")) assert.Nil(t, err2) server.Router().ServeHTTP(rec2, req2) jr2 := &LockResponse{} assert.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &jr2)) assert.Equal(t, "1", jr2.LockId) v2, exists2 := server.mdb.DirectGet(memdb.Key("key0")) assert.True(t, exists2) assert.Equal(t, memdb.Value("value"), v2) // try to release unexists lock for exists key rec3 := httptest.NewRecorder() req3, err3 := http.NewRequest("POST", "http://memdb.devel/values/key0/2?release=true", strings.NewReader("")) assert.Nil(t, err3) server.Router().ServeHTTP(rec3, req3) assert.Equal(t, http.StatusUnauthorized, rec3.Code) // set new value for exists key and release lock rec4 := httptest.NewRecorder() req4, err4 := http.NewRequest("POST", "http://memdb.devel/values/key0/1?release=true", strings.NewReader("newValue")) assert.Nil(t, err4) server.Router().ServeHTTP(rec4, req4) assert.Equal(t, http.StatusNoContent, rec4.Code) v, exists := server.mdb.DirectGet(memdb.Key("key0")) assert.True(t, exists) assert.Equal(t, memdb.Value("newValue"), v) // check if lock was released... rec5 := httptest.NewRecorder() req5, err5 := http.NewRequest("POST", "http://memdb.devel/values/key0/1?release=false", strings.NewReader("")) assert.Nil(t, err5) server.Router().ServeHTTP(rec5, req5) assert.Equal(t, http.StatusUnauthorized, rec5.Code) // get new lock for exists key //server.router.HandleFunc("/reservations/{key}", http.HandlerFunc(server.GetAndLock)).Methods("POST") rec6 := httptest.NewRecorder() req6, err6 := http.NewRequest("POST", "http://memdb.devel/reservations/key0", strings.NewReader("")) assert.Nil(t, err6) server.Router().ServeHTTP(rec6, req6) assert.Equal(t, http.StatusOK, rec6.Code) jr6 := &LockValueResponse{} err6 = json.Unmarshal(rec6.Body.Bytes(), &jr6) assert.NoError(t, err6) assert.Equal(t, "2", jr6.LockId) assert.Equal(t, "newValue", jr6.Value) // update value but don't release lock rec7 := httptest.NewRecorder() req7, err7 := http.NewRequest("POST", "http://memdb.devel/values/key0/2?release=false", strings.NewReader("otherValue")) assert.Nil(t, err7) server.Router().ServeHTTP(rec7, req7) assert.Equal(t, http.StatusNoContent, rec7.Code) v7, exists7 := server.mdb.DirectGet(memdb.Key("key0")) assert.True(t, exists7) assert.Equal(t, memdb.Value("otherValue"), v7) // release lock rec8 := httptest.NewRecorder() req8, err8 := http.NewRequest("POST", "http://memdb.devel/values/key0/2?release=true", strings.NewReader("released")) assert.Nil(t, err8) server.Router().ServeHTTP(rec8, req8) assert.Equal(t, http.StatusNoContent, rec8.Code) }