Missing Role Based Access Control for the REST handlers in bleve/http package
Description
Bleve is a text indexing library for go. Bleve includes HTTP utilities under bleve/http package, that are used by its sample application. These HTTP methods pave way for exploitation of a node’s filesystem where the bleve index resides, if the user has used bleve’s own HTTP (bleve/http) handlers for exposing the access to the indexes. For instance, the CreateIndexHandler (http/index_create.go) and DeleteIndexHandler (http/index_delete.go) enable an attacker to create a bleve index (directory structure) anywhere where the user running the server has the write permissions and to delete recursively any directory owned by the same user account. Users who have used the bleve/http package for exposing access to bleve index without the explicit handling for the Role Based Access Controls(RBAC) of the index assets would be impacted by this issue. Version 2.5.0 relocated the http/ dir used _only_ by bleve-explorer to blevesearch/bleve-explorer, thereby addressing the issue. However, the http package is purely intended to be used for demonstration purposes. Bleve was never designed handle the RBACs, nor it was ever advertised to be used in that way. The collaborators of this project have decided to stay away from adding any authentication or authorization to bleve project at the moment. The bleve/http package is mainly for demonstration purposes and it lacks exhaustive validation of the user inputs as well as any authentication and authorization measures. It is recommended to not use bleve/http in production use cases.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Bleve HTTP handlers lack authentication and authorization, allowing attackers to create or delete directories on the server filesystem.
Vulnerability
The bleve/http package in Bleve versions prior to 2.5.0 provides HTTP handlers for index management, including CreateIndexHandler and DeleteIndexHandler. These handlers do not perform any authentication or authorization checks, and they lack exhaustive validation of user inputs [1][4]. As a result, an attacker can create a bleve index (directory structure) anywhere the server process has write permissions, or recursively delete any directory owned by the same user account [1][4]. The affected package is intended only for demonstration purposes and was never designed to handle role-based access controls [4].
Exploitation
An attacker needs network access to the HTTP endpoints exposed by the bleve/http handlers. No authentication or prior access is required. By sending crafted HTTP requests to the CreateIndexHandler or DeleteIndexHandler, the attacker can specify arbitrary file paths, causing the server to create or delete directories on the filesystem [1][4]. The attacker does not need any special privileges beyond the ability to reach the server.
Impact
Successful exploitation allows an attacker to create arbitrary directories or recursively delete directories on the server's filesystem, limited only by the permissions of the user running the Bleve process. This can lead to denial of service (by deleting critical directories) or potential further compromise if the attacker can place files in writable locations [1][4]. The vulnerability does not directly enable remote code execution or data disclosure, but it can be a stepping stone for more severe attacks.
Mitigation
The Bleve project has addressed this issue in version 2.5.0 by relocating the http/ directory (used only by bleve-explorer) to a separate repository, effectively removing the vulnerable handlers from the main Bleve library [2][4]. Users should upgrade to Bleve 2.5.0 or later. If upgrading is not possible, the only workaround is to avoid exposing the bleve/http handlers in production environments, as they are intended solely for demonstration purposes [1][4]. No authentication or authorization layer will be added to the bleve/http package [4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/blevesearch/bleveGo | <= 1.10.14 | — |
github.com/blevesearch/bleve/v2Go | < 2.5.0 | 2.5.0 |
Affected products
92- osv-coords91 versionspkg:apk/chainguard/dgraphpkg:apk/chainguard/giteapkg:apk/chainguard/gitea-compatpkg:apk/chainguard/gitea-fipspkg:apk/chainguard/gitea-fips-compatpkg:apk/chainguard/gitnesspkg:apk/chainguard/grafana-11.3pkg:apk/chainguard/grafana-11.3-oci-compatpkg:apk/chainguard/grafana-11.4pkg:apk/chainguard/grafana-11.4-oci-compatpkg:apk/chainguard/grafana-11.5pkg:apk/chainguard/grafana-11.5-oci-compatpkg:apk/chainguard/grafana-11.6pkg:apk/chainguard/grafana-11.6-oci-compatpkg:apk/chainguard/grafana-fips-11.3pkg:apk/chainguard/grafana-fips-11.3-oci-compatpkg:apk/chainguard/grafana-fips-11.4pkg:apk/chainguard/grafana-fips-11.4-oci-compatpkg:apk/chainguard/grafana-fips-11.5pkg:apk/chainguard/grafana-fips-11.5-oci-compatpkg:apk/chainguard/grafana-fips-11.6pkg:apk/chainguard/grafana-fips-11.6-oci-compatpkg:apk/chainguard/mattermost-10.0pkg:apk/chainguard/mattermost-10.0-compatpkg:apk/chainguard/mattermost-10.1pkg:apk/chainguard/mattermost-10.1-compatpkg:apk/chainguard/mattermost-10.2pkg:apk/chainguard/mattermost-10.2-compatpkg:apk/chainguard/mattermost-10.3pkg:apk/chainguard/mattermost-10.3-compatpkg:apk/chainguard/mattermost-10.4pkg:apk/chainguard/mattermost-10.4-compatpkg:apk/chainguard/mattermost-10.5pkg:apk/chainguard/mattermost-10.5-compatpkg:apk/chainguard/mattermost-10.6pkg:apk/chainguard/mattermost-10.6-compatpkg:apk/chainguard/mattermost-10.7pkg:apk/chainguard/mattermost-10.7-compatpkg:apk/chainguard/mattermost-10.8pkg:apk/chainguard/mattermost-10.8-compatpkg:apk/chainguard/mattermost-10.9pkg:apk/chainguard/mattermost-10.9-compatpkg:apk/chainguard/mattermost-9.11pkg:apk/chainguard/mattermost-9.11-compatpkg:apk/chainguard/mattermost-fips-10.5pkg:apk/chainguard/mattermost-fips-10.5-compatpkg:apk/chainguard/mattermost-fips-10.6pkg:apk/chainguard/mattermost-fips-10.6-compatpkg:apk/chainguard/mattermost-fips-10.7pkg:apk/chainguard/mattermost-fips-10.7-compatpkg:apk/chainguard/mattermost-fips-10.8pkg:apk/chainguard/mattermost-fips-10.8-compatpkg:apk/chainguard/mattermost-fips-10.9pkg:apk/chainguard/mattermost-fips-10.9-compatpkg:apk/chainguard/mattermost-fips-9.11pkg:apk/chainguard/mattermost-fips-9.11-compatpkg:apk/wolfi/dgraphpkg:apk/wolfi/giteapkg:apk/wolfi/gitea-compatpkg:apk/wolfi/gitnesspkg:apk/wolfi/grafana-11.3pkg:apk/wolfi/grafana-11.3-oci-compatpkg:apk/wolfi/grafana-11.4pkg:apk/wolfi/grafana-11.4-oci-compatpkg:apk/wolfi/grafana-11.5pkg:apk/wolfi/grafana-11.5-oci-compatpkg:apk/wolfi/grafana-11.6pkg:apk/wolfi/grafana-11.6-oci-compatpkg:apk/wolfi/mattermost-10.0pkg:apk/wolfi/mattermost-10.0-compatpkg:apk/wolfi/mattermost-10.1pkg:apk/wolfi/mattermost-10.1-compatpkg:apk/wolfi/mattermost-10.2pkg:apk/wolfi/mattermost-10.2-compatpkg:apk/wolfi/mattermost-10.3pkg:apk/wolfi/mattermost-10.3-compatpkg:apk/wolfi/mattermost-10.4pkg:apk/wolfi/mattermost-10.4-compatpkg:apk/wolfi/mattermost-10.5pkg:apk/wolfi/mattermost-10.5-compatpkg:apk/wolfi/mattermost-10.6pkg:apk/wolfi/mattermost-10.6-compatpkg:apk/wolfi/mattermost-10.7pkg:apk/wolfi/mattermost-10.7-compatpkg:apk/wolfi/mattermost-10.8pkg:apk/wolfi/mattermost-10.8-compatpkg:apk/wolfi/mattermost-10.9pkg:apk/wolfi/mattermost-10.9-compatpkg:golang/github.com/blevesearch/blevepkg:golang/github.com/blevesearch/bleve/v2pkg:rpm/opensuse/govulncheck-vulndb&distro=openSUSE%20Tumbleweed
< 0+ 90 more
- (no CPE)range: < 0
- (no CPE)range: < 1.23.7-r2
- (no CPE)range: < 1.23.7-r2
- (no CPE)range: < 1.23.7-r2
- (no CPE)range: < 1.23.7-r2
- (no CPE)range: < 3.0.0-r0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 11.6.1-r2
- (no CPE)range: < 11.6.1-r2
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 10.0.4-r11
- (no CPE)range: < 10.0.4-r11
- (no CPE)range: < 10.1.7-r8
- (no CPE)range: < 10.1.7-r8
- (no CPE)range: < 10.2.3-r6
- (no CPE)range: < 10.2.3-r6
- (no CPE)range: < 10.3.4-r8
- (no CPE)range: < 10.3.4-r8
- (no CPE)range: < 10.4.5-r2
- (no CPE)range: < 10.4.5-r2
- (no CPE)range: < 10.5.5-r1
- (no CPE)range: < 10.5.5-r1
- (no CPE)range: < 10.6.3-r2
- (no CPE)range: < 10.6.3-r2
- (no CPE)range: < 10.7.1-r2
- (no CPE)range: < 10.7.1-r2
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 9.11.15-r1
- (no CPE)range: < 9.11.15-r1
- (no CPE)range: < 10.5.5-r1
- (no CPE)range: < 10.5.5-r1
- (no CPE)range: < 10.6.3-r2
- (no CPE)range: < 10.6.3-r2
- (no CPE)range: < 10.7.1-r2
- (no CPE)range: < 10.7.1-r2
- (no CPE)range: < 10.8.1-r2
- (no CPE)range: < 10.8.1-r2
- (no CPE)range: < 10.9.2-r1
- (no CPE)range: < 10.9.2-r1
- (no CPE)range: < 9.11.14-r1
- (no CPE)range: < 9.11.14-r1
- (no CPE)range: < 0
- (no CPE)range: < 1.23.7-r2
- (no CPE)range: < 1.23.7-r2
- (no CPE)range: < 3.0.0-r0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 11.6.7-r0
- (no CPE)range: < 11.6.7-r0
- (no CPE)range: < 10.0.4-r11
- (no CPE)range: < 10.0.4-r11
- (no CPE)range: < 10.1.7-r8
- (no CPE)range: < 10.1.7-r8
- (no CPE)range: < 10.2.3-r6
- (no CPE)range: < 10.2.3-r6
- (no CPE)range: < 10.3.4-r8
- (no CPE)range: < 10.3.4-r8
- (no CPE)range: < 10.4.5-r2
- (no CPE)range: < 10.4.5-r2
- (no CPE)range: < 10.5.5-r1
- (no CPE)range: < 10.5.5-r1
- (no CPE)range: < 10.6.3-r2
- (no CPE)range: < 10.6.3-r2
- (no CPE)range: < 10.7.1-r2
- (no CPE)range: < 10.7.1-r2
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: <= 1.10.14
- (no CPE)range: < 2.5.0
- (no CPE)range: < 0.0.20250612T141001-1.1
- blevesearch/blevev5Range: >= 0.1.0, < 2.5.0
Patches
2af9e3111dadfRemove http/ from bleve for CVE-2022-31022 (#2156)
16 files changed · +0 −1838
http/alias.go+0 −70 removed@@ -1,70 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "encoding/json" - "fmt" - "io" - "net/http" -) - -type AliasAction struct { - Alias string `json:"alias"` - AddIndexes []string `json:"add"` - RemoveIndexes []string `json:"remove"` -} - -type AliasHandler struct{} - -func NewAliasHandler() *AliasHandler { - return &AliasHandler{} -} - -func (h *AliasHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - - // read the request body - requestBody, err := io.ReadAll(req.Body) - if err != nil { - showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400) - return - } - - var aliasAction AliasAction - // interpret request body as alias actions - if len(requestBody) > 0 { - err := json.Unmarshal(requestBody, &aliasAction) - if err != nil { - showError(w, req, fmt.Sprintf("error parsing alias actions: %v", err), 400) - return - } - } else { - showError(w, req, "request body must contain alias actions", 400) - return - } - - err = UpdateAlias(aliasAction.Alias, aliasAction.AddIndexes, aliasAction.RemoveIndexes) - if err != nil { - showError(w, req, fmt.Sprintf("error updating alias: %v", err), 400) - return - } - - rv := struct { - Status string `json:"status"` - }{ - Status: "ok", - } - mustEncode(w, rv) -}
http/debug.go+0 −100 removed@@ -1,100 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "net/http" - - "github.com/blevesearch/bleve/v2/index/upsidedown" -) - -// DebugDocumentHandler allows you to debug the index content -// for a given document id. -type DebugDocumentHandler struct { - defaultIndexName string - IndexNameLookup varLookupFunc - DocIDLookup varLookupFunc -} - -func NewDebugDocumentHandler(defaultIndexName string) *DebugDocumentHandler { - return &DebugDocumentHandler{ - defaultIndexName: defaultIndexName, - } -} - -func (h *DebugDocumentHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - - // find the index to operate on - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - indexName = h.defaultIndexName - } - index := IndexByName(indexName) - if index == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - // find the docID - var docID string - if h.DocIDLookup != nil { - docID = h.DocIDLookup(req) - } - - internalIndex, err := index.Advanced() - if err != nil { - showError(w, req, fmt.Sprintf("error getting index: %v", err), 500) - return - } - internalIndexReader, err := internalIndex.Reader() - if err != nil { - showError(w, req, fmt.Sprintf("error operning index reader: %v", err), 500) - return - } - upsideDownReader, ok := internalIndexReader.(*upsidedown.IndexReader) - if !ok { - showError(w, req, fmt.Sprintf("dump is only supported by index type upsidedown"), 500) - return - } - - var rv []interface{} - rowChan := upsideDownReader.DumpDoc(docID) - for row := range rowChan { - switch row := row.(type) { - case error: - showError(w, req, fmt.Sprintf("error debugging document: %v", row), 500) - return - case upsidedown.UpsideDownCouchRow: - tmp := struct { - Key []byte `json:"key"` - Val []byte `json:"val"` - }{ - Key: row.Key(), - Val: row.Value(), - } - rv = append(rv, tmp) - } - } - err = internalIndexReader.Close() - if err != nil { - showError(w, req, fmt.Sprintf("error closing index reader: %v", err), 500) - return - } - mustEncode(w, rv) -}
http/doc_count.go+0 −61 removed@@ -1,61 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "net/http" -) - -type DocCountHandler struct { - defaultIndexName string - IndexNameLookup varLookupFunc -} - -func NewDocCountHandler(defaultIndexName string) *DocCountHandler { - return &DocCountHandler{ - defaultIndexName: defaultIndexName, - } -} - -func (h *DocCountHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // find the index to operate on - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - indexName = h.defaultIndexName - } - index := IndexByName(indexName) - if index == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - docCount, err := index.DocCount() - if err != nil { - showError(w, req, fmt.Sprintf("error counting docs: %v", err), 500) - return - } - rv := struct { - Status string `json:"status"` - Count uint64 `json:"count"` - }{ - Status: "ok", - Count: docCount, - } - mustEncode(w, rv) -}
http/doc_delete.go+0 −72 removed@@ -1,72 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "net/http" -) - -type DocDeleteHandler struct { - defaultIndexName string - IndexNameLookup varLookupFunc - DocIDLookup varLookupFunc -} - -func NewDocDeleteHandler(defaultIndexName string) *DocDeleteHandler { - return &DocDeleteHandler{ - defaultIndexName: defaultIndexName, - } -} - -func (h *DocDeleteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - - // find the index to operate on - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - indexName = h.defaultIndexName - } - index := IndexByName(indexName) - if index == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - // find the doc id - var docID string - if h.DocIDLookup != nil { - docID = h.DocIDLookup(req) - } - if docID == "" { - showError(w, req, "document id cannot be empty", 400) - return - } - - err := index.Delete(docID) - if err != nil { - showError(w, req, fmt.Sprintf("error deleting document '%s': %v", docID, err), 500) - return - } - - rv := struct { - Status string `json:"status"` - }{ - Status: "ok", - } - mustEncode(w, rv) -}
http/doc_get.go+0 −137 removed@@ -1,137 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "net/http" - "strconv" - "time" - - "github.com/blevesearch/bleve/v2/analysis/datetime/timestamp/microseconds" - "github.com/blevesearch/bleve/v2/analysis/datetime/timestamp/milliseconds" - "github.com/blevesearch/bleve/v2/analysis/datetime/timestamp/nanoseconds" - "github.com/blevesearch/bleve/v2/analysis/datetime/timestamp/seconds" - index "github.com/blevesearch/bleve_index_api" -) - -type DocGetHandler struct { - defaultIndexName string - IndexNameLookup varLookupFunc - DocIDLookup varLookupFunc -} - -func NewDocGetHandler(defaultIndexName string) *DocGetHandler { - return &DocGetHandler{ - defaultIndexName: defaultIndexName, - } -} - -func (h *DocGetHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // find the index to operate on - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - indexName = h.defaultIndexName - } - idx := IndexByName(indexName) - if idx == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - // find the doc id - var docID string - if h.DocIDLookup != nil { - docID = h.DocIDLookup(req) - } - if docID == "" { - showError(w, req, "document id cannot be empty", 400) - return - } - - doc, err := idx.Document(docID) - if err != nil { - showError(w, req, fmt.Sprintf("error deleting document '%s': %v", docID, err), 500) - return - } - if doc == nil { - showError(w, req, fmt.Sprintf("no such document '%s'", docID), 404) - return - } - - rv := struct { - ID string `json:"id"` - Fields map[string]interface{} `json:"fields"` - }{ - ID: docID, - Fields: map[string]interface{}{}, - } - - doc.VisitFields(func(field index.Field) { - var newval interface{} - switch field := field.(type) { - case index.TextField: - newval = field.Text() - case index.NumericField: - n, err := field.Number() - if err == nil { - newval = n - } - case index.DateTimeField: - d, layout, err := field.DateTime() - if err == nil { - if layout == "" { - // missing layout means we fallback to - // the default layout which is RFC3339 - newval = d.Format(time.RFC3339) - } else { - // the layout here can now either be representative - // of an actual layout or a timestamp - switch layout { - case seconds.Name: - newval = strconv.FormatInt(d.Unix(), 10) - case milliseconds.Name: - newval = strconv.FormatInt(d.UnixMilli(), 10) - case microseconds.Name: - newval = strconv.FormatInt(d.UnixMicro(), 10) - case nanoseconds.Name: - newval = strconv.FormatInt(d.UnixNano(), 10) - default: - newval = d.Format(layout) - } - } - } - } - existing, existed := rv.Fields[field.Name()] - if existed { - switch existing := existing.(type) { - case []interface{}: - rv.Fields[field.Name()] = append(existing, newval) - case interface{}: - arr := make([]interface{}, 2) - arr[0] = existing - arr[1] = newval - rv.Fields[field.Name()] = arr - } - } else { - rv.Fields[field.Name()] = newval - } - }) - - mustEncode(w, rv) -}
http/doc_index.go+0 −89 removed@@ -1,89 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "encoding/json" - "fmt" - "io" - "net/http" -) - -type DocIndexHandler struct { - defaultIndexName string - IndexNameLookup varLookupFunc - DocIDLookup varLookupFunc -} - -func NewDocIndexHandler(defaultIndexName string) *DocIndexHandler { - return &DocIndexHandler{ - defaultIndexName: defaultIndexName, - } -} - -func (h *DocIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - - // find the index to operate on - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - indexName = h.defaultIndexName - } - index := IndexByName(indexName) - if index == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - // find the doc id - var docID string - if h.DocIDLookup != nil { - docID = h.DocIDLookup(req) - } - if docID == "" { - showError(w, req, "document id cannot be empty", 400) - return - } - - // read the request body - requestBody, err := io.ReadAll(req.Body) - if err != nil { - showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400) - return - } - - // parse request body as json - var doc interface{} - err = json.Unmarshal(requestBody, &doc) - if err != nil { - showError(w, req, fmt.Sprintf("error parsing request body as JSON: %v", err), 400) - return - } - - err = index.Index(docID, doc) - if err != nil { - showError(w, req, fmt.Sprintf("error indexing document '%s': %v", docID, err), 500) - return - } - - rv := struct { - Status string `json:"status"` - }{ - Status: "ok", - } - mustEncode(w, rv) -}
http/fields.go+0 −63 removed@@ -1,63 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "net/http" -) - -type ListFieldsHandler struct { - defaultIndexName string - IndexNameLookup varLookupFunc -} - -func NewListFieldsHandler(defaultIndexName string) *ListFieldsHandler { - return &ListFieldsHandler{ - defaultIndexName: defaultIndexName, - } -} - -func (h *ListFieldsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - - // find the index to operate on - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - indexName = h.defaultIndexName - } - index := IndexByName(indexName) - if index == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - fields, err := index.Fields() - if err != nil { - showError(w, req, fmt.Sprintf("error: %v", err), 500) - return - } - - fieldsResponse := struct { - Fields []string `json:"fields"` - }{ - Fields: fields, - } - - // encode the response - mustEncode(w, fieldsResponse) -}
http/handlers_test.go+0 −702 removed@@ -1,702 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "bytes" - "io" - "net/http" - "net/http/httptest" - "net/url" - "os" - "reflect" - "testing" -) - -func docIDLookup(req *http.Request) string { - return req.FormValue("docID") -} - -func indexNameLookup(req *http.Request) string { - return req.FormValue("indexName") -} - -func TestHandlers(t *testing.T) { - - basePath := "testbase" - err := os.MkdirAll(basePath, 0700) - if err != nil { - t.Fatal(err) - } - defer func() { - err := os.RemoveAll(basePath) - if err != nil { - t.Fatal(err) - } - }() - - createIndexHandler := NewCreateIndexHandler(basePath) - createIndexHandler.IndexNameLookup = indexNameLookup - - getIndexHandler := NewGetIndexHandler() - getIndexHandler.IndexNameLookup = indexNameLookup - - deleteIndexHandler := NewDeleteIndexHandler(basePath) - deleteIndexHandler.IndexNameLookup = indexNameLookup - - listIndexesHandler := NewListIndexesHandler() - - docIndexHandler := NewDocIndexHandler("") - docIndexHandler.IndexNameLookup = indexNameLookup - docIndexHandler.DocIDLookup = docIDLookup - - docCountHandler := NewDocCountHandler("") - docCountHandler.IndexNameLookup = indexNameLookup - - docGetHandler := NewDocGetHandler("") - docGetHandler.IndexNameLookup = indexNameLookup - docGetHandler.DocIDLookup = docIDLookup - - docDeleteHandler := NewDocDeleteHandler("") - docDeleteHandler.IndexNameLookup = indexNameLookup - docDeleteHandler.DocIDLookup = docIDLookup - - searchHandler := NewSearchHandler("") - searchHandler.IndexNameLookup = indexNameLookup - - listFieldsHandler := NewListFieldsHandler("") - listFieldsHandler.IndexNameLookup = indexNameLookup - - debugHandler := NewDebugDocumentHandler("") - debugHandler.IndexNameLookup = indexNameLookup - debugHandler.DocIDLookup = docIDLookup - - aliasHandler := NewAliasHandler() - - tests := []struct { - Desc string - Handler http.Handler - Path string - Method string - Params url.Values - Body []byte - Status int - ResponseBody []byte - ResponseMatch map[string]bool - }{ - { - Desc: "create index", - Handler: createIndexHandler, - Path: "/create", - Method: "PUT", - Params: url.Values{"indexName": []string{"ti1"}}, - Body: []byte("{}"), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "create existing index", - Handler: createIndexHandler, - Path: "/create", - Method: "PUT", - Params: url.Values{"indexName": []string{"ti1"}}, - Body: []byte("{}"), - Status: http.StatusInternalServerError, - ResponseMatch: map[string]bool{ - `path already exists`: true, - }, - }, - { - Desc: "create index missing index", - Handler: createIndexHandler, - Path: "/create", - Method: "PUT", - Body: []byte("{}"), - Status: http.StatusBadRequest, - ResponseBody: []byte(`index name is required`), - }, - { - Desc: "create index invalid json", - Handler: createIndexHandler, - Path: "/create", - Method: "PUT", - Params: url.Values{"indexName": []string{"ti9"}}, - Body: []byte("{"), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `error parsing index mapping`: true, - }, - }, - { - Desc: "get index", - Handler: getIndexHandler, - Path: "/get", - Method: "GET", - Params: url.Values{"indexName": []string{"ti1"}}, - Status: http.StatusOK, - ResponseMatch: map[string]bool{ - `"status":"ok"`: true, - `"name":"ti1"`: true, - }, - }, - { - Desc: "get index does not exist", - Handler: getIndexHandler, - Path: "/get", - Method: "GET", - Params: url.Values{"indexName": []string{"dne"}}, - Status: http.StatusNotFound, - ResponseMatch: map[string]bool{ - `no such index`: true, - }, - }, - { - Desc: "get index missing name", - Handler: getIndexHandler, - Path: "/get", - Method: "GET", - Status: http.StatusBadRequest, - ResponseBody: []byte(`index name is required`), - }, - { - Desc: "create another index", - Handler: createIndexHandler, - Path: "/create", - Method: "PUT", - Params: url.Values{"indexName": []string{"ti2"}}, - Body: []byte("{}"), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "list indexes", - Handler: listIndexesHandler, - Path: "/list", - Method: "GET", - Status: http.StatusOK, - ResponseMatch: map[string]bool{ - `"status":"ok"`: true, - `"ti1"`: true, - `"ti2"`: true, - }, - }, - { - Desc: "delete index", - Handler: deleteIndexHandler, - Path: "/delete", - Method: "DELETE", - Params: url.Values{"indexName": []string{"ti2"}}, - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "delete index missing name", - Handler: deleteIndexHandler, - Path: "/delete", - Method: "DELETE", - Status: http.StatusBadRequest, - ResponseBody: []byte(`index name is required`), - }, - { - Desc: "list indexes after delete", - Handler: listIndexesHandler, - Path: "/list", - Method: "GET", - Status: http.StatusOK, - ResponseMatch: map[string]bool{ - `"status":"ok"`: true, - `"ti1"`: true, - `"ti2"`: false, - }, - }, - { - Desc: "index doc", - Handler: docIndexHandler, - Path: "/ti1/a", - Method: "PUT", - Params: url.Values{ - "indexName": []string{"ti1"}, - "docID": []string{"a"}, - }, - Body: []byte(`{"name":"a","body":"test","rating":7,"created":"2014-11-26","former_ratings":[3,4,2]}`), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "index doc invalid index", - Handler: docIndexHandler, - Path: "/tix/a", - Method: "PUT", - Params: url.Values{ - "indexName": []string{"tix"}, - "docID": []string{"a"}, - }, - Body: []byte(`{"name":"a","body":"test","rating":7,"created":"2014-11-26","former_ratings":[3,4,2]}`), - Status: http.StatusNotFound, - ResponseBody: []byte(`no such index 'tix'`), - }, - { - Desc: "index doc missing ID", - Handler: docIndexHandler, - Path: "/ti1/a", - Method: "PUT", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Body: []byte(`{"name":"a","body":"test","rating":7,"created":"2014-11-26","former_ratings":[3,4,2]}`), - Status: http.StatusBadRequest, - ResponseBody: []byte(`document id cannot be empty`), - }, - { - Desc: "doc count", - Handler: docCountHandler, - Path: "/ti1/count", - Method: "GET", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok","count":1}`), - }, - { - Desc: "doc count invalid index", - Handler: docCountHandler, - Path: "/tix/count", - Method: "GET", - Params: url.Values{ - "indexName": []string{"tix"}, - }, - Status: http.StatusNotFound, - ResponseBody: []byte(`no such index 'tix'`), - }, - { - Desc: "doc get", - Handler: docGetHandler, - Path: "/ti1/a", - Method: "GET", - Params: url.Values{ - "indexName": []string{"ti1"}, - "docID": []string{"a"}, - }, - Status: http.StatusOK, - ResponseMatch: map[string]bool{ - `"id":"a"`: true, - `"body":"test"`: true, - `"name":"a"`: true, - }, - }, - { - Desc: "doc get invalid index", - Handler: docGetHandler, - Path: "/tix/a", - Method: "GET", - Params: url.Values{ - "indexName": []string{"tix"}, - "docID": []string{"a"}, - }, - Status: http.StatusNotFound, - ResponseBody: []byte(`no such index 'tix'`), - }, - { - Desc: "doc get missing ID", - Handler: docGetHandler, - Path: "/ti1/a", - Method: "GET", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Status: http.StatusBadRequest, - ResponseBody: []byte(`document id cannot be empty`), - }, - { - Desc: "index another doc", - Handler: docIndexHandler, - Path: "/ti1/b", - Method: "PUT", - Params: url.Values{ - "indexName": []string{"ti1"}, - "docID": []string{"b"}, - }, - Body: []byte(`{"name":"b","body":"del"}`), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "doc count again", - Handler: docCountHandler, - Path: "/ti1/count", - Method: "GET", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok","count":2}`), - }, - { - Desc: "delete doc", - Handler: docDeleteHandler, - Path: "/ti1/b", - Method: "DELETE", - Params: url.Values{ - "indexName": []string{"ti1"}, - "docID": []string{"b"}, - }, - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "delete doc invalid index", - Handler: docDeleteHandler, - Path: "/tix/b", - Method: "DELETE", - Params: url.Values{ - "indexName": []string{"tix"}, - "docID": []string{"b"}, - }, - Status: http.StatusNotFound, - ResponseBody: []byte(`no such index 'tix'`), - }, - { - Desc: "delete doc missing docID", - Handler: docDeleteHandler, - Path: "/ti1/b", - Method: "DELETE", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Status: http.StatusBadRequest, - ResponseBody: []byte(`document id cannot be empty`), - }, - { - Desc: "doc get", - Handler: docGetHandler, - Path: "/ti1/b", - Method: "GET", - Params: url.Values{ - "indexName": []string{"ti1"}, - "docID": []string{"b"}, - }, - Status: http.StatusNotFound, - ResponseMatch: map[string]bool{ - `no such document`: true, - }, - }, - { - Desc: "search", - Handler: searchHandler, - Path: "/ti1/search", - Method: "POST", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Body: []byte(`{ - "from": 0, - "size": 10, - "query": { - "fuzziness": 0, - "prefix_length": 0, - "field": "body", - "match": "test" - } - }`), - Status: http.StatusOK, - ResponseMatch: map[string]bool{ - `"total_hits":1`: true, - `"id":"a"`: true, - }, - }, - { - Desc: "search index doesn't exist", - Handler: searchHandler, - Path: "/tix/search", - Method: "POST", - Params: url.Values{ - "indexName": []string{"tix"}, - }, - Body: []byte(`{ - "from": 0, - "size": 10, - "query": { - "fuzziness": 0, - "prefix_length": 0, - "field": "body", - "match": "test" - } - }`), - Status: http.StatusNotFound, - ResponseBody: []byte(`no such index 'tix'`), - }, - { - Desc: "search invalid json", - Handler: searchHandler, - Path: "/ti1/search", - Method: "POST", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Body: []byte(`{`), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `error parsing query`: true, - }, - }, - { - Desc: "search query does not validate", - Handler: searchHandler, - Path: "/ti1/search", - Method: "POST", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Body: []byte(`{ - "from": 0, - "size": 10, - "query": { - "field": "body", - "terms": [] - } - }`), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `error validating query`: true, - }, - }, - { - Desc: "list fields", - Handler: listFieldsHandler, - Path: "/ti1/fields", - Method: "GET", - Params: url.Values{ - "indexName": []string{"ti1"}, - }, - Status: http.StatusOK, - ResponseMatch: map[string]bool{ - `"fields":`: true, - `"name"`: true, - `"body"`: true, - `"_all"`: true, - }, - }, - { - Desc: "list fields invalid index", - Handler: listFieldsHandler, - Path: "/tix/fields", - Method: "GET", - Params: url.Values{ - "indexName": []string{"tix"}, - }, - Status: http.StatusNotFound, - ResponseBody: []byte(`no such index 'tix'`), - }, - { - Desc: "create alias", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "a1", - "add": ["ti1"] - }`), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "create alias invalid json", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{`), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `error parsing alias actions`: true, - }, - }, - { - Desc: "create alias empty", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(``), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `request body must contain alias actions`: true, - }, - }, - { - Desc: "create alias referring to non-existent index", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "a2", - "add": ["tix"] - }`), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `index named 'tix' does not exist`: true, - }, - }, - { - Desc: "create alias removing from new", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "a2", - "remove": ["ti1"] - }`), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `cannot remove indexes from a new alias`: true, - }, - }, - { - Desc: "create alias same name as index", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "ti1", - "remove": ["ti1"] - }`), - Status: http.StatusBadRequest, - ResponseMatch: map[string]bool{ - `is not an alias`: true, - }, - }, - { - Desc: "search alias", - Handler: searchHandler, - Path: "/a1/search", - Method: "POST", - Params: url.Values{ - "indexName": []string{"a1"}, - }, - Body: []byte(`{ - "from": 0, - "size": 10, - "query": { - "fuzziness": 0, - "prefix_length": 0, - "field": "body", - "match": "test" - } - }`), - Status: http.StatusOK, - ResponseMatch: map[string]bool{ - `"total_hits":1`: true, - `"id":"a"`: true, - }, - }, - { - Desc: "create index to add to alias", - Handler: createIndexHandler, - Path: "/create", - Method: "PUT", - Params: url.Values{"indexName": []string{"ti6"}}, - Body: []byte("{}"), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "update alias add ti6", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "a1", - "add": ["ti6"] - }`), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "update alias add doesn't exist", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "a1", - "add": ["ti99"] - }`), - Status: http.StatusBadRequest, - ResponseBody: []byte(`error updating alias: index named 'ti99' does not exist`), - }, - { - Desc: "update alias remove ti6", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "a1", - "remove": ["ti6"] - }`), - Status: http.StatusOK, - ResponseBody: []byte(`{"status":"ok"}`), - }, - { - Desc: "update alias remove doesn't exist", - Handler: aliasHandler, - Path: "/alias", - Method: "POST", - Body: []byte(`{ - "alias": "a1", - "remove": ["ti98"] - }`), - Status: http.StatusBadRequest, - ResponseBody: []byte(`error updating alias: index named 'ti98' does not exist`), - }, - } - - for _, test := range tests { - record := httptest.NewRecorder() - req := &http.Request{ - Method: test.Method, - URL: &url.URL{Path: test.Path}, - Form: test.Params, - Body: io.NopCloser(bytes.NewBuffer(test.Body)), - } - test.Handler.ServeHTTP(record, req) - if got, want := record.Code, test.Status; got != want { - t.Errorf("%s: response code = %d, want %d", test.Desc, got, want) - t.Errorf("%s: response body = %s", test.Desc, record.Body) - } - - got := bytes.TrimRight(record.Body.Bytes(), "\n") - if test.ResponseBody != nil { - if !reflect.DeepEqual(got, test.ResponseBody) { - t.Errorf("%s: expected: '%s', got: '%s'", test.Desc, test.ResponseBody, got) - } - } - for pattern, shouldMatch := range test.ResponseMatch { - didMatch := bytes.Contains(got, []byte(pattern)) - if didMatch != shouldMatch { - t.Errorf("%s: expected match %t for pattern %s, got %t", test.Desc, shouldMatch, pattern, didMatch) - t.Errorf("%s: response body was: %s", test.Desc, got) - } - } - } - - // close indexes - for _, indexName := range IndexNames() { - index := UnregisterIndexByName(indexName) - if index != nil { - err := index.Close() - if err != nil { - t.Errorf("error closing index %s: %v", indexName, err) - } - } - } -}
http/index_create.go+0 −84 removed@@ -1,84 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "os" - - "github.com/blevesearch/bleve/v2" -) - -type CreateIndexHandler struct { - basePath string - IndexNameLookup varLookupFunc -} - -func NewCreateIndexHandler(basePath string) *CreateIndexHandler { - return &CreateIndexHandler{ - basePath: basePath, - } -} - -func (h *CreateIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // find the name of the index to create - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - showError(w, req, "index name is required", 400) - return - } - - indexMapping := bleve.NewIndexMapping() - - // read the request body - requestBody, err := io.ReadAll(req.Body) - if err != nil { - showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400) - return - } - - // interpret request body as index mapping - if len(requestBody) > 0 { - err := json.Unmarshal(requestBody, &indexMapping) - if err != nil { - showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400) - return - } - } - - newIndex, err := bleve.New(h.indexPath(indexName), indexMapping) - if err != nil { - showError(w, req, fmt.Sprintf("error creating index: %v", err), 500) - return - } - newIndex.SetName(indexName) - RegisterIndexName(indexName, newIndex) - rv := struct { - Status string `json:"status"` - }{ - Status: "ok", - } - mustEncode(w, rv) -} - -func (h *CreateIndexHandler) indexPath(name string) string { - return h.basePath + string(os.PathSeparator) + name -}
http/index_delete.go+0 −75 removed@@ -1,75 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "net/http" - "os" -) - -type DeleteIndexHandler struct { - basePath string - IndexNameLookup varLookupFunc -} - -func NewDeleteIndexHandler(basePath string) *DeleteIndexHandler { - return &DeleteIndexHandler{ - basePath: basePath, - } -} - -func (h *DeleteIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // find the name of the index to delete - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - showError(w, req, "index name is required", 400) - return - } - - indexToDelete := UnregisterIndexByName(indexName) - if indexToDelete == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - // close the index - err := indexToDelete.Close() - if err != nil { - showError(w, req, fmt.Sprintf("error closing index: %v", err), 500) - return - } - - // now delete it - err = os.RemoveAll(h.indexPath(indexName)) - if err != nil { - showError(w, req, fmt.Sprintf("error deleting index: %v", err), 500) - return - } - - rv := struct { - Status string `json:"status"` - }{ - Status: "ok", - } - mustEncode(w, rv) -} - -func (h *DeleteIndexHandler) indexPath(name string) string { - return h.basePath + string(os.PathSeparator) + name -}
http/index_get.go+0 −59 removed@@ -1,59 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "net/http" - - "github.com/blevesearch/bleve/v2/mapping" -) - -type GetIndexHandler struct { - IndexNameLookup varLookupFunc -} - -func NewGetIndexHandler() *GetIndexHandler { - return &GetIndexHandler{} -} - -func (h *GetIndexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // find the name of the index to create - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - showError(w, req, "index name is required", 400) - return - } - - index := IndexByName(indexName) - if index == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - rv := struct { - Status string `json:"status"` - Name string `json:"name"` - Mapping mapping.IndexMapping `json:"mapping"` - }{ - Status: "ok", - Name: indexName, - Mapping: index.Mapping(), - } - mustEncode(w, rv) -}
http/index_list.go+0 −38 removed@@ -1,38 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "net/http" -) - -type ListIndexesHandler struct { -} - -func NewListIndexesHandler() *ListIndexesHandler { - return &ListIndexesHandler{} -} - -func (h *ListIndexesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - indexNames := IndexNames() - rv := struct { - Status string `json:"status"` - Indexes []string `json:"indexes"` - }{ - Status: "ok", - Indexes: indexNames, - } - mustEncode(w, rv) -}
http/README.md+0 −12 removed@@ -1,12 +0,0 @@ -# http support in bleve - -This http package is purely for the demonstration purposes and is used by sample -applications like https://github.com/blevesearch/bleve-explorer. - -Please be aware that the http handler implementations neither provide exhaustive -user input validations nor authentication or authorization of the user access. - -So, it is strongly recommended that users exercise caution while using the http -package in production situations. - -[Here](https://github.com/blevesearch/bleve/security/advisories/GHSA-9w9f-6mg8-jp7w) is the security advisory on this.
http/registry.go+0 −118 removed@@ -1,118 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "fmt" - "sync" - - "github.com/blevesearch/bleve/v2" -) - -var indexNameMapping map[string]bleve.Index -var indexNameMappingLock sync.RWMutex - -func RegisterIndexName(name string, idx bleve.Index) { - indexNameMappingLock.Lock() - defer indexNameMappingLock.Unlock() - - if indexNameMapping == nil { - indexNameMapping = make(map[string]bleve.Index) - } - indexNameMapping[name] = idx -} - -func UnregisterIndexByName(name string) bleve.Index { - indexNameMappingLock.Lock() - defer indexNameMappingLock.Unlock() - - if indexNameMapping == nil { - return nil - } - rv := indexNameMapping[name] - if rv != nil { - delete(indexNameMapping, name) - } - return rv -} - -func IndexByName(name string) bleve.Index { - indexNameMappingLock.RLock() - defer indexNameMappingLock.RUnlock() - - return indexNameMapping[name] -} - -func IndexNames() []string { - indexNameMappingLock.RLock() - defer indexNameMappingLock.RUnlock() - - rv := make([]string, len(indexNameMapping)) - count := 0 - for k := range indexNameMapping { - rv[count] = k - count++ - } - return rv -} - -func UpdateAlias(alias string, add, remove []string) error { - indexNameMappingLock.Lock() - defer indexNameMappingLock.Unlock() - - index, exists := indexNameMapping[alias] - if !exists { - // new alias - if len(remove) > 0 { - return fmt.Errorf("cannot remove indexes from a new alias") - } - indexes := make([]bleve.Index, len(add)) - for i, addIndexName := range add { - addIndex, indexExists := indexNameMapping[addIndexName] - if !indexExists { - return fmt.Errorf("index named '%s' does not exist", addIndexName) - } - indexes[i] = addIndex - } - indexAlias := bleve.NewIndexAlias(indexes...) - indexNameMapping[alias] = indexAlias - } else { - // something with this name already exists - indexAlias, isAlias := index.(bleve.IndexAlias) - if !isAlias { - return fmt.Errorf("'%s' is not an alias", alias) - } - // build list of add indexes - addIndexes := make([]bleve.Index, len(add)) - for i, addIndexName := range add { - addIndex, indexExists := indexNameMapping[addIndexName] - if !indexExists { - return fmt.Errorf("index named '%s' does not exist", addIndexName) - } - addIndexes[i] = addIndex - } - // build list of remove indexes - removeIndexes := make([]bleve.Index, len(remove)) - for i, removeIndexName := range remove { - removeIndex, indexExists := indexNameMapping[removeIndexName] - if !indexExists { - return fmt.Errorf("index named '%s' does not exist", removeIndexName) - } - removeIndexes[i] = removeIndex - } - indexAlias.Swap(addIndexes, removeIndexes) - } - return nil -}
http/search.go+0 −108 removed@@ -1,108 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "time" - - "github.com/blevesearch/bleve/v2" - "github.com/blevesearch/bleve/v2/search/query" -) - -// SearchHandler can handle search requests sent over HTTP -type SearchHandler struct { - defaultIndexName string - IndexNameLookup varLookupFunc -} - -func NewSearchHandler(defaultIndexName string) *SearchHandler { - return &SearchHandler{ - defaultIndexName: defaultIndexName, - } -} - -func (h *SearchHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - - // find the index to operate on - var indexName string - if h.IndexNameLookup != nil { - indexName = h.IndexNameLookup(req) - } - if indexName == "" { - indexName = h.defaultIndexName - } - index := IndexByName(indexName) - if index == nil { - showError(w, req, fmt.Sprintf("no such index '%s'", indexName), 404) - return - } - - // read the request body - requestBody, err := io.ReadAll(req.Body) - if err != nil { - showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400) - return - } - - logger.Printf("request body: %s", requestBody) - - // parse the request - var searchRequest bleve.SearchRequest - err = json.Unmarshal(requestBody, &searchRequest) - if err != nil { - showError(w, req, fmt.Sprintf("error parsing query: %v", err), 400) - return - } - - logger.Printf("parsed request %#v", searchRequest) - - // validate the query - if srqv, ok := searchRequest.Query.(query.ValidatableQuery); ok { - err = srqv.Validate() - if err != nil { - showError(w, req, fmt.Sprintf("error validating query: %v", err), 400) - return - } - } - - // check for timeout and create context - var ctx context.Context - timeoutStr := req.FormValue("timeout") - if timeoutStr == "" { - ctx = context.Background() - } else { - timeout, err := time.ParseDuration(timeoutStr) - if err != nil { - showError(w, req, fmt.Sprintf("error parsing timeout value: %v", err), 400) - return - } - ctx, _ = context.WithTimeout(context.Background(), timeout) - } - - // execute the query - searchResponse, err := index.SearchInContext(ctx, &searchRequest) - if err != nil { - showError(w, req, fmt.Sprintf("error executing query: %v", err), 500) - return - } - - // encode the response - mustEncode(w, searchResponse) -}
http/util.go+0 −50 removed@@ -1,50 +0,0 @@ -// Copyright (c) 2014 Couchbase, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package http - -import ( - "encoding/json" - "io" - "log" - "net/http" -) - -func showError(w http.ResponseWriter, r *http.Request, - msg string, code int) { - logger.Printf("Reporting error %v/%v", code, msg) - http.Error(w, msg, code) -} - -func mustEncode(w io.Writer, i interface{}) { - if headered, ok := w.(http.ResponseWriter); ok { - headered.Header().Set("Cache-Control", "no-cache") - headered.Header().Set("Content-type", "application/json") - } - - e := json.NewEncoder(w) - if err := e.Encode(i); err != nil { - panic(err) - } -} - -type varLookupFunc func(req *http.Request) string - -var logger = log.New(io.Discard, "bleve.http", log.LstdFlags) - -// SetLog sets the logger used for logging -// by default log messages are sent to io.Discard -func SetLog(l *log.Logger) { - logger = l -}
1c7509d6a17dLink security advisory to README (#1694)
1 file changed · +6 −4
http/README.md+6 −4 modified@@ -3,8 +3,10 @@ This http package is purely for the demonstration purposes and is used by sample applications like https://github.com/blevesearch/bleve-explorer. -Please be aware that the http handler implementations neither provide exhaustive -user input validations nor authentication or authorization of the user access. +Please be aware that the http handler implementations neither provide exhaustive +user input validations nor authentication or authorization of the user access. -So, it is recommended to remain cautious against the use of the http package in -production use cases. \ No newline at end of file +So, it is strongly recommended that users exercise caution while using the http +package in production situations. + +[Here](https://github.com/blevesearch/bleve/security/advisories/GHSA-9w9f-6mg8-jp7w) is the security advisory on this.
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-9w9f-6mg8-jp7wghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-31022ghsaADVISORY
- github.com/blevesearch/bleveghsaPACKAGE
- github.com/blevesearch/bleve/commit/1c7509d6a17d36f265c90b4e8f4e3a3182fe79ffghsax_refsource_MISCWEB
- github.com/blevesearch/bleve/commit/af9e3111dadfedf9d30f0448506b4a57fecc8550ghsax_refsource_MISCWEB
- github.com/blevesearch/bleve/security/advisories/GHSA-9w9f-6mg8-jp7wghsax_refsource_CONFIRMWEB
- pkg.go.dev/vuln/GO-2022-0470ghsaWEB
News mentions
0No linked articles in our index yet.