Use content negotiation for /write API
If content-type is "application/json", we'll process the request as of old JSON write API. Otherwise, we assume line protocol but check the first byte in case a older client is still sending JSON without the correct content-type header.pull/2721/head
parent
50be500777
commit
badb2bf057
|
@ -1,6 +1,7 @@
|
||||||
package httpd
|
package httpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -95,10 +96,6 @@ func NewHandler(requireAuthentication, loggingEnabled bool, version string) *Han
|
||||||
"write", // Data-ingest route.
|
"write", // Data-ingest route.
|
||||||
"POST", "/write", true, true, h.serveWrite,
|
"POST", "/write", true, true, h.serveWrite,
|
||||||
},
|
},
|
||||||
route{
|
|
||||||
"write_points", // Data-ingest route.
|
|
||||||
"POST", "/write_points", true, true, h.serveWritePoints,
|
|
||||||
},
|
|
||||||
route{ // Ping
|
route{ // Ping
|
||||||
"ping",
|
"ping",
|
||||||
"GET", "/ping", true, true, h.servePing,
|
"GET", "/ping", true, true, h.servePing,
|
||||||
|
@ -261,37 +258,45 @@ func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, user *meta.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveWrite receives incoming series data and writes it to the database.
|
|
||||||
func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user *meta.UserInfo) {
|
func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user *meta.UserInfo) {
|
||||||
// Check to see if we have a gzip'd post
|
|
||||||
var body io.ReadCloser
|
// Handle gzip decoding of the body
|
||||||
|
body := r.Body
|
||||||
if r.Header.Get("Content-encoding") == "gzip" {
|
if r.Header.Get("Content-encoding") == "gzip" {
|
||||||
b, err := gzip.NewReader(r.Body)
|
b, err := gzip.NewReader(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultError(w, influxql.Result{Err: err}, http.StatusBadRequest)
|
h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
body = b
|
body = b
|
||||||
defer r.Body.Close()
|
}
|
||||||
} else {
|
defer body.Close()
|
||||||
body = r.Body
|
|
||||||
|
b, err := ioutil.ReadAll(body)
|
||||||
|
if err != nil {
|
||||||
|
if h.WriteTrace {
|
||||||
|
h.Logger.Print("write handler unable to read bytes from request body")
|
||||||
|
}
|
||||||
|
h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if h.WriteTrace {
|
||||||
|
h.Logger.Printf("write body received by handler: %s", string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.Header.Get("Content-Type") == "application/json" {
|
||||||
|
h.serveWriteJSON(w, r, b, user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.serveWriteLine(w, r, b, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveWriteJSON receives incoming series data in JSON and writes it to the database.
|
||||||
|
func (h *Handler) serveWriteJSON(w http.ResponseWriter, r *http.Request, body []byte, user *meta.UserInfo) {
|
||||||
var bp client.BatchPoints
|
var bp client.BatchPoints
|
||||||
var dec *json.Decoder
|
var dec *json.Decoder
|
||||||
|
|
||||||
if h.WriteTrace {
|
dec = json.NewDecoder(bytes.NewReader(body))
|
||||||
b, err := ioutil.ReadAll(body)
|
|
||||||
if err != nil {
|
|
||||||
h.Logger.Print("write handler unable to read bytes from request body")
|
|
||||||
} else {
|
|
||||||
h.Logger.Printf("write body received by handler: %s", string(b))
|
|
||||||
}
|
|
||||||
dec = json.NewDecoder(strings.NewReader(string(b)))
|
|
||||||
} else {
|
|
||||||
dec = json.NewDecoder(body)
|
|
||||||
defer body.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := dec.Decode(&bp); err != nil {
|
if err := dec.Decode(&bp); err != nil {
|
||||||
if err.Error() == "EOF" {
|
if err.Error() == "EOF" {
|
||||||
|
@ -343,77 +348,56 @@ func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user *meta.
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveWritePoints receives incoming series data and writes it to the database.
|
func (h *Handler) writeError(w http.ResponseWriter, result influxql.Result, statusCode int) {
|
||||||
func (h *Handler) serveWritePoints(w http.ResponseWriter, r *http.Request, user *meta.UserInfo) {
|
w.WriteHeader(statusCode)
|
||||||
var writeError = func(result influxql.Result, statusCode int) {
|
w.Write([]byte(result.Err.Error()))
|
||||||
w.WriteHeader(statusCode)
|
}
|
||||||
w.Write([]byte(result.Err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if we have a gzip'd post
|
// serveWriteLine receives incoming series data in line protocol format and writes it to the database.
|
||||||
var body io.ReadCloser
|
func (h *Handler) serveWriteLine(w http.ResponseWriter, r *http.Request, body []byte, user *meta.UserInfo) {
|
||||||
if r.Header.Get("Content-encoding") == "gzip" {
|
// Some clients may not set the content-type header appropriately and send JSON with a non-json
|
||||||
b, err := gzip.NewReader(r.Body)
|
// content-type. If the body looks JSON, try to handle it as as JSON instead
|
||||||
if err != nil {
|
if len(body) > 0 && body[0] == '{' {
|
||||||
writeError(influxql.Result{Err: err}, http.StatusBadRequest)
|
h.serveWriteJSON(w, r, body, user)
|
||||||
return
|
|
||||||
}
|
|
||||||
body = b
|
|
||||||
defer r.Body.Close()
|
|
||||||
} else {
|
|
||||||
body = r.Body
|
|
||||||
defer r.Body.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(body)
|
|
||||||
if err != nil {
|
|
||||||
if h.WriteTrace {
|
|
||||||
h.Logger.Print("write handler unable to read bytes from request body")
|
|
||||||
}
|
|
||||||
writeError(influxql.Result{Err: err}, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if h.WriteTrace {
|
|
||||||
h.Logger.Printf("write body received by handler: %s", string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
precision := r.FormValue("precision")
|
precision := r.FormValue("precision")
|
||||||
if precision == "" {
|
if precision == "" {
|
||||||
precision = "n"
|
precision = "n"
|
||||||
}
|
}
|
||||||
|
|
||||||
points, err := tsdb.ParsePointsWithPrecision(b, time.Now().UTC(), precision)
|
points, err := tsdb.ParsePointsWithPrecision(body, time.Now().UTC(), precision)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "EOF" {
|
if err.Error() == "EOF" {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeError(influxql.Result{Err: err}, http.StatusBadRequest)
|
h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
database := r.FormValue("db")
|
database := r.FormValue("db")
|
||||||
if database == "" {
|
if database == "" {
|
||||||
writeError(influxql.Result{Err: fmt.Errorf("database is required")}, http.StatusBadRequest)
|
h.writeError(w, influxql.Result{Err: fmt.Errorf("database is required")}, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if di, err := h.MetaStore.Database(database); err != nil {
|
if di, err := h.MetaStore.Database(database); err != nil {
|
||||||
resultError(w, influxql.Result{Err: fmt.Errorf("metastore database error: %s", err)}, http.StatusInternalServerError)
|
h.writeError(w, influxql.Result{Err: fmt.Errorf("metastore database error: %s", err)}, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
} else if di == nil {
|
} else if di == nil {
|
||||||
resultError(w, influxql.Result{Err: fmt.Errorf("database not found: %q", database)}, http.StatusNotFound)
|
h.writeError(w, influxql.Result{Err: fmt.Errorf("database not found: %q", database)}, http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.requireAuthentication && user == nil {
|
if h.requireAuthentication && user == nil {
|
||||||
resultError(w, influxql.Result{Err: fmt.Errorf("user is required to write to database %q", database)}, http.StatusUnauthorized)
|
h.writeError(w, influxql.Result{Err: fmt.Errorf("user is required to write to database %q", database)}, http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.requireAuthentication && !user.Authorize(influxql.WritePrivilege, database) {
|
if h.requireAuthentication && !user.Authorize(influxql.WritePrivilege, database) {
|
||||||
resultError(w, influxql.Result{Err: fmt.Errorf("%q user is not authorized to write to database %q", user.Name, database)}, http.StatusUnauthorized)
|
h.writeError(w, influxql.Result{Err: fmt.Errorf("%q user is not authorized to write to database %q", user.Name, database)}, http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,10 +421,10 @@ func (h *Handler) serveWritePoints(w http.ResponseWriter, r *http.Request, user
|
||||||
ConsistencyLevel: consistency,
|
ConsistencyLevel: consistency,
|
||||||
Points: points,
|
Points: points,
|
||||||
}); influxdb.IsClientError(err) {
|
}); influxdb.IsClientError(err) {
|
||||||
writeError(influxql.Result{Err: err}, http.StatusBadRequest)
|
h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
writeError(influxql.Result{Err: err}, http.StatusInternalServerError)
|
h.writeError(w, influxql.Result{Err: err}, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue