mirror of https://github.com/milvus-io/milvus.git
feat: restful phase two (#29728)
issue: #29732 Signed-off-by: PowderLi <min.li@zilliz.com>pull/30332/head
parent
8fc4ebfa11
commit
6abbab12fa
|
@ -1,5 +1,45 @@
|
||||||
package httpserver
|
package httpserver
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// v2
|
||||||
|
const (
|
||||||
|
// --- category ---
|
||||||
|
CollectionCategory = "/collections/"
|
||||||
|
EntityCategory = "/entities/"
|
||||||
|
PartitionCategory = "/partitions/"
|
||||||
|
UserCategory = "/users/"
|
||||||
|
RoleCategory = "/roles/"
|
||||||
|
IndexCategory = "/indexes/"
|
||||||
|
AliasCategory = "/aliases/"
|
||||||
|
JobCategory = "/jobs/"
|
||||||
|
|
||||||
|
ListAction = "list"
|
||||||
|
HasAction = "has"
|
||||||
|
DescribeAction = "describe"
|
||||||
|
CreateAction = "create"
|
||||||
|
DropAction = "drop"
|
||||||
|
StatsAction = "get_stats"
|
||||||
|
LoadStateAction = "get_load_state"
|
||||||
|
RenameAction = "rename"
|
||||||
|
LoadAction = "load"
|
||||||
|
ReleaseAction = "release"
|
||||||
|
QueryAction = "query"
|
||||||
|
GetAction = "get"
|
||||||
|
DeleteAction = "delete"
|
||||||
|
InsertAction = "insert"
|
||||||
|
UpsertAction = "upsert"
|
||||||
|
SearchAction = "search"
|
||||||
|
|
||||||
|
UpdatePasswordAction = "update_password"
|
||||||
|
GrantRoleAction = "grant_role"
|
||||||
|
RevokeRoleAction = "revoke_role"
|
||||||
|
GrantPrivilegeAction = "grant_privilege"
|
||||||
|
RevokePrivilegeAction = "revoke_privilege"
|
||||||
|
AlterAction = "alter"
|
||||||
|
GetProgressAction = "get_progress"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ContextUsername = "username"
|
ContextUsername = "username"
|
||||||
VectorCollectionsPath = "/vector/collections"
|
VectorCollectionsPath = "/vector/collections"
|
||||||
|
@ -15,19 +55,36 @@ const (
|
||||||
|
|
||||||
ShardNumDefault = 1
|
ShardNumDefault = 1
|
||||||
|
|
||||||
|
PrimaryFieldName = "id"
|
||||||
|
VectorFieldName = "vector"
|
||||||
|
|
||||||
EnableDynamic = true
|
EnableDynamic = true
|
||||||
EnableAutoID = true
|
EnableAutoID = true
|
||||||
DisableAutoID = false
|
DisableAutoID = false
|
||||||
|
|
||||||
HTTPCollectionName = "collectionName"
|
HTTPCollectionName = "collectionName"
|
||||||
HTTPDbName = "dbName"
|
HTTPDbName = "dbName"
|
||||||
|
HTTPPartitionName = "partitionName"
|
||||||
|
HTTPPartitionNames = "partitionNames"
|
||||||
|
HTTPUserName = "userName"
|
||||||
|
HTTPRoleName = "roleName"
|
||||||
|
HTTPIndexName = "indexName"
|
||||||
|
HTTPIndexField = "fieldName"
|
||||||
|
HTTPAliasName = "aliasName"
|
||||||
DefaultDbName = "default"
|
DefaultDbName = "default"
|
||||||
DefaultIndexName = "vector_idx"
|
DefaultIndexName = "vector_idx"
|
||||||
|
DefaultAliasName = "the_alias"
|
||||||
DefaultOutputFields = "*"
|
DefaultOutputFields = "*"
|
||||||
HTTPHeaderAllowInt64 = "Accept-Type-Allow-Int64"
|
HTTPHeaderAllowInt64 = "Accept-Type-Allow-Int64"
|
||||||
|
HTTPHeaderRequestTimeout = "Request-Timeout"
|
||||||
|
HTTPDefaultTimeout = 30 * time.Second
|
||||||
HTTPReturnCode = "code"
|
HTTPReturnCode = "code"
|
||||||
HTTPReturnMessage = "message"
|
HTTPReturnMessage = "message"
|
||||||
HTTPReturnData = "data"
|
HTTPReturnData = "data"
|
||||||
|
HTTPReturnLoadState = "loadState"
|
||||||
|
HTTPReturnLoadProgress = "loadProgress"
|
||||||
|
|
||||||
|
HTTPReturnHas = "has"
|
||||||
|
|
||||||
HTTPReturnFieldName = "name"
|
HTTPReturnFieldName = "name"
|
||||||
HTTPReturnFieldType = "type"
|
HTTPReturnFieldType = "type"
|
||||||
|
@ -35,12 +92,24 @@ const (
|
||||||
HTTPReturnFieldAutoID = "autoId"
|
HTTPReturnFieldAutoID = "autoId"
|
||||||
HTTPReturnDescription = "description"
|
HTTPReturnDescription = "description"
|
||||||
|
|
||||||
HTTPReturnIndexName = "indexName"
|
|
||||||
HTTPReturnIndexField = "fieldName"
|
|
||||||
HTTPReturnIndexMetricsType = "metricType"
|
HTTPReturnIndexMetricsType = "metricType"
|
||||||
|
HTTPReturnIndexType = "indexType"
|
||||||
|
HTTPReturnIndexTotalRows = "totalRows"
|
||||||
|
HTTPReturnIndexPendingRows = "pendingRows"
|
||||||
|
HTTPReturnIndexIndexedRows = "indexedRows"
|
||||||
|
HTTPReturnIndexState = "indexState"
|
||||||
|
HTTPReturnIndexFailReason = "failReason"
|
||||||
|
|
||||||
HTTPReturnDistance = "distance"
|
HTTPReturnDistance = "distance"
|
||||||
|
|
||||||
|
HTTPReturnRowCount = "rowCount"
|
||||||
|
|
||||||
|
HTTPReturnObjectType = "objectType"
|
||||||
|
HTTPReturnObjectName = "objectName"
|
||||||
|
HTTPReturnPrivilege = "privilege"
|
||||||
|
HTTPReturnGrantor = "grantor"
|
||||||
|
HTTPReturnDbName = "dbName"
|
||||||
|
|
||||||
DefaultMetricType = "L2"
|
DefaultMetricType = "L2"
|
||||||
DefaultPrimaryFieldName = "id"
|
DefaultPrimaryFieldName = "id"
|
||||||
DefaultVectorFieldName = "vector"
|
DefaultVectorFieldName = "vector"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,285 @@
|
||||||
|
package httpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatabaseReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *DatabaseReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type CollectionNameReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionNames []string `json:"partitionNames"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CollectionNameReq) GetDbName() string {
|
||||||
|
return req.DbName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CollectionNameReq) GetCollectionName() string {
|
||||||
|
return req.CollectionName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CollectionNameReq) GetPartitionNames() []string {
|
||||||
|
return req.PartitionNames
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenameCollectionReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
NewCollectionName string `json:"newCollectionName" binding:"required"`
|
||||||
|
NewDbName string `json:"newDbName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *RenameCollectionReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type PartitionReq struct {
|
||||||
|
// CollectionNameReq
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionName string `json:"partitionName" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *PartitionReq) GetDbName() string { return req.DbName }
|
||||||
|
func (req *PartitionReq) GetCollectionName() string { return req.CollectionName }
|
||||||
|
func (req *PartitionReq) GetPartitionName() string { return req.PartitionName }
|
||||||
|
|
||||||
|
type QueryReqV2 struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionNames []string `json:"partitionNames"`
|
||||||
|
OutputFields []string `json:"outputFields"`
|
||||||
|
Filter string `json:"filter" binding:"required"`
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *QueryReqV2) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type CollectionIDOutputReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionName string `json:"partitionName"`
|
||||||
|
PartitionNames []string `json:"partitionNames"`
|
||||||
|
OutputFields []string `json:"outputFields"`
|
||||||
|
ID interface{} `json:"id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CollectionIDOutputReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type CollectionIDFilterReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionName string `json:"partitionName"`
|
||||||
|
ID interface{} `json:"id"`
|
||||||
|
Filter string `json:"filter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CollectionIDFilterReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type CollectionDataReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionName string `json:"partitionName"`
|
||||||
|
Data []map[string]interface{} `json:"data" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CollectionDataReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type SearchReqV2 struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionNames []string `json:"partitionNames"`
|
||||||
|
Filter string `json:"filter"`
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
OutputFields []string `json:"outputFields"`
|
||||||
|
Vector []float32 `json:"vector"`
|
||||||
|
Params map[string]float64 `json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *SearchReqV2) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type ReturnErrMsg struct {
|
||||||
|
Code int32 `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PartitionsReq struct {
|
||||||
|
// CollectionNameReq
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
PartitionNames []string `json:"partitionNames" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *PartitionsReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type UserReq struct {
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *UserReq) GetUserName() string { return req.UserName }
|
||||||
|
|
||||||
|
type UserNameGetter interface {
|
||||||
|
GetUserName() string
|
||||||
|
}
|
||||||
|
type RoleNameGetter interface {
|
||||||
|
GetRoleName() string
|
||||||
|
}
|
||||||
|
type IndexNameGetter interface {
|
||||||
|
GetIndexName() string
|
||||||
|
}
|
||||||
|
type AliasNameGetter interface {
|
||||||
|
GetAliasName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PasswordReq struct {
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewPasswordReq struct {
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
NewPassword string `json:"newPassword"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRoleReq struct {
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
RoleName string `json:"roleName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleReq struct {
|
||||||
|
RoleName string `json:"roleName"`
|
||||||
|
Timeout int32 `json:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *RoleReq) GetRoleName() string {
|
||||||
|
return req.RoleName
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantReq struct {
|
||||||
|
RoleName string `json:"roleName" binding:"required"`
|
||||||
|
ObjectType string `json:"objectType" binding:"required"`
|
||||||
|
ObjectName string `json:"objectName" binding:"required"`
|
||||||
|
Privilege string `json:"privilege" binding:"required"`
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IndexParam struct {
|
||||||
|
FieldName string `json:"fieldName" binding:"required"`
|
||||||
|
IndexName string `json:"indexName" binding:"required"`
|
||||||
|
MetricsType string `json:"metricsType" binding:"required"`
|
||||||
|
IndexType string `json:"indexType"`
|
||||||
|
IndexConfig map[string]interface{} `json:"indexConfig"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IndexParamReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
IndexParams []IndexParam `json:"indexParams" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *IndexParamReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type IndexReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
IndexName string `json:"indexName"`
|
||||||
|
Timeout int32 `json:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *IndexReq) GetDbName() string { return req.DbName }
|
||||||
|
func (req *IndexReq) GetCollectionName() string {
|
||||||
|
return req.CollectionName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *IndexReq) GetIndexName() string {
|
||||||
|
return req.IndexName
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldSchema struct {
|
||||||
|
FieldName string `json:"fieldName" binding:"required"`
|
||||||
|
DataType string `json:"dataType" binding:"required"`
|
||||||
|
IsPrimary bool `json:"isPrimary"`
|
||||||
|
IsPartitionKey bool `json:"isPartitionKey"`
|
||||||
|
Dim int `json:"dimension"`
|
||||||
|
MaxLength int `json:"maxLength"`
|
||||||
|
MaxCapacity int `json:"maxCapacity"`
|
||||||
|
ElementTypeParams map[string]string `json:"elementTypeParams" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CollectionSchema struct {
|
||||||
|
Fields []FieldSchema `json:"fields"`
|
||||||
|
AutoId bool `json:"autoID"`
|
||||||
|
EnableDynamicField bool `json:"enableDynamicField"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CollectionReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
Dimension int32 `json:"dimension"`
|
||||||
|
MetricsType string `json:"metricsType"`
|
||||||
|
Schema CollectionSchema `json:"schema"`
|
||||||
|
IndexParams []IndexParam `json:"indexParams"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CollectionReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
type AliasReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
AliasName string `json:"aliasName" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *AliasReq) GetAliasName() string {
|
||||||
|
return req.AliasName
|
||||||
|
}
|
||||||
|
|
||||||
|
type AliasCollectionReq struct {
|
||||||
|
DbName string `json:"dbName"`
|
||||||
|
CollectionName string `json:"collectionName" binding:"required"`
|
||||||
|
AliasName string `json:"aliasName" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *AliasCollectionReq) GetDbName() string { return req.DbName }
|
||||||
|
|
||||||
|
func (req *AliasCollectionReq) GetCollectionName() string {
|
||||||
|
return req.CollectionName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *AliasCollectionReq) GetAliasName() string {
|
||||||
|
return req.AliasName
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapperReturnHas(has bool) gin.H {
|
||||||
|
return gin.H{HTTPReturnCode: http.StatusOK, HTTPReturnData: gin.H{HTTPReturnHas: has}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapperReturnList(names []string) gin.H {
|
||||||
|
if names == nil {
|
||||||
|
return gin.H{HTTPReturnCode: http.StatusOK, HTTPReturnData: []string{}}
|
||||||
|
}
|
||||||
|
return gin.H{HTTPReturnCode: http.StatusOK, HTTPReturnData: names}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapperReturnRowCount(pairs []*commonpb.KeyValuePair) gin.H {
|
||||||
|
rowCount := "0"
|
||||||
|
for _, keyValue := range pairs {
|
||||||
|
if keyValue.Key == "row_count" {
|
||||||
|
rowCount = keyValue.GetValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gin.H{HTTPReturnCode: http.StatusOK, HTTPReturnData: gin.H{HTTPReturnRowCount: rowCount}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapperReturnDefault() gin.H {
|
||||||
|
return gin.H{HTTPReturnCode: http.StatusOK, HTTPReturnData: gin.H{}}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
package httpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func defaultResponse(c *gin.Context) {
|
||||||
|
c.String(http.StatusRequestTimeout, "timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
// BufferPool represents a pool of buffers.
|
||||||
|
type BufferPool struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a buffer from the buffer pool.
|
||||||
|
// If the pool is empty, a new buffer is created and returned.
|
||||||
|
func (p *BufferPool) Get() *bytes.Buffer {
|
||||||
|
buf := p.pool.Get()
|
||||||
|
if buf == nil {
|
||||||
|
return &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
return buf.(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put adds a buffer back to the pool.
|
||||||
|
func (p *BufferPool) Put(buf *bytes.Buffer) {
|
||||||
|
p.pool.Put(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout struct
|
||||||
|
type Timeout struct {
|
||||||
|
timeout time.Duration
|
||||||
|
handler gin.HandlerFunc
|
||||||
|
response gin.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer is a writer with memory buffer
|
||||||
|
type Writer struct {
|
||||||
|
gin.ResponseWriter
|
||||||
|
body *bytes.Buffer
|
||||||
|
headers http.Header
|
||||||
|
mu sync.Mutex
|
||||||
|
timeout bool
|
||||||
|
wroteHeaders bool
|
||||||
|
code int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriter will return a timeout.Writer pointer
|
||||||
|
func NewWriter(w gin.ResponseWriter, buf *bytes.Buffer) *Writer {
|
||||||
|
return &Writer{ResponseWriter: w, body: buf, headers: make(http.Header)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write will write data to response body
|
||||||
|
func (w *Writer) Write(data []byte) (int, error) {
|
||||||
|
if w.timeout || w.body == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
|
return w.body.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader sends an HTTP response header with the provided status code.
|
||||||
|
// If the response writer has already written headers or if a timeout has occurred,
|
||||||
|
// this method does nothing.
|
||||||
|
func (w *Writer) WriteHeader(code int) {
|
||||||
|
if w.timeout || w.wroteHeaders {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// gin is using -1 to skip writing the status code
|
||||||
|
// see https://github.com/gin-gonic/gin/blob/a0acf1df2814fcd828cb2d7128f2f4e2136d3fac/response_writer.go#L61
|
||||||
|
if code == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checkWriteHeaderCode(code)
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
|
w.writeHeader(code)
|
||||||
|
w.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) writeHeader(code int) {
|
||||||
|
w.wroteHeaders = true
|
||||||
|
w.code = code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header will get response headers
|
||||||
|
func (w *Writer) Header() http.Header {
|
||||||
|
return w.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteString will write string to response body
|
||||||
|
func (w *Writer) WriteString(s string) (int, error) {
|
||||||
|
return w.Write([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeBuffer will release buffer pointer
|
||||||
|
func (w *Writer) FreeBuffer() {
|
||||||
|
// if not reset body,old bytes will put in bufPool
|
||||||
|
w.body.Reset()
|
||||||
|
w.body = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status we must override Status func here,
|
||||||
|
// or the http status code returned by gin.Context.Writer.Status()
|
||||||
|
// will always be 200 in other custom gin middlewares.
|
||||||
|
func (w *Writer) Status() int {
|
||||||
|
if w.code == 0 || w.timeout {
|
||||||
|
return w.ResponseWriter.Status()
|
||||||
|
}
|
||||||
|
return w.code
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkWriteHeaderCode(code int) {
|
||||||
|
if code < 100 || code > 999 {
|
||||||
|
panic(fmt.Sprintf("invalid http status code: %d", code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeoutMiddleware(handler gin.HandlerFunc) gin.HandlerFunc {
|
||||||
|
t := &Timeout{
|
||||||
|
timeout: HTTPDefaultTimeout,
|
||||||
|
handler: handler,
|
||||||
|
response: defaultResponse,
|
||||||
|
}
|
||||||
|
bufPool := &BufferPool{}
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
timeoutSecond, err := strconv.ParseInt(c.Request.Header.Get(HTTPHeaderRequestTimeout), 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
t.timeout = time.Duration(timeoutSecond) * time.Second
|
||||||
|
}
|
||||||
|
finish := make(chan struct{}, 1)
|
||||||
|
panicChan := make(chan interface{}, 1)
|
||||||
|
|
||||||
|
w := c.Writer
|
||||||
|
buffer := bufPool.Get()
|
||||||
|
tw := NewWriter(w, buffer)
|
||||||
|
c.Writer = tw
|
||||||
|
buffer.Reset()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if p := recover(); p != nil {
|
||||||
|
panicChan <- p
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
t.handler(c)
|
||||||
|
finish <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case p := <-panicChan:
|
||||||
|
tw.FreeBuffer()
|
||||||
|
c.Writer = w
|
||||||
|
panic(p)
|
||||||
|
|
||||||
|
case <-finish:
|
||||||
|
c.Next()
|
||||||
|
tw.mu.Lock()
|
||||||
|
defer tw.mu.Unlock()
|
||||||
|
dst := tw.ResponseWriter.Header()
|
||||||
|
for k, vv := range tw.Header() {
|
||||||
|
dst[k] = vv
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tw.ResponseWriter.Write(buffer.Bytes()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
tw.FreeBuffer()
|
||||||
|
bufPool.Put(buffer)
|
||||||
|
|
||||||
|
case <-time.After(t.timeout):
|
||||||
|
c.Abort()
|
||||||
|
tw.mu.Lock()
|
||||||
|
defer tw.mu.Unlock()
|
||||||
|
tw.timeout = true
|
||||||
|
tw.FreeBuffer()
|
||||||
|
bufPool.Put(buffer)
|
||||||
|
|
||||||
|
c.Writer = w
|
||||||
|
t.response(c)
|
||||||
|
c.Writer = tw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -162,8 +162,8 @@ func printIndexes(indexes []*milvuspb.IndexDescription) []gin.H {
|
||||||
var res []gin.H
|
var res []gin.H
|
||||||
for _, index := range indexes {
|
for _, index := range indexes {
|
||||||
res = append(res, gin.H{
|
res = append(res, gin.H{
|
||||||
HTTPReturnIndexName: index.IndexName,
|
HTTPIndexName: index.IndexName,
|
||||||
HTTPReturnIndexField: index.FieldName,
|
HTTPIndexField: index.FieldName,
|
||||||
HTTPReturnIndexMetricsType: getMetricType(index.Params),
|
HTTPReturnIndexMetricsType: getMetricType(index.Params),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,8 +292,8 @@ func TestPrintCollectionDetails(t *testing.T) {
|
||||||
}, printFields(coll.Fields))
|
}, printFields(coll.Fields))
|
||||||
assert.Equal(t, []gin.H{
|
assert.Equal(t, []gin.H{
|
||||||
{
|
{
|
||||||
HTTPReturnIndexName: DefaultIndexName,
|
HTTPIndexName: DefaultIndexName,
|
||||||
HTTPReturnIndexField: FieldBookIntro,
|
HTTPIndexField: FieldBookIntro,
|
||||||
HTTPReturnIndexMetricsType: DefaultMetricType,
|
HTTPReturnIndexMetricsType: DefaultMetricType,
|
||||||
},
|
},
|
||||||
}, printIndexes(indexes))
|
}, printIndexes(indexes))
|
||||||
|
|
|
@ -202,6 +202,8 @@ func (s *Server) startHTTPServer(errChan chan error) {
|
||||||
}
|
}
|
||||||
app := ginHandler.Group("/v1")
|
app := ginHandler.Group("/v1")
|
||||||
httpserver.NewHandlersV1(s.proxy).RegisterRoutesToV1(app)
|
httpserver.NewHandlersV1(s.proxy).RegisterRoutesToV1(app)
|
||||||
|
appV2 := ginHandler.Group("/v2/vectordb")
|
||||||
|
httpserver.NewHandlersV2(s.proxy).RegisterRoutesToV2(appV2)
|
||||||
s.httpServer = &http.Server{Handler: ginHandler, ReadHeaderTimeout: time.Second}
|
s.httpServer = &http.Server{Handler: ginHandler, ReadHeaderTimeout: time.Second}
|
||||||
errChan <- nil
|
errChan <- nil
|
||||||
if err := s.httpServer.Serve(s.httpListener); err != nil && err != cmux.ErrServerClosed {
|
if err := s.httpServer.Serve(s.httpListener); err != nil && err != cmux.ErrServerClosed {
|
||||||
|
|
|
@ -904,10 +904,13 @@ func GetCurDBNameFromContextOrDefault(ctx context.Context) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContextWithMetadata(ctx context.Context, username string, dbName string) context.Context {
|
func NewContextWithMetadata(ctx context.Context, username string, dbName string) context.Context {
|
||||||
|
dbKey := strings.ToLower(util.HeaderDBName)
|
||||||
|
if username == "" {
|
||||||
|
return contextutil.AppendToIncomingContext(ctx, dbKey, dbName)
|
||||||
|
}
|
||||||
originValue := fmt.Sprintf("%s%s%s", username, util.CredentialSeperator, username)
|
originValue := fmt.Sprintf("%s%s%s", username, util.CredentialSeperator, username)
|
||||||
authKey := strings.ToLower(util.HeaderAuthorize)
|
authKey := strings.ToLower(util.HeaderAuthorize)
|
||||||
authValue := crypto.Base64Encode(originValue)
|
authValue := crypto.Base64Encode(originValue)
|
||||||
dbKey := strings.ToLower(util.HeaderDBName)
|
|
||||||
return contextutil.AppendToIncomingContext(ctx, authKey, authValue, dbKey, dbName)
|
return contextutil.AppendToIncomingContext(ctx, authKey, authValue, dbKey, dbName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue