What is the meaning of Go/Gin debug output - (x handlers) - go

In the following Go/Gin debug output, what is the meaning of (5 handlers). As you can see, I obviously have more than 5 handlers.
[GIN-debug] GET / --> .../handlers.APIDetail (5 handlers)
[GIN-debug] POST /signup --> .../handlers.SignUp (5 handlers)
[GIN-debug] POST /signin --> .../handlers.SignIn (5 handlers)
[GIN-debug] POST /refresh-token --> .../handlers.RefreshToken (5 handlers)
[GIN-debug] POST /verify-email --> .../handlers.VerifyEmailVerificationToken (5 handlers)
[GIN-debug] POST /resend-verification-email --> .../handlers.ResendEmailVerificationEmail (5 handlers)
[GIN-debug] POST /reset-password --> .../handlers.ResetPassword (5 handlers)
[GIN-debug] POST /change-password --> .../handlers.ChangePassword (5 handlers)
[GIN-debug] PATCH /users/me --> .../handlers.UpdateProfile (5 handlers)
[GIN-debug] POST /signout --> .../handlers.SignOut (5 handlers)
[GIN-debug] GET /orgs/:id --> .../handlers.GetOrganizations (5 handlers)
[GIN-debug] GET /orgs --> .../handlers.GetOrganizations (5 handlers)

It should be the number of handlers in each route's handler chain, i.e. the maximum number of handlers, including middleware, that will be executed when a request is routed to a certain endpoint.
Relevant code from Gin sources #latest:
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
if IsDebugging() {
nuHandlers := len(handlers)
handlerName := nameOfFunction(handlers.Last())
if DebugPrintRouteFunc == nil {
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
} else {
DebugPrintRouteFunc(httpMethod, absolutePath, handlerName, nuHandlers)
}
}
}
If you are setting a few global middlewares, e.g. with router.Use, before declaring the routes, and no route has per-route middlewares, that explains why the number is always the same.
For example, consider the following:
r.Use(func(*gin.Context) { fmt.Println("FIRST") })
r.GET("/foo", func(c *gin.Context) {
c.Status(http.StatusOK)
})
r.Use(func(*gin.Context) { fmt.Println("SECOND") })
r.GET("/bar", func(c *gin.Context) {
c.Status(http.StatusOK)
})
This prints:
[GIN-debug] GET /foo --> main.main.func2 (2 handlers)
[GIN-debug] GET /bar --> main.main.func4 (3 handlers)
Because /foo's chain has the FIRST middleware, and the handler itself (2), and /bar's chain has both FIRST and SECOND middleware, plus the handler itself (3).

Related

Swagger generation is ignoring SecurityDefiniton

Cannot understand one thing, why swag init command is generating everything, except SecurityDefinition block
For example, this is my code on main function:
// #title Swagger API
// #version 1.0
// #description This is a documentation for PlatOps Core tool.
// #termsOfService http://platops.com/
// #license.name Apache 2.0
// #license.url http://www.apache.org/licenses/LICENSE-2.0.html
// #host test.com
// #BasePath /v2
// #securityDefinitions.apikey Bearer
// #in header
// #name Authorization
And how I'm using :
// GetUser godoc
// #Summary Retrieves users list
// #Tags users
// #Accept json
// #Produce json
// #Success 200 {object} string
// #Failure 400,404 {object} string
// #Failure 500 {object} string
// #Router /api/users/ [get]
// #Security Bearer
From this, it's generating everything, this path also, but no SecurityDefinition block inside.
I'm trying to implement this one:
https://github.com/swaggo/swag#security
P.S. If I put already generated text on the generated doc file, it's working... so why it cannot generate it?
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "Bearer",
"in": "header"
},
}
Add your security definitions in the file which you are passing in the swag init -g flag... Mostly it should be your main.go file... it should show up once you do that

AWS Cognito PreventUserExistenceErrors setting with UserMigration Lambda in Golang

I am trying to use PreventUserExistenceErrors on a Cognito pool with a user migration Lambda trigger. Cognito documentation says:
With user migration Lambda trigger, Cognito will return a simulated response for non existing users when an empty response was returned by the Lambda trigger.
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html
I don't know how to get this to work. I isolated this by setting up a pool and attaching a simple trigger which always returns an empty response (we're writing the triggers in Go on the project):
package main
import (
"fmt"
"github.com/aws/aws-lambda-go/lambda"
)
func Handle(event interface{}) (interface{}, error) {
fmt.Println("nil")
// also tried:
// return struct{}{}, nil
// return "", nil
return nil, nil
}
func main() {
lambda.Start(func(event interface{}) (interface{}, error) {
return Handle(event)
})
}
I wrote a client to try to log in to the pool with a username that doesn't exist. With the PreventUserExistenceErrors enabled I expect the error to be the same as if the trigger was not attached to the pool:
Error executing "InitiateAuth" on "https://cognito-idp.eu-west-1.amazonaws.com"; AWS HTTP error: Client error: `POST https://cognito-idp.eu-west-1.amazonaws.com` resulted in a `400 Bad Request` response:
{"__type":"NotAuthorizedException","message":"Incorrect username or password."}
But I get a different error that shows the trigger failed:
HTTP error: Client error: `POST https://cognito-idp.eu-west-1.amazonaws.com` resulted in a `400 Bad Request` response:
{"__type":"UserNotFoundException","message":"Exception migrating user in app client 4i2oaatugssocd44d40kb55kni"}
I tried returning nil, empty string and empty struct and all three show the error from the trigger.
What's the correct way to return "empty response" from Cognito Lambda trigger in Go?

Beego POST method always looks for template file

I am writing a simple login/logout feature using Beego.
My init() in router.go file is given below:
func init() {
beego.Router("/", &controllers.MainController{})
beego.Router("/login", &controllers.AuthController{})
beego.Router("/verify", &controllers.AuthController{}, "post:Verify")
}
In AuthController:
func (c *AuthController) Verify() {
email := c.GetString("email")
password := c.GetString("password")
fmt.Printf("email: %v password: %v", email, password)
}
I just want to print the details to browser (for debugging purpose) and later redirect it to another page if the user is authenticated. But the issue here is that Beego always looks for a template file and throws the below error:
can't find templatefile in the path:views/authcontroller/verify.tpl
How can I stop Beego from acting like that or am I doing something that is "not-beego-like"?
if you don't set response type, beego is always looking for default template path.
if you don't want to render template you can set response type as;
func (c *AuthController) Verify() {
defer c.ServerJSON() // response type
email := c.GetString("email")
password := c.GetString("password")
fmt.Printf("email: %v password: %v", email, password)
}

Understanding of Prometheus

I would like to try simple example of using Prometheus.
I have downloaded server binaries
I have started simple code, but with few modifications
var addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
func main() {
flag.Parse()
http.Handle("/metrics", promhttp.Handler())
http.Handle("/test/{id}", myHandler(promhttp.Handler()))
log.Fatal(http.ListenAndServe(*addr, nil))
}
func myHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, you've hit %s\n", r.URL.Path)
next.ServeHTTP(w, r)
})
}
Questions:
I assume Prometheus is monitoring tool and I would like to monitor endpoints metrics and /test/{id} separately. Did I get the idea correctly by creating several handlers and using promhttp.Handler() as middleware?
What else apart of quantity and latency of requests can be monitored in e.g. simple web app with database?
To follow up on #David Maze's answer, the default handler promhttp.Handler is for reporting metrics. (gathers from all registered handlers and reports them on request).
Unfortunately, it is not a generic http middleware that gives you any metrics out of the box.
I have seen many of go's web frameworks have some sort of community prometheus middleware (gin's) that give metrics out of the box (latency, response code, request counts, etc).
The go prometheus client library has examples of how to add metrics to your application.
var (
httpRequests = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Number of http requests.",
},
)
)
func init() {
// Metrics have to be registered to be exposed:
prometheus.MustRegister(httpRequests)
}
func myHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
httpRequests.Inc()
fmt.Fprintf(w, "hello, you've hit %s\n", r.URL.Path)
next.ServeHTTP(w, r)
})
}
As for your second question lots :) Google advocates for monitoring 4 golden signals:
https://landing.google.com/sre/book/chapters/monitoring-distributed-systems.html#xref_monitoring_golden-signals
These are
Traffic - Throughput - Counts/Time
Latency - distribution / histogram
Errors - HTTP Response codes/explicit error counts
Saturation - resource queues ie if there is a goroutine pool how many goroutines are active at a given time
In my experie8inces it has also been helpful to have visibility of all the interactions between your appl8ication and your database (ie the 4 golden signals applied to your database):
number of calls made to db from app
latencies of calls made
results (err/success) of your calls made to determine availability (success / total)
saturation available from your db driver (https://golang.org/pkg/database/sql/#DBStats)

golang static stop index.html redirection

package main
import (
"log"
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("."))
http.Handle("/", fs)
log.Println("Listening...")
http.ListenAndServe(":3000", nil)
}
So I have a index.html file and want server to stop showing it.
The docs for FileServer state that:
As a special case, the returned file server redirects any request
ending in "/index.html" to the same path, without the final
"index.html".
So /index.html is redirected to /, /foo/bar/index.html is redirected to /foo/bar/.
To avoid this register an additional handler for the special case.
http.HandleFunc("/index.html", func(w http.ResponseWriter, r *http.Request) {
f, err := os.Open("index.html")
if err != nil {
// handle error
return
}
http.ServeContent(w, r, "index.html", time.Now(), f)
})
Please note I'm using ServeContent insead of ServeFile because ServeFile handles /index.html requests the same way as FileServer.
There's no redirection going on, the default file to render when requesting a directory is index.html. The directory listing is a fallback for when this file isn't found, so you can't get a directory listing without removing the index.html file.
If you want a directory listing, you'll have to write it out yourself, which you can then format and style however you choose. The basic structure is very simple if you want to write it directly, take the internal dirList function for example:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, "<pre>\n")
for _, d := range dirs {
name := d.Name()
if d.IsDir() {
name += "/"
}
url := url.URL{Path: name}
fmt.Fprintf(w, "%s\n", url.String(), htmlReplacer.Replace(name))
}
fmt.Fprintf(w, "</pre>\n")
I think there are 3 ways:
Make a PR which allow accessing /index.html without redirect to golang's net/http/fs library. https://github.com/golang/go/issues/53870
Hack your code: replace /index.html with / to stop 301 redirect, e.g., https://github.com/ahuigo/go-lib/commit/1a99191d5c01cf9025136ce8ddb9668f123de05c#diff-67e8621fbb99281a50c089bae53d4874663d3d21ca5e90809ec207c070029351R44
Customize your own http fs handler instead of official tools.

Resources