Prometheus handler strange output using Gin - http

Basically, I'm developing an HTTP endpoint to get the metrics from prometheus package.
Following the instructions in this link [https://stackoverflow.com/a/65609042/17150602] I created a handler to be able to call promhttp.Handler() like so:
g.GET("/metrics", prometheusHandler())
func prometheusHandler() gin.HandlerFunc {
h := promhttp.Handler()
return func(c *gin.Context) {
h.ServeHTTP(c.Writer, c.Request)
}
}
The thing is, when I call localhost:1080/metrics the output shows like this (btw, I'm using Postman):
Postman request to get metrics with wrong output
But if, for instance, I change the port and use http instead of gin package like so:
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(promAddr, nil)
The output shows OK as you can see here:
Postman reuest to get metrics with correct output
What and why is this happening?
Thanks

You probably forgot to remove a compression middleware like gzip.

Related

Can we read and assert on response in cypress cy.route Function?

I couldn't find anything in the documentation about asserting on the xhr response on the page.Would like to know how it could be achieved in code?
you can use expect
let's suppose that you set up the cypress server as below:
cy.server()
cy.route({<your route details>}).as('aliasName')
Then later in the code, you can use
cy.wait('#aliasName').then(xhr => {
expect(xhr.responseBody.<your Item>).to.eql('something')
})

An output problem about nginx module?How to fix it?

I'm new to nginx and I'm trying to develope a simple nginx module,a handler module specifically. Although it's not what I really wanna do,I try to finish this task first. I'm trying to get the socketfd when a browser(or a client) connects to nginx.And I have get it successfully.However, when I tried to output something using dup2(),the nginx is always pending and just outputs nothing.Sometimes I can get output after a long time and once I stop nginx like nginx -s stop,and the output appears immediately.
Like this:
reach http://100.100.60.199/nc?search=123456
get
search=123456 HTTP/1.l
HOST
output
I have read some blogs about nginx module and I found that handler module has its own pattern (to my understanding?).For example, the output should be nginx_chain_t,and I should construct that chain instead of using dup2 like a regular c code.So I wonder if it's feasible to get output like the function below.
Here is my handler function:
static ngx_int_t ngx_http_nc_handler(ngx_http_request_t *r){
//ngx_int_t rc;
ngx_socket_t connfd = r->connection->fd;
int nZero=0;
//if(setsockopt(connfd,SOL_SOCKET,SO_SNDBUF,(const void*)&nZero,sizeof(nZero))==0)
if(setsockopt(connfd,IPPROTO_TCP,TCP_NODELAY,(const void*)&nZero,sizeof(int))==0){
setbuf(stdout,NULL);
setbuf(stdin,NULL);
setbuf(stderr,NULL);
dup2(connfd,STDOUT_FILENO);
dup2(connfd,STDERR_FILENO);
dup2(connfd,STDIN_FILENO);
printf("%s\n", r->args.data);
//close(connfd);
}
return NGX_OK;
}
So I wonder if it's feasible,how can I get things right using the method above or can anybody just say it's impossible and construct a chain is the only way?
I finally solved this problem by trying to understand how exactly nginx works.In short,all I need to do is to add a http header to the output.But it's not that easy like what I described.

golang http: how to route requests to handlers

I'm newbie on Golang and have a simple question about building a web server.
Saying that my web server has users so the users can change their names and their passwords. Here is how I design the URLs:
/users/Test GET
/users/Test/rename POST newname=Test2
/users/Test/newpassword POST newpassword=PWD
The first line is to show the information of the user named Test. The second and the third is to rename and to reset password.
So I'm thinking that I need to use some regular expression to match the HTTP requests, things like http.HandleFunc("/users/{\w}+", controller.UsersHandler).
However, it doesn't seem that Golang supports such a thing. So does it mean that I have to change my design? For example, to show the information of the user Test, I have to do /users GET name=Test?
You may want to run pattern matching on r.URL.Path, using the regex package (in your case you may need it on POST) This post shows some pattern matching samples. As #Eugene suggests there are routers/http utility packages also which can help.
Here's something which can give you some ideas, in case you don't want to use other packages:
In main:
http.HandleFunc("/", multiplexer)
...
func multiplexer(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
getHandler(w, r)
case "POST":
postHandler(w, r)
}
}
func getHandler(w http.ResponseWriter, r *http.Request) {
//Match r.URL.path here as required using switch/use regex on it
}
func postHandler(w http.ResponseWriter, r *http.Request) {
//Regex as needed on r.URL.Path
//and then get the values POSTed
name := r.FormValue("newname")
}
Unfortunately standard http router not to savvy. There are 2 ways:
Manually check methods, urls and extract usernames.
Use routers from other packages like
https://github.com/gorilla/mux
gorilla mix, echo gin-gonic etc

AWS API Gateway - change to 404 if query returns nothing

I have a Dynamodb table with a few fields - my_id is the PrimaryKey. In the API gateway I set up a response with a method that takes in a parameter {my_id}.
Then I have an Integration Request mapping template that takes the passed in parameter and queries the table to return all the fields that match.
Then I have an Integration response mapping template that cleans up the returned items the way I want.
This all works perfect.
The thing I can't figure out how to do is if the parameter that is passed in doesn't match anything in the table, how do I get it to change from a 200 status into a 404?
From what I can tell when the passed in parameter doesn't match anything it doesn't cause an error, it just doesn't return anything.
It seems like I need to change the mapping template on the Integration response to first check if the params are empty and then somehow tell it to change the response status.
I can find info about this type of thing with people using Lambda, but I am not using Lambda - just the Dynamodb table and the API Gateway.
You can use Mapping Template to convert the response that you get from DDB and overrride the response code. You can get more details in the link https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-override-request-response-parameters.html
If you are using cloud formation, you can do this by using below snippet
IntegrationResponses:
- StatusCode: "200"
ResponseTemplates:
application/json: |
{
"payload" : {
}
},
}
IntegrationResponses:
- StatusCode: "200"
ResponseTemplates:
application/json: |
#set($inputRoot = $input.path('$'))
#if($inputRoot.toString().contains("Item"))
$input.json("$")
#set($context.responseOverride.status = 200)
#else
#set($context.responseOverride.status = 404)
#end
Api gateway currently supports mapping the status code using the status code of the integration response (Here dynamodb response code). The only workaround is to use a lambda function which outputs different error messages that can be mapped using a error regex http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-method-settings-execution-console.html.

multiple response.WriteHeader calls in really simple example?

I have the most basic net/http program that I'm using to learn the namespace in Go:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
go HandleIndex(w, r)
})
fmt.Println("Starting Server...")
log.Fatal(http.ListenAndServe(":5678", nil))
}
func HandleIndex(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("Hello, World!"))
}
When I run the program and connect to localhost:5678 in Chrome, I get this in the console:
Starting Server...
/
2015/01/15 13:41:29 http: multiple response.WriteHeader calls
/favicon.ico
2015/01/15 13:41:29 http: multiple response.WriteHeader calls
But I don't see how that's possible. I print the URL, start up a new goroutine, write the header once, and give it a static body of Hello, World! It seems like one of two things is happening. Either something behind the scenes is writing another header or somehow HandleIndex is called twice for the same request. What can I do to stop writing multiple headers?
EDIT: It seems to have something to do with the go HandleIndex(w, r) line because if I remove go and just make it a function call instead of a goroutine, I don't get any issues and the browser gets it's data. With it being a goroutine, I get the multiple WriteHeader error and the browser doesn't show "Hello World." Why is making this a goroutine breaking it?
Take a look at the anonymous function you register as the handler of incoming requests:
func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
go HandleIndex(w, r)
}
It prints the URL (to the standard output) then calls HandleIndex() in a new goroutine and continues execution.
If you have a handler function where you do not set the response status before the first call to Write, Go will automatically set the response status to 200 (HTTP OK). If the handler function does not write anything to the response (and does not set the response status and completes normally), that is also treated as a successful handling of the request and the response status 200 will be sent back. Your anonymous function does not set it, it does not even write anything to the response. So Go will do just that: set the response status to 200 HTTP OK.
Note that handling each request runs in its own goroutine.
So if you call HandleIndex in a new goroutine, your original anonymous function will continue: it will end and so the response header will be set - meanwhile (concurrently) your started new goroutine will also set the response header - hence the "multiple response.WriteHeader calls" error.
If you remove the "go", your HandleIndex function will set the response header in the same goroutine before your handler function returns, and the "net/http" will know about this and will not try to set the response header again, so the error you experienced will not happen.
You already received a correct answer which addresses your problem, I will give some information about the general case (such error appears often).
From the documentation, you see that WriteHeader sends an http status code and you can't send more than 1 status code. If you Write anything this is equivalent to sending 200 status code and then writing things.
So the message that you see appears if you either user w.WriteHeader more than once explicitly or uses w.Write before w.WriteHeader.
From the documentation:
// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
What is happening in your case is that you are launching go HandleIndex from the handler.
The first handler finishes. The standard WriteHeader writes to the ResponseWriter. Then the go routine HandleIndex is launched and it also tries to write a header and write.
Just remove the go from HandleIndex and it will work.
the root cause is that you called WriteHeader more than once. from the source codes
func (w *response) WriteHeader(code int) {
if w.conn.hijacked() {
w.conn.server.logf("http: response.WriteHeader on hijacked connection")
return
}
if w.wroteHeader {
w.conn.server.logf("http: multiple response.WriteHeader calls")
return
}
w.wroteHeader = true
w.status = code
if w.calledHeader && w.cw.header == nil {
w.cw.header = w.handlerHeader.clone()
}
if cl := w.handlerHeader.get("Content-Length"); cl != "" {
v, err := strconv.ParseInt(cl, 10, 64)
if err == nil && v >= 0 {
w.contentLength = v
} else {
w.conn.server.logf("http: invalid Content-Length of %q", cl)
w.handlerHeader.Del("Content-Length")
}
}
}
so when you wrote once, the variable wroteHeader would be true, then you wrote header again, it wouldn't be effective and gave a warning "http: multiple respnse.WriteHeader calls".
actually the function Write also calls WriteHeader, so putting the function WriteHeader after the function Write also causes that error, and the later WriteHeader doesn't work.
from your case, go handleindex runs in another thread and the original already returns, if you do nothing, it will call WriteHeader to set 200. when running handleindex, it calls another WriteHeader, at that time wroteHeader is true, then the message "http: multiple response.WriteHeader calls" is output.
Yes, use HandleIndex(w, r) instead of go HandleIndex(w, r) will fix your issue, I think you have already figured that out.
The reason is simple, when handling multiple requests at the same time, the http server will start multiple goroutines, and your handler function will be called separately in each of the goroutines without blocking others.
You don't need to start your own goroutine in the handler, unless you practically need it, but that will be another topic.
Because modern browsers send an extra request for /favicon.ico which is also handled in your / request handler.
If you ping your server with curl for example, you'll see only one request being sent:
curl localhost:5678
To be sure you can add an EndPoint in your http.HandleFunc
http.HandleFunc("/Home", func(w http.ResponseWriter, r *http.Request)

Resources