示例#1
0
// handleConsume is an HTTP request handler for `GET /topic/{topic}/messages`
func (as *T) handleConsume(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	topic := mux.Vars(r)[paramTopic]
	group, err := getGroupParam(r)
	if err != nil {
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{err.Error()})
		return
	}

	consMsg, err := as.consumer.Consume(group, topic)
	if err != nil {
		var status int
		switch err.(type) {
		case consumer.ErrRequestTimeout:
			status = http.StatusRequestTimeout
		case consumer.ErrBufferOverflow:
			status = 429 // StatusTooManyRequests
		default:
			status = http.StatusInternalServerError
		}
		respondWithJSON(w, status, errorHTTPResponse{err.Error()})
		return
	}

	respondWithJSON(w, http.StatusOK, consumeHTTPResponse{
		Key:       consMsg.Key,
		Value:     consMsg.Value,
		Partition: consMsg.Partition,
		Offset:    consMsg.Offset,
	})
}
示例#2
0
// handleProduce is an HTTP request handler for `POST /topic/{topic}/messages`
func (as *T) handleProduce(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	topic := mux.Vars(r)[paramTopic]
	key := getParamBytes(r, paramKey)
	_, isSync := r.Form[paramSync]

	// Get the message body from the HTTP request.
	if _, ok := r.Header[headerContentLength]; !ok {
		errorText := fmt.Sprintf("Missing %s header", headerContentLength)
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{errorText})
		return
	}
	messageSizeStr := r.Header.Get(headerContentLength)
	messageSize, err := strconv.Atoi(messageSizeStr)
	if err != nil {
		errorText := fmt.Sprintf("Invalid %s header: %s", headerContentLength, messageSizeStr)
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{errorText})
		return
	}
	message, err := ioutil.ReadAll(r.Body)
	if err != nil {
		errorText := fmt.Sprintf("Failed to read a message: err=(%s)", err)
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{errorText})
		return
	}
	if len(message) != messageSize {
		errorText := fmt.Sprintf("Message size does not match %s: expected=%v, actual=%v",
			headerContentLength, messageSize, len(message))
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{errorText})
		return
	}

	// Asynchronously submit the message to the Kafka cluster.
	if !isSync {
		as.producer.AsyncProduce(topic, toEncoderPreservingNil(key), sarama.StringEncoder(message))
		respondWithJSON(w, http.StatusOK, EmptyResponse)
		return
	}

	prodMsg, err := as.producer.Produce(topic, toEncoderPreservingNil(key), sarama.StringEncoder(message))
	if err != nil {
		var status int
		switch err {
		case sarama.ErrUnknownTopicOrPartition:
			status = http.StatusNotFound
		default:
			status = http.StatusInternalServerError
		}
		respondWithJSON(w, status, errorHTTPResponse{err.Error()})
		return
	}

	respondWithJSON(w, http.StatusOK, produceHTTPResponse{
		Partition: prodMsg.Partition,
		Offset:    prodMsg.Offset,
	})
}
示例#3
0
// handleGetTopicConsumers is an HTTP request handler for `GET /topic/{topic}/consumers`
func (as *T) handleGetTopicConsumers(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	var err error

	topic := mux.Vars(r)[paramTopic]

	group := ""
	r.ParseForm()
	groups := r.Form[paramGroup]
	if len(groups) > 1 {
		err = fmt.Errorf("One consumer group is expected, but %d provided", len(groups))
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{err.Error()})
		return
	}
	if len(groups) == 1 {
		group = groups[0]
	}

	var consumers map[string]map[string][]int32
	if group == "" {
		consumers, err = as.admin.GetAllTopicConsumers(topic)
		if err != nil {
			respondWithJSON(w, http.StatusInternalServerError, errorHTTPResponse{err.Error()})
			return
		}
	} else {
		groupConsumers, err := as.admin.GetTopicConsumers(group, topic)
		if err != nil {
			if _, ok := err.(admin.ErrInvalidParam); ok {
				respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{err.Error()})
				return
			}
			respondWithJSON(w, http.StatusInternalServerError, errorHTTPResponse{err.Error()})
			return
		}
		consumers = make(map[string]map[string][]int32)
		if len(groupConsumers) != 0 {
			consumers[group] = groupConsumers
		}
	}

	encodedRes, err := json.MarshalIndent(consumers, "", "  ")
	if err != nil {
		log.Errorf("Failed to send HTTP reponse: status=%d, body=%v, reason=%v", http.StatusOK, encodedRes, err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	encodedRes = prettyfmt.CollapseJSON(encodedRes)

	w.Header().Add(headerContentType, "application/json")
	w.WriteHeader(http.StatusOK)
	if _, err := w.Write(encodedRes); err != nil {
		log.Errorf("Failed to send HTTP reponse: status=%d, body=%v, reason=%v", http.StatusOK, encodedRes, err)
	}
}
示例#4
0
// handleGetOffsets is an HTTP request handler for `POST /topic/{topic}/offsets`
func (as *T) handleSetOffsets(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	topic := mux.Vars(r)[paramTopic]
	group, err := getGroupParam(r)
	if err != nil {
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{err.Error()})
		return
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		errorText := fmt.Sprintf("Failed to read the request: err=(%s)", err)
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{errorText})
		return
	}

	var partitionOffsetViews []partitionOffsetView
	if err := json.Unmarshal(body, &partitionOffsetViews); err != nil {
		errorText := fmt.Sprintf("Failed to parse the request: err=(%s)", err)
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{errorText})
		return
	}

	partitionOffsets := make([]admin.PartitionOffset, len(partitionOffsetViews))
	for i, pov := range partitionOffsetViews {
		partitionOffsets[i].Partition = pov.Partition
		partitionOffsets[i].Offset = pov.Offset
		partitionOffsets[i].Metadata = pov.Metadata
	}

	err = as.admin.SetGroupOffsets(group, topic, partitionOffsets)
	if err != nil {
		if err, ok := err.(admin.ErrQuery); ok && err.Cause() == sarama.ErrUnknownTopicOrPartition {
			respondWithJSON(w, http.StatusNotFound, errorHTTPResponse{"Unknown topic"})
			return
		}
		respondWithJSON(w, http.StatusInternalServerError, errorHTTPResponse{err.Error()})
		return
	}

	respondWithJSON(w, http.StatusOK, EmptyResponse)
}
示例#5
0
// handleGetTopicConsumers is an HTTP request handler for `GET /topic/{topic}/consumers`
func (as *HTTPAPIServer) handleGetTopicConsumers(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	var err error

	topic := mux.Vars(r)[ParamTopic]

	group := ""
	r.ParseForm()
	groups := r.Form[ParamGroup]
	if len(groups) > 1 {
		err = fmt.Errorf("One consumer group is expected, but %d provided", len(groups))
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{err.Error()})
		return
	}
	if len(groups) == 1 {
		group = groups[0]
	}

	var consumers map[string]map[string][]int32
	if group == "" {
		consumers, err = as.admin.GetAllTopicConsumers(topic)
		if err != nil {
			respondWithJSON(w, http.StatusInternalServerError, errorHTTPResponse{err.Error()})
			return
		}
	} else {
		groupConsumers, err := as.admin.GetTopicConsumers(group, topic)
		if err != nil {
			if _, ok := err.(ErrAdminInvalidParam); ok {
				respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{err.Error()})
				return
			}
			respondWithJSON(w, http.StatusInternalServerError, errorHTTPResponse{err.Error()})
			return
		}
		consumers = make(map[string]map[string][]int32)
		if len(groupConsumers) != 0 {
			consumers[group] = groupConsumers
		}
	}

	respondWithJSON(w, http.StatusOK, consumers)
}
示例#6
0
// handleGetOffsets is an HTTP request handler for `GET /topic/{topic}/offsets`
func (as *T) handleGetOffsets(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	topic := mux.Vars(r)[paramTopic]
	group, err := getGroupParam(r)
	if err != nil {
		respondWithJSON(w, http.StatusBadRequest, errorHTTPResponse{err.Error()})
		return
	}

	partitionOffsets, err := as.admin.GetGroupOffsets(group, topic)
	if err != nil {
		if err, ok := err.(admin.ErrQuery); ok && err.Cause() == sarama.ErrUnknownTopicOrPartition {
			respondWithJSON(w, http.StatusNotFound, errorHTTPResponse{"Unknown topic"})
			return
		}
		respondWithJSON(w, http.StatusInternalServerError, errorHTTPResponse{err.Error()})
		return
	}

	partitionOffsetView := make([]partitionOffsetView, len(partitionOffsets))
	for i, po := range partitionOffsets {
		partitionOffsetView[i].Partition = po.Partition
		partitionOffsetView[i].Begin = po.Begin
		partitionOffsetView[i].End = po.End
		partitionOffsetView[i].Count = po.End - po.Begin
		partitionOffsetView[i].Offset = po.Offset
		if po.Offset == sarama.OffsetNewest {
			partitionOffsetView[i].Lag = 0
		} else if po.Offset == sarama.OffsetOldest {
			partitionOffsetView[i].Lag = po.End - po.Begin
		} else {
			partitionOffsetView[i].Lag = po.End - po.Offset
		}
		partitionOffsetView[i].Metadata = po.Metadata
	}
	respondWithJSON(w, http.StatusOK, partitionOffsetView)
}