From 831c93f6455a3630a88503740e504aeb611b85d8 Mon Sep 17 00:00:00 2001 From: Lynn Date: Fri, 9 Mar 2018 15:35:52 +0800 Subject: [PATCH] server, docs: change rowBin to request. (#5983) --- docs/tidb_http_api.md | 6 +++-- server/http_handler.go | 53 ++++++++++++++++++++++++++----------- server/http_handler_test.go | 15 ++++++++++- server/http_status.go | 2 +- 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/docs/tidb_http_api.md b/docs/tidb_http_api.md index cbcc893d3482a..cf5ebf63d7c71 100644 --- a/docs/tidb_http_api.md +++ b/docs/tidb_http_api.md @@ -96,9 +96,11 @@ timezone.* curl http://{TiDBIP}:10080/settings ``` -1. Get the column value by an encoded row and some information that can be obtained from a column of the table schema information. +1. Get the column value by an encoded row and some information that can be obtained from a column of the table schema information. + + Argument example: rowBin=base64_encoded_row_value ```shell - curl http://{TiDBIP}:10080/tables/{colID}/{colFlag}/{colLen}/{rowBin} + curl http://{TiDBIP}:10080/tables/{colID}/{colFlag}/{colLen}?rowBin={val} ``` *Hint: For the column which field type is timezone dependent, e.g. `timestamp`, convert its value to UTC timezone.* \ No newline at end of file diff --git a/server/http_handler.go b/server/http_handler.go index 7cf3d5b353546..0c99efff82bcc 100644 --- a/server/http_handler.go +++ b/server/http_handler.go @@ -342,8 +342,25 @@ func (vh valueHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - // Get binary. - bin := params[pRowBin] + // Get the unchanged binary. + if req.URL == nil { + err = errors.BadRequestf("Invalid URL") + writeError(w, err) + return + } + values := make(url.Values) + shouldUnescape := false + err = parseQuery(req.URL.RawQuery, values, shouldUnescape) + if err != nil { + writeError(w, err) + return + } + if len(values[pRowBin]) != 1 { + err = errors.BadRequestf("Invalid Query:%v", values[pRowBin]) + writeError(w, err) + return + } + bin := values[pRowBin][0] valData, err := base64.StdEncoding.DecodeString(bin) if err != nil { writeError(w, err) @@ -882,11 +899,13 @@ func (r RegionFrameRange) lastTableID() int64 { return r.last.TableID } -// parseQuery is used to parse query string in URL, due to golang http package can not distinguish +// parseQuery is used to parse query string in URL with shouldUnescape, due to golang http package can not distinguish // query like "?a=" and "?a". We rewrite it to separate these two queries. e.g. // "?a=" which means that a is an empty string ""; // "?a" which means that a is null. -func parseQuery(query string, m url.Values) error { +// If shouldUnescape is true, we use QueryUnescape to handle keys and values that will be put in m. +// If shouldUnescape is false, we don't use QueryUnescap to handle. +func parseQuery(query string, m url.Values, shouldUnescape bool) error { var err error for query != "" { key := query @@ -901,19 +920,23 @@ func parseQuery(query string, m url.Values) error { if i := strings.Index(key, "="); i >= 0 { value := "" key, value = key[:i], key[i+1:] - key, err = url.QueryUnescape(key) - if err != nil { - return errors.Trace(err) - } - value, err = url.QueryUnescape(value) - if err != nil { - return errors.Trace(err) + if shouldUnescape { + key, err = url.QueryUnescape(key) + if err != nil { + return errors.Trace(err) + } + value, err = url.QueryUnescape(value) + if err != nil { + return errors.Trace(err) + } } m[key] = append(m[key], value) } else { - key, err = url.QueryUnescape(key) - if err != nil { - return errors.Trace(err) + if shouldUnescape { + key, err = url.QueryUnescape(key) + if err != nil { + return errors.Trace(err) + } } if _, ok := m[key]; !ok { m[key] = nil @@ -937,7 +960,7 @@ func (h mvccTxnHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { break } values := make(url.Values) - err = parseQuery(req.URL.RawQuery, values) + err = parseQuery(req.URL.RawQuery, values, true) if err == nil { data, err = h.handleMvccGetByIdx(params, values) } diff --git a/server/http_handler_test.go b/server/http_handler_test.go index 440bc87919ca7..249b9994d9c93 100644 --- a/server/http_handler_test.go +++ b/server/http_handler_test.go @@ -363,9 +363,10 @@ func (ts *HTTPHandlerTestSuite) TestDecodeColumnValue(c *C) { bs, err := tablecodec.EncodeRow(sc, row, colIDs, nil, nil) c.Assert(err, IsNil) c.Assert(bs, NotNil) + bin := base64.StdEncoding.EncodeToString(bs) unitTest := func(col *column) { - url := fmt.Sprintf("http://127.0.0.1:10090/tables/%d/%v/%d/%d/%s", col.id, col.tp.Tp, col.tp.Flag, col.tp.Flen, base64.StdEncoding.EncodeToString(bs)) + url := fmt.Sprintf("http://127.0.0.1:10090/tables/%d/%v/%d/%d?rowBin=%s", col.id, col.tp.Tp, col.tp.Flag, col.tp.Flen, bin) resp, err := http.Get(url) c.Assert(err, IsNil, Commentf("url:%s", url)) decoder := json.NewDecoder(resp.Body) @@ -380,6 +381,18 @@ func (ts *HTTPHandlerTestSuite) TestDecodeColumnValue(c *C) { for _, col := range cols { unitTest(col) } + + // Test bin has `+`. + // 2018-03-08 16:01:00.315313 + bin = "CAIIyAEIBAIGYWJjCAYGAQCBCAgJsZ+TgISg1M8Z" + row[3] = types.NewTimeDatum(types.Time{Time: types.FromGoTime(time.Date(2018, 3, 8, 16, 1, 0, 315313000, time.UTC)), Fsp: 6, Type: mysql.TypeTimestamp}) + unitTest(cols[3]) + + // Test bin has `/`. + // 2018-03-08 02:44:46.409199 + bin = "CAIIyAEIBAIGYWJjCAYGAQCBCAgJ7/yY8LKF1M8Z" + row[3] = types.NewTimeDatum(types.Time{Time: types.FromGoTime(time.Date(2018, 3, 8, 2, 44, 46, 409199000, time.UTC)), Fsp: 6, Type: mysql.TypeTimestamp}) + unitTest(cols[3]) } func (ts *HTTPHandlerTestSuite) TestGetIndexMVCC(c *C) { diff --git a/server/http_status.go b/server/http_status.go index 37c4cec6f3761..3a39340864065 100644 --- a/server/http_status.go +++ b/server/http_status.go @@ -53,7 +53,7 @@ func (s *Server) startHTTPServer() { router.Handle("/schema", schemaHandler{tikvHandlerTool}) router.Handle("/schema/{db}", schemaHandler{tikvHandlerTool}) router.Handle("/schema/{db}/{table}", schemaHandler{tikvHandlerTool}) - router.Handle("/tables/{colID}/{colTp}/{colFlag}/{colLen}/{rowBin}", valueHandler{}) + router.Handle("/tables/{colID}/{colTp}/{colFlag}/{colLen}", valueHandler{}) if s.cfg.Store == "tikv" { // HTTP path for tikv router.Handle("/tables/{db}/{table}/regions", tableHandler{tikvHandlerTool, opTableRegions})