mirror of https://github.com/tikv/client-go.git
272 lines
8.7 KiB
Go
272 lines
8.7 KiB
Go
// Copyright 2019 PingCAP, 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,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package httpproxy
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/tikv/client-go/config"
|
|
"github.com/tikv/client-go/proxy"
|
|
)
|
|
|
|
type txnkvHandler struct {
|
|
p proxy.TxnKVProxy
|
|
}
|
|
|
|
// TxnRequest is the structure of a txnkv request that the http proxy accepts.
|
|
type TxnRequest struct {
|
|
PDAddrs []string `json:"pd_addrs,omitempty"` // for new
|
|
TS uint64 `json:"ts,omitempty"` // for beginWithTS
|
|
Key []byte `json:"key,omitempty"` // for get, set, delete, iter, iterReverse
|
|
Value []byte `json:"value,omitempty"` // for set
|
|
Keys [][]byte `json:"keys,omitempty"` // for batchGet, lockKeys
|
|
UpperBound []byte `json:"upper_bound,omitempty"` // for iter
|
|
}
|
|
|
|
// TxnResponse is the structure of a txnkv response that the http proxy sends.
|
|
type TxnResponse struct {
|
|
ID string `json:"id,omitempty"` // for new, begin, beginWithTS, iter, iterReverse
|
|
TS uint64 `json:"ts,omitempty"` // for getTS
|
|
Key []byte `json:"key,omitempty"` // for iterKey
|
|
Value []byte `json:"value,omitempty"` // for get, iterValue
|
|
Keys [][]byte `json:"keys,omitempty"` // for batchGet
|
|
Values [][]byte `json:"values,omitempty"` // for batchGet
|
|
IsValid bool `json:"is_valid,omitempty"` // for valid, iterValid
|
|
IsReadOnly bool `json:"is_readonly,omitempty"` // for isReadOnly
|
|
Size int `json:"size,omitempty"` // for size
|
|
Length int `json:"length,omitempty"` // for length
|
|
}
|
|
|
|
func (h txnkvHandler) New(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
id, err := h.p.New(ctx, r.PDAddrs, config.Default())
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{ID: string(id)}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) Close(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.Close(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) Begin(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
txnID, err := h.p.Begin(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{ID: string(txnID)}, http.StatusCreated, nil
|
|
}
|
|
|
|
func (h txnkvHandler) BeginWithTS(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
txnID, err := h.p.BeginWithTS(ctx, r.TS)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{ID: string(txnID)}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) GetTS(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
ts, err := h.p.GetTS(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{TS: ts}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnGet(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
val, err := h.p.TxnGet(ctx, r.Key)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{Value: val}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnBatchGet(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
m, err := h.p.TxnBatchGet(ctx, r.Keys)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
keys, values := make([][]byte, 0, len(m)), make([][]byte, 0, len(m))
|
|
for k, v := range m {
|
|
keys = append(keys, []byte(k))
|
|
values = append(values, v)
|
|
}
|
|
return &TxnResponse{Keys: keys, Values: values}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnSet(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.TxnSet(ctx, r.Key, r.Value)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnIter(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
iterID, err := h.p.TxnIter(ctx, r.Key, r.UpperBound)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{ID: string(iterID)}, http.StatusCreated, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnIterReverse(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
iterID, err := h.p.TxnIterReverse(ctx, r.Key)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{ID: string(iterID)}, http.StatusCreated, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnIsReadOnly(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
readonly, err := h.p.TxnIsReadOnly(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{IsReadOnly: readonly}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnDelete(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.TxnDelete(ctx, r.Key)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnCommit(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.TxnCommit(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnRollback(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.TxnRollback(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnLockKeys(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.TxnLockKeys(ctx, r.Keys)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnValid(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
valid, err := h.p.TxnValid(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{IsValid: valid}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnLen(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
length, err := h.p.TxnLen(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{Length: length}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) TxnSize(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
size, err := h.p.TxnSize(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{Size: size}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) IterValid(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
valid, err := h.p.IterValid(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{IsValid: valid}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) IterKey(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
key, err := h.p.IterKey(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{Key: key}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) IterValue(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
val, err := h.p.IterValue(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{Value: val}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) IterNext(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.IterNext(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) IterClose(ctx context.Context, r *TxnRequest) (*TxnResponse, int, error) {
|
|
err := h.p.IterClose(ctx)
|
|
if err != nil {
|
|
return nil, http.StatusInternalServerError, err
|
|
}
|
|
return &TxnResponse{}, http.StatusOK, nil
|
|
}
|
|
|
|
func (h txnkvHandler) handlerFunc(f func(context.Context, *TxnRequest) (*TxnResponse, int, error)) func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
data, err := ioutil.ReadAll(r.Body)
|
|
if err != nil {
|
|
sendError(w, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
var req TxnRequest
|
|
if err = json.Unmarshal(data, &req); err != nil {
|
|
sendError(w, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
ctx, cancel := reqContext(mux.Vars(r))
|
|
res, status, err := f(ctx, &req)
|
|
cancel()
|
|
if err != nil {
|
|
sendError(w, err, status)
|
|
return
|
|
}
|
|
data, err = json.Marshal(res)
|
|
if err != nil {
|
|
sendError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(status)
|
|
w.Write(data)
|
|
}
|
|
}
|