xR34P3Rx
xR34P3Rx

Reputation: 395

Golang Gin: Headers were already written. Wanted to override status code 301 with 200

I'm developing a control panel and had hired some people to build it for me. They all bailed and I'm left having to cleanup the spaghetti.

What I need to do is this:

Just a simple login process. Problem is that when the login succeeds, the console enters into this redirect loop as you can see below:

[GIN] 2023/02/21 - 15:43:32 | 301 |  1.224601041s |             ::1 | POST     "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:33 | 200 |    787.3905ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:33 | 200 |  197.989875ms |             ::1 | GET      "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:34 | 200 |  817.293166ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:34 | 200 |  206.107791ms |             ::1 | GET      "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:35 | 200 |  792.954375ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:35 | 200 |  201.972708ms |             ::1 | GET      "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:36 | 200 |  840.773625ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:36 | 200 |  198.680125ms |             ::1 | GET      "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:37 | 200 |  897.679708ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:37 | 200 |  200.759917ms |             ::1 | GET      "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:38 | 200 |   795.39975ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:38 | 200 |     196.538ms |             ::1 | GET      "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:39 | 200 |  844.680709ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:39 | 200 |  180.598084ms |             ::1 | GET      "/login"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:40 | 200 |  814.666208ms |             ::1 | GET      "/dashboard"
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
[GIN] 2023/02/21 - 15:43:40 | 200 |     210.281ms |             ::1 | GET      "/login"

Now, since I'm picking up the slack of old devs, I'm still studying/new to Golang and Gin so bare with me...

From what I understand, main() is configuring the routes, middleware, loading templates then running the engine.

main.go

func main() {
    //gin.SetMode(gin.ReleaseMode) // uncomment for production

    // Startup Tasks
    startup()
    logging.LogInfo("Ran Startup Tasks...")

    // Configure Engine
    hostPort := fmt.Sprintf(
        "%s:%d",
        dataManagers.LoadConfig().Bshost,
        dataManagers.LoadConfig().Bsport)
    webEngine := gin.Default()
    webEngine.SetTrustedProxies([]string{hostPort})
    logging.LogInfo("Configured Engine...")

    // Load Middleware
    store := cookie.NewStore([]byte(randstr.String(64)))
    webEngine.Use(sessions.Sessions("session", store))
    webEngine.Use(errorHandler.ErrorsHandler500())
    logging.LogInfo("Loaded Middleware...")

    // Configure Routes
    pubRoutes := webEngine.Group("/")
    privRoutes := webEngine.Group("/")
    routes.PublicRoutes(pubRoutes)
    privRoutes.Use(middleware.AuthRequired)
    routes.PrivateRoutes(privRoutes)
    logging.LogInfo("Configured Routes...")

    // Non Routables
    webEngine.NoRoute(errorHandler.ErrorsHandler404())
    logging.LogInfo("Configured Non-Routables...")

    // Load Template Files
    LoadTemplates(webEngine)
    logging.LogInfo("Loaded Templates...")

    // Start the Gin Engine
    err := webEngine.Run(hostPort)
    logging.LogInfo("...BlockSuite-WebUI Loaded")
    logging.Catch(err)
}

When / is accessed, I'm redirected to /login which brings up the login form.

I submit the form with valid credentials and it redirects me to /dashboard. I don't know if redirect is the correct thing to do after a successful sign-in, that is what the original dev did and it worked fine.

routes.go

func PublicRoutes(webEngine *gin.RouterGroup) {
    webEngine.GET("/login", entry.LoginGetHandler)
    webEngine.POST("/login", entry.LoginPostHandler)
    webEngine.GET("/", other.IndexGetHandler())
}
func PrivateRoutes(webEngine *gin.RouterGroup) {
    dashboardRoutes := webEngine.Group("/dashboard")
    {
        dashboardRoutes.GET("/", dashboard.DashboardGetHandler)
    }
}

login.go

func LoginGetHandler(context *gin.Context) {
    user := utility.GetUserSession(context).Get("userEmail")
    if user != nil {
        context.Redirect(http.StatusMovedPermanently, "/dashboard")
    }
    context.HTML(http.StatusOK, "login.html", gin.H{
        "siteKey":    dataManagers.GetRecaptchaSettings().SiteKey,
        "enabled":    dataManagers.GetRecaptchaSettings().Enabled,
        "content":    "",
        "success":    "",
        "serverLogo": brand.GetBrandLogo(),
        "title":      "Welcome back",
    })
}
func LoginPostHandler(context *gin.Context) {
    user := utility.GetUserSession(context).Get("userEmail")
    if user != nil {
        context.Redirect(http.StatusMovedPermanently, "/dashboard")
        //return
    }
    userEmail := utility.Sanitize(context.PostForm("email"))
    password := utility.Sanitize(context.PostForm("password"))
    rememberme := utility.Sanitize(context.PostForm("rememberme"))
    //captcha := context.PostForm("g-recaptcha-response")
    if !utility.IsEmailValid(userEmail) {
        context.HTML(http.StatusBadRequest, "login.html", gin.H{"content": "Please enter a valid email address"})
        return
    }
    /*if helpers2.RecaptchaCheck(captcha) || dataManagers.GetConfig().SiteKey != "" {
        // success
    } else {
        if dataManagers.GetConfig().Enabled {
            context.HTML(http.StatusBadRequest, "login.html", gin.H{"content": "Please verify captcha"})
            return
        }
    }*/
    if utility.EmptyUserPass(userEmail, password) {
        context.HTML(http.StatusBadRequest, "login.html", gin.H{"content": "Email and password cannot be empty"})
        return
    }

    if utility.CheckForWhiteSpaces(userEmail, password) != nil {
        context.HTML(http.StatusBadRequest, "login.html", gin.H{"content": "Username and password can't contain spaces"})
        return
    }
    if !utility.CheckUserPass(userEmail, password) {
        context.HTML(http.StatusUnauthorized, "login.html", gin.H{"content": "Incorrect username or password"})
        return
    }
    utility.NewUserSession(context, userEmail)
    if rememberme == "yes" {
        utility.SetSessionAge(context)
    }
    context.Redirect(http.StatusMovedPermanently, "/dashboard")
}

And then, the /dashboard page is supposed to be loaded.

dashboard.go

func DashboardGetHandler(context *gin.Context) {
    user := utility.GetUserSession(context).Get("userEmail")
    db := dataManagers.GetDB()
    if user == nil {
        context.Redirect(http.StatusMovedPermanently, "/login")
    }
    [...]
    context.HTML(http.StatusOK, "dashboard.html", gin.H{
        "info":       info,
        "imageUrl":   utility.GetImage(user),
        "serverLogo": brand.GetBrandIcon(),
        "title":      "Dashboard",
        "serverName": dataManagers.GetServerInfo().Servername,
    })
}

(In the dashboard.go code, I omitted the code that pulls data into the dashboard as it is long and don't think it is that necessary.)


func DashboardGetHandler() gin.HandlerFunc {
    return func(context *gin.Context) {
    [...]
    }
}

I'm completely out of ideas and don't know where to go from here. Thanks!

Upvotes: 0

Views: 1802

Answers (1)

xR34P3Rx
xR34P3Rx

Reputation: 395

Thanks everyone that pitched in to help. I got with the previous dev and he helped me figure out what was wrong.

In his code, he had created a middleware func that, for some reason, checked the session again. That piece of code was checking old vars that did not exist in the session cookies. Because of that, I was getting kicked back to the login screen.

So, all I did was remove that middleware since I was handling that in login.go anyways.

Upvotes: 0

Related Questions