// parseIntoBSO takes JSON and turns into a syncstorage.PutBSOInput func parseIntoBSO(jsonData json.RawMessage, bso *syncstorage.PutBSOInput) *parseError { // make sure JSON BSO data *only* has the keys that are allowed var bkeys map[string]json.RawMessage err := json.Unmarshal(jsonData, &bkeys) if err != nil { return &parseError{field: "-", msg: "Could not parse into object"} } else { for k, _ := range bkeys { switch k { case "id", "payload", "ttl", "sortindex": // it's ok case "modified": // to pass the python test_meta_global_sanity functional test default: return &parseError{field: k, msg: "invalid field"} } } } var bId string // check to make sure values are appropriate if r, ok := bkeys["id"]; ok { err := json.Unmarshal(r, &bId) if err != nil { return &parseError{field: "id", msg: "Invalid format"} } else { bso.Id = bId } } if r, ok := bkeys["payload"]; ok { var payload string err := json.Unmarshal(r, &payload) if err != nil { return &parseError{bId: bId, field: "payload", msg: "Invalid format"} } else { bso.Payload = &payload } } if r, ok := bkeys["ttl"]; ok { var ttl int err := json.Unmarshal(r, &ttl) if err != nil { return &parseError{bId: bId, field: "ttl", msg: "Invalid format"} } else { bso.TTL = &ttl } } if r, ok := bkeys["sortindex"]; ok { var sortindex int err := json.Unmarshal(r, &sortindex) if err != nil { return &parseError{bId: bId, field: "sortindex", msg: "Invalid format"} } else { bso.SortIndex = &sortindex } } return nil }
func (s *SyncUserHandler) hBsoPUT(w http.ResponseWriter, r *http.Request) { if !AcceptHeaderOk(w, r) { return } // accept text/plain from old (broken) clients ct := getMediaType(r.Header.Get("Content-Type")) if ct != "application/json" && ct != "text/plain" && ct != "application/newlines" { sendRequestProblem(w, r, http.StatusUnsupportedMediaType, errors.Errorf("Not acceptable Content-Type: %s", ct)) return } var ( bId string ok bool cId int modified int err error ) if bId, ok = extractBsoIdFail(w, r); !ok { return } cId, err = s.getcid(r, true) if err != nil { InternalError(w, r, err) return } modified, err = s.db.GetBSOModified(cId, bId) if err != nil { if err != syncstorage.ErrNotFound { InternalError(w, r, errors.Wrap(err, "Could not get Modified ts")) return } } if sentNotModified(w, r, modified) { return } body, err := ioutil.ReadAll(r.Body) if err != nil { InternalError(w, r, errors.New("PUT could not read JSON body")) return } var bso syncstorage.PutBSOInput if err := parseIntoBSO(body, &bso); err != nil { WeaveInvalidWBOError(w, r, errors.Wrap(err, "Could not parse body into BSO")) return } if bso.Payload != nil && len(*bso.Payload) > s.config.MaxRecordPayloadBytes { sendRequestProblem(w, r, http.StatusRequestEntityTooLarge, errors.New("Payload too big")) return } // change bso.TTL to milliseconds (what the db uses) // from seconds (what client's send) if bso.TTL != nil { tmp := *bso.TTL * 1000 bso.TTL = &tmp } modified, err = s.db.PutBSO(cId, bId, bso.Payload, bso.SortIndex, bso.TTL) if err != nil { sendRequestProblem(w, r, http.StatusBadRequest, err) return } m := syncstorage.ModifiedToString(modified) w.Header().Set("Content-Type", "application/json") w.Header().Set("X-Last-Modified", m) w.Write([]byte(m)) }