// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() action := strings.ToLower(r.Method) switch action { case "get": // Acceptable default: server.BadRequest(w, r, "googlevoxels can only handle GET HTTP verbs at this time") return } // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } if len(parts) < 4 { server.BadRequest(w, r, "incomplete API request") return } switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, d.Help()) case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "tile": if err := d.handleTileReq(w, r, parts); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: tile (%s)", r.Method, r.URL) case "raw": queryStrings := r.URL.Query() if throttle := queryStrings.Get("throttle"); throttle == "on" || throttle == "true" { if server.ThrottledHTTP(w) { return } defer server.ThrottledOpDone() } if err := d.handleImageReq(w, r, parts); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: image (%s)", r.Method, r.URL) default: server.BadAPIRequest(w, r, d) } }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() // versionID := ctx.VersionID() // Get the action (GET, POST) action := strings.ToLower(r.Method) // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } // Handle POST on data -> setting of configuration if len(parts) == 3 && action == "put" { config, err := server.DecodeJSON(r) if err != nil { server.BadRequest(w, r, err) return } if err := d.ModifyConfig(config); err != nil { server.BadRequest(w, r, err) return } if err := datastore.SaveDataByUUID(uuid, d); err != nil { server.BadRequest(w, r, err) return } fmt.Fprintf(w, "Changed '%s' based on received configuration:\n%s\n", d.DataName(), config) return } if len(parts) < 4 { server.BadRequest(w, r, "Incomplete API request") return } // Process help and info. switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, dtype.Help()) case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "sync": if action != "post" { server.BadRequest(w, r, "Only POST allowed to sync endpoint") return } replace := r.URL.Query().Get("replace") == "true" if err := datastore.SetSyncByJSON(d, uuid, replace, r.Body); err != nil { server.BadRequest(w, r, err) return } case "label": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'label' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include label after 'label' endpoint.") return } label, err := strconv.ParseUint(parts[4], 10, 64) if err != nil { server.BadRequest(w, r, err) return } if label == 0 { server.BadRequest(w, r, "Label 0 is protected background value and cannot be used for query.") return } queryStrings := r.URL.Query() jsonBytes, err := d.GetLabelJSON(ctx, label, queryStrings.Get("relationships") == "true") if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get synaptic elements for label %d (%s)", r.Method, label, r.URL) case "tag": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'tag' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include tag string after 'tag' endpoint.") return } tag := Tag(parts[4]) queryStrings := r.URL.Query() jsonBytes, err := d.GetTagJSON(ctx, tag, queryStrings.Get("relationships") == "true") if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get synaptic elements for tag %s (%s)", r.Method, tag, r.URL) case "elements": switch action { case "get": // GET <api URL>/node/<UUID>/<data name>/elements/<size>/<offset> if len(parts) < 6 { server.BadRequest(w, r, "Expect size and offset to follow 'elements' in GET request") return } sizeStr, offsetStr := parts[4], parts[5] ext3d, err := dvid.NewExtents3dFromStrings(offsetStr, sizeStr, "_") if err != nil { server.BadRequest(w, r, err) return } elements, err := d.GetRegionSynapses(ctx, ext3d) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonBytes, err := json.Marshal(elements) if err != nil { server.BadRequest(w, r, err) return } if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: synapse elements in subvolume (size %s, offset %s) (%s)", r.Method, sizeStr, offsetStr, r.URL) case "post": if err := d.StoreSynapses(ctx, r.Body); err != nil { server.BadRequest(w, r, err) return } default: server.BadRequest(w, r, "Only GET or POST action is available on 'elements' endpoint.") return } case "element": // DELETE <api URL>/node/<UUID>/<data name>/element/<coord> if action != "delete" { server.BadRequest(w, r, "Only DELETE action is available on 'element' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include coordinate after DELETE on 'element' endpoint.") return } pt, err := dvid.StringToPoint3d(parts[4], "_") if err != nil { server.BadRequest(w, r, err) return } if err := d.DeleteElement(ctx, pt); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: delete synaptic element at %s (%s)", r.Method, pt, r.URL) case "move": // POST <api URL>/node/<UUID>/<data name>/move/<from_coord>/<to_coord> if action != "post" { server.BadRequest(w, r, "Only POST action is available on 'move' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include 'from' and 'to' coordinate after 'move' endpoint.") return } fromPt, err := dvid.StringToPoint3d(parts[4], "_") if err != nil { server.BadRequest(w, r, err) return } toPt, err := dvid.StringToPoint3d(parts[5], "_") if err != nil { server.BadRequest(w, r, err) return } if err := d.MoveElement(ctx, fromPt, toPt); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: move synaptic element from %s to %s (%s)", r.Method, fromPt, toPt, r.URL) default: server.BadAPIRequest(w, r, d) } }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() action := strings.ToLower(r.Method) switch action { case "get", "post": // Acceptable default: server.BadRequest(w, r, "Can only handle GET or POST HTTP verbs") return } // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } if len(parts) < 4 { server.BadRequest(w, r, "incomplete API request") return } switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, d.Help()) case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "metadata": switch action { case "post": jsonBytes, err := ioutil.ReadAll(r.Body) if err != nil { server.BadRequest(w, r, err) return } if err := d.SetMetadata(uuid, jsonBytes); err != nil { server.BadRequest(w, r, err) return } case "get": if d.Levels == nil || len(d.Levels) == 0 { server.BadRequest(w, r, "tile metadata for imagetile %q was not set\n", d.DataName()) return } metadata := struct { MinTileCoord dvid.Point3d MaxTileCoord dvid.Point3d Levels TileSpec }{ d.MinTileCoord, d.MaxTileCoord, d.Levels, } jsonBytes, err := json.Marshal(metadata) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) } timedLog.Infof("HTTP %s: metadata (%s)", r.Method, r.URL) case "tile": switch action { case "post": err := d.PostTile(ctx, w, r, parts) if err != nil { server.BadRequest(w, r, "Error in posting tile with URL %q: %v\n", url, err) return } case "get": if err := d.ServeTile(ctx, w, r, parts); err != nil { server.BadRequest(w, r, err) return } } timedLog.Infof("HTTP %s: tile (%s)", r.Method, r.URL) case "tilekey": switch action { case "get": var err error var hexkey string if hexkey, err = d.GetTileKey(ctx, w, r, parts); err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, `{"key": "%s"}`, hexkey) timedLog.Infof("HTTP %s: tilekey (%s) returns %s", r.Method, r.URL, hexkey) default: server.BadRequest(w, r, fmt.Errorf("Cannot use HTTP %s for tilekey endpoint", action)) return } case "raw", "isotropic": if action == "post" { server.BadRequest(w, r, "imagetile '%s' can only PUT tiles not images", d.DataName()) return } if len(parts) < 7 { server.BadRequest(w, r, "%q must be followed by shape/size/offset", parts[3]) return } shapeStr, sizeStr, offsetStr := parts[4], parts[5], parts[6] planeStr := dvid.DataShapeString(shapeStr) plane, err := planeStr.DataShape() if err != nil { server.BadRequest(w, r, err) return } if plane.ShapeDimensions() != 2 { server.BadRequest(w, r, "Quadtrees can only return 2d images not %s", plane) return } slice, err := dvid.NewSliceFromStrings(planeStr, offsetStr, sizeStr, "_") if err != nil { server.BadRequest(w, r, err) return } source, err := datastore.GetDataByUUIDName(uuid, d.Source) if err != nil { server.BadRequest(w, r, err) return } src, ok := source.(*imageblk.Data) if !ok { server.BadRequest(w, r, "Cannot construct imagetile for non-voxels data: %s", d.Source) return } img, err := d.GetImage(ctx, src, slice, parts[3] == "isotropic") if err != nil { server.BadRequest(w, r, err) return } var formatStr string if len(parts) >= 8 { formatStr = parts[7] } err = dvid.WriteImageHttp(w, img.Get(), formatStr) if err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: tile-accelerated %s %s (%s)", r.Method, planeStr, parts[3], r.URL) default: server.BadAPIRequest(w, r, d) } }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() // Get the action (GET, POST) action := strings.ToLower(r.Method) // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } // Handle POST on data -> setting of configuration if len(parts) == 3 && action == "put" { config, err := server.DecodeJSON(r) if err != nil { server.BadRequest(w, r, err) return } if err := d.ModifyConfig(config); err != nil { server.BadRequest(w, r, err) return } if err := datastore.SaveDataByUUID(uuid, d); err != nil { server.BadRequest(w, r, err) return } fmt.Fprintf(w, "Changed '%s' based on received configuration:\n%s\n", d.DataName(), config) return } if len(parts) < 4 { server.BadRequest(w, r, "Incomplete API request") return } // Process help and info. switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, dtype.Help()) case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "sync": if action != "post" { server.BadRequest(w, r, "Only POST allowed to sync endpoint") return } replace := r.URL.Query().Get("replace") == "true" if err := datastore.SetSyncByJSON(d, uuid, replace, r.Body); err != nil { server.BadRequest(w, r, err) return } case "count": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'count' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include label and element type after 'count' endpoint.") return } label, err := strconv.ParseUint(parts[4], 10, 64) if err != nil { server.BadRequest(w, r, err) return } i := StringToIndexType(parts[5]) if i == UnknownIndex { server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5])) return } count, err := d.GetCountElementType(ctx, label, i) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonStr := fmt.Sprintf(`{"Label":%d,%q:%d}`, label, i, count) if _, err := io.WriteString(w, jsonStr); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get count for label %d, index type %s: %s", r.Method, label, i, r.URL) case "top": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'top' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include N and element type after 'top' endpoint.") return } n, err := strconv.ParseUint(parts[4], 10, 32) if err != nil { server.BadRequest(w, r, err) return } i := StringToIndexType(parts[5]) if i == UnknownIndex { server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5])) return } labelSizes, err := d.GetTopElementType(ctx, int(n), i) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonBytes, err := json.Marshal(labelSizes) if err != nil { server.BadRequest(w, r, err) return } if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get top %d labels for index type %s: %s", r.Method, n, i, r.URL) case "threshold": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'threshold' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include threshold # and element type after 'threshold' endpoint.") return } t, err := strconv.ParseUint(parts[4], 10, 32) if err != nil { server.BadRequest(w, r, err) return } minSize := uint32(t) i := StringToIndexType(parts[5]) if i == UnknownIndex { server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5])) return } queryStrings := r.URL.Query() var num, offset int offsetStr := queryStrings.Get("offset") if offsetStr != "" { offset, err = strconv.Atoi(offsetStr) if err != nil { server.BadRequest(w, r, fmt.Errorf("bad offset specified in query string (%q)", offsetStr)) return } } numStr := queryStrings.Get("n") if numStr != "" { num, err = strconv.Atoi(numStr) if err != nil { server.BadRequest(w, r, fmt.Errorf("bad num specified in query string (%q)", numStr)) return } } labels, err := d.GetLabelsByThreshold(ctx, i, minSize, offset, num) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonBytes, err := json.Marshal(labels) if err != nil { server.BadRequest(w, r, err) return } if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get %d labels for index type %s with threshold %d: %s", r.Method, num, i, t, r.URL) default: server.BadAPIRequest(w, r, d) } }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } if len(parts) < 4 { server.BadAPIRequest(w, r, d) return } // Process help and info. switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, d.Help()) return case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) return default: } // Get the key and process request var comment string command := parts[3] method := strings.ToLower(r.Method) switch command { case "roi": switch method { case "get": if !d.IsReady(ctx.VersionID()) { w.WriteHeader(http.StatusPartialContent) } jsonBytes, err := Get(ctx) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) comment = fmt.Sprintf("HTTP GET ROI %q: %d bytes\n", d.DataName(), len(jsonBytes)) case "post": data, err := ioutil.ReadAll(r.Body) if err != nil { server.BadRequest(w, r, err) return } err = d.PutJSON(ctx.VersionID(), data) if err != nil { server.BadRequest(w, r, err) return } comment = fmt.Sprintf("HTTP POST ROI %q: %d bytes\n", d.DataName(), len(data)) case "delete": if err := d.Delete(ctx); err != nil { server.BadRequest(w, r, err) return } comment = fmt.Sprintf("HTTP DELETE ROI %q\n", d.DataName()) } case "mask": if method != "get" { server.BadRequest(w, r, "ROI mask only supports GET") return } if len(parts) < 7 { server.BadRequest(w, r, "%q must be followed by shape/size/offset", command) return } shapeStr, sizeStr, offsetStr := parts[4], parts[5], parts[6] planeStr := dvid.DataShapeString(shapeStr) plane, err := planeStr.DataShape() if err != nil { server.BadRequest(w, r, err) return } switch plane.ShapeDimensions() { case 3: subvol, err := dvid.NewSubvolumeFromStrings(offsetStr, sizeStr, "_") if err != nil { server.BadRequest(w, r, err) return } data, err := d.GetMask(ctx, subvol) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/octet-stream") _, err = w.Write(data) if err != nil { server.BadRequest(w, r, err) return } default: server.BadRequest(w, r, "Currently only 3d masks ('0_1_2' shape) is supported") return } case "ptquery": switch method { case "get": server.BadRequest(w, r, "ptquery requires POST with list of points") return case "post": data, err := ioutil.ReadAll(r.Body) if err != nil { server.BadRequest(w, r, err) return } jsonBytes, err := d.PointQuery(ctx, data) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) comment = fmt.Sprintf("HTTP POST ptquery '%s'\n", d.DataName()) } case "partition": if method != "get" { server.BadRequest(w, r, "partition only supports GET request") return } queryStrings := r.URL.Query() batchsizeStr := queryStrings.Get("batchsize") batchsize, err := strconv.Atoi(batchsizeStr) if err != nil { server.BadRequest(w, r, fmt.Sprintf("Error reading batchsize query string: %v", err)) return } var jsonBytes []byte optimizedStr := queryStrings.Get("optimized") dvid.Infof("queryvalues = %v\n", queryStrings) if optimizedStr == "true" || optimizedStr == "on" { dvid.Infof("Perform optimized partitioning into subvolumes using batchsize %d\n", batchsize) jsonBytes, err = d.Partition(ctx, int32(batchsize)) } else { dvid.Infof("Performing simple partitioning into subvolumes using batchsize %d\n", batchsize) jsonBytes, err = d.SimplePartition(ctx, int32(batchsize)) } if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) comment = fmt.Sprintf("HTTP partition '%s' with batch size %d\n", d.DataName(), batchsize) default: server.BadAPIRequest(w, r, d) return } timedLog.Infof(comment) }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } if len(parts) < 4 { server.BadRequest(w, r, "incomplete API specification") return } var comment string action := strings.ToLower(r.Method) switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, d.Help()) return case "info": jsonStr, err := d.JSONString() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, jsonStr) return case "keys": keyList, err := d.GetKeys(ctx) if err != nil { server.BadRequest(w, r, err) return } jsonBytes, err := json.Marshal(keyList) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) comment = "HTTP GET keys" case "keyrange": if len(parts) < 6 { server.BadRequest(w, r, "expect beginning and end keys to follow 'keyrange' endpoint") return } // Return JSON list of keys keyBeg := parts[4] keyEnd := parts[5] keyList, err := d.GetKeysInRange(ctx, keyBeg, keyEnd) if err != nil { server.BadRequest(w, r, err) return } jsonBytes, err := json.Marshal(keyList) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) comment = fmt.Sprintf("HTTP GET keyrange [%q, %q]", keyBeg, keyEnd) case "key": if len(parts) < 5 { server.BadRequest(w, r, "expect key string to follow 'key' endpoint") return } keyStr := parts[4] switch action { case "get": // Return value of single key value, found, err := d.GetData(ctx, keyStr) if err != nil { server.BadRequest(w, r, err) return } if !found { http.Error(w, fmt.Sprintf("Key %q not found", keyStr), http.StatusNotFound) return } if value != nil || len(value) > 0 { _, err = w.Write(value) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/octet-stream") } comment = fmt.Sprintf("HTTP GET key %q of keyvalue %q: %d bytes (%s)\n", keyStr, d.DataName(), len(value), url) case "delete": if err := d.DeleteData(ctx, keyStr); err != nil { server.BadRequest(w, r, err) return } comment = fmt.Sprintf("HTTP DELETE data with key %q of keyvalue %q (%s)\n", keyStr, d.DataName(), url) case "post": data, err := ioutil.ReadAll(r.Body) if err != nil { server.BadRequest(w, r, err) return } err = d.PutData(ctx, keyStr, data) if err != nil { server.BadRequest(w, r, err) return } comment = fmt.Sprintf("HTTP POST keyvalue '%s': %d bytes (%s)\n", d.DataName(), len(data), url) default: server.BadRequest(w, r, "key endpoint does not support %q HTTP verb", action) return } default: server.BadAPIRequest(w, r, d) return } timedLog.Infof(comment) }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { // --- Don't time labelgraph ops because they are very small and frequent. // --- TODO -- Implement monitoring system that aggregates logged ops instead of // ----------- printing out each one. // timedLog := dvid.NewTimeLog() db, err := storage.GraphStore() if err != nil { server.BadRequest(w, r, err) return } // make sure transaction log is created d.initializeLog() // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts) < 4 { server.BadRequest(w, r, "No resource specified in URI") return } method := strings.ToLower(r.Method) if method == "put" { server.BadRequest(w, r, "PUT requests not supported") return } // Process help and info. switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, d.Help()) return case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "subgraph": // disable json schema validation (will speedup POST command) queryStrings := r.URL.Query() disableSchemaT := dvid.InstanceName(queryStrings.Get("unsafe")) disableSchema := false if len(disableSchemaT) != 0 && disableSchemaT == "true" { disableSchema = true } labelgraph, err := d.ExtractGraph(r, disableSchema) if err != nil { server.BadRequest(w, r, err) return } err = d.handleSubgraphBulk(ctx, db, w, labelgraph, method) if err != nil { server.BadRequest(w, r, err) return } case "neighbors": if method != "get" { server.BadRequest(w, r, "Only supports GETs") return } err := d.handleNeighbors(ctx, db, w, parts[4:]) if err != nil { server.BadRequest(w, r, err) return } case "merge": if method != "post" { server.BadRequest(w, r, "Only supports POSTs") return } labelgraph, err := d.ExtractGraph(r, false) if err != nil { server.BadRequest(w, r, err) return } err = d.handleMerge(ctx, db, w, labelgraph) if err != nil { server.BadRequest(w, r, err) return } case "weight": if method != "post" { server.BadRequest(w, r, "Only supports POSTs") return } labelgraph, err := d.ExtractGraph(r, false) if err != nil { server.BadRequest(w, r, err) return } err = d.handleWeightUpdate(ctx, db, w, labelgraph) if err != nil { server.BadRequest(w, r, err) return } case "propertytransaction": err := d.handlePropertyTransaction(ctx, db, w, r, parts[4:], method) if err != nil { server.BadRequest(w, r, err) return } case "property": err := d.handleProperty(ctx, db, w, r, parts[4:], method) if err != nil { server.BadRequest(w, r, err) return } case "undomerge": // not supported until transaction history is supported server.BadRequest(w, r, "undomerge not yet implemented") return default: server.BadAPIRequest(w, r, d) return } //timedLog.Infof("Successful labelgraph op %s on %q", parts[3], d.DataName()) }