func (decoder *ProtobufDecoderV1) Read(ctx context.Context, req *http.Request) error { body := req.Body bufferedBody := bufio.NewReaderSize(body, 32768) for { log.WithField("body", body).Debug("Starting protobuf loop") buf, err := bufferedBody.Peek(1) if err == io.EOF { log.Debug("EOF") return nil } buf, err = bufferedBody.Peek(4) // should be big enough for any varint if err != nil { log.WithField("err", err).Info("peek error") return err } num, bytesRead := proto.DecodeVarint(buf) if bytesRead == 0 { // Invalid varint? return errInvalidProtobufVarint } if num > 32768 { // Sanity check return errProtobufTooLarge } // Get the varint out buf = make([]byte, bytesRead) io.ReadFull(bufferedBody, buf) // Get the structure out buf = make([]byte, num) _, err = io.ReadFull(bufferedBody, buf) if err != nil { return fmt.Errorf("unable to fully read protobuf message: %s", err) } var msg com_signalfx_metrics_protobuf.DataPoint err = proto.Unmarshal(buf, &msg) if err != nil { return err } if datapointProtobufIsInvalidForV1(&msg) { return errInvalidProtobuf } mt := decoder.TypeGetter.GetMetricTypeFromMap(msg.GetMetric()) if dp, err := NewProtobufDataPointWithType(&msg, mt); err == nil { decoder.Sink.AddDatapoints(ctx, []*datapoint.Datapoint{dp}) } } }
// NewProtobufDataPointWithType creates a new datapoint from SignalFx's protobuf definition (backwards compatable with old API) func NewProtobufDataPointWithType(dp *com_signalfx_metrics_protobuf.DataPoint, mType com_signalfx_metrics_protobuf.MetricType) *datapoint.Datapoint { var mt com_signalfx_metrics_protobuf.MetricType if dp.MetricType != nil { mt = dp.GetMetricType() } else { mt = mType } dims := make(map[string]string, len(dp.GetDimensions())+1) if dp.GetSource() != "" { dims["sf_source"] = dp.GetSource() } dpdims := dp.GetDimensions() for _, dpdim := range dpdims { dims[dpdim.GetKey()] = dpdim.GetValue() } return datapoint.New(dp.GetMetric(), dims, NewDatumValue(dp.GetValue()), fromMT(mt), fromTs(dp.GetTimestamp())) }