Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using scs with Echo #5

Closed
cxjava opened this issue Oct 23, 2016 · 7 comments
Closed

Using scs with Echo #5

cxjava opened this issue Oct 23, 2016 · 7 comments

Comments

@cxjava
Copy link

cxjava commented Oct 23, 2016

Is there a way to use the middle ware provided by scs within echo? I try to do as below way, but failed. Can you give me some suggestion?

package main

import (
    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    "github.com/labstack/echo/middleware"
    "github.com/alexedwards/scs/session"
    "github.com/alexedwards/scs/engine/memstore"
    "net/http"
)

func main() {
    sessionManager := session.Manage(memstore.New(0))
    e := echo.New()
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(standard.WrapMiddleware(sessionManager))
        e.SetDebug(true)

    e.GET("/", func(c echo.Context) error {
        err := session.PutString(c.Request().(*standard.Request).Request, "username", "admin")
        if err != nil {
            c.Logger().Error("session.PutString:", err)
            return err
        }

        if msg, err := session.GetString(c.Request().(*standard.Request).Request, "username"); err != nil || len(msg) == 0 {
            c.Logger().Info("session.GetString:", msg)
        }

        return c.String(http.StatusOK, "Hello, World!")
    })

    e.Run(standard.New(":1323"))
}
@alexedwards
Copy link
Owner

alexedwards commented Oct 23, 2016

Unfortunately, I believe this is a bug with Echo which will affect anyone who wants to use the new net/http request.Context() and request.WithContext() functions (like SCS uses under the hood).

For example, here's a more generalized example, with no SCS stuff in it:

package main

import (
    "context"
    "net/http"

    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
)

func fooMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := context.WithValue(r.Context(), "foo", "bar")
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func main() {
    e := echo.New()
    e.Use(standard.WrapMiddleware(fooMiddleware))

    e.GET("/", func(c echo.Context) error {
        foo, ok := c.Request().(*standard.Request).Request.Context().Value("foo").(string)
        if !ok {
            return c.String(http.StatusInternalServerError, "could not find foo")
        }

        return c.String(http.StatusOK, foo)
    })

    e.Run(standard.New(":1323"))
}

You would expect the handler to output a 200 status and the content "bar", but it throws an error instead. It looks like, at some point, Echo loses the data which the middleware has been stored in the (net/http) request context.

I've opened an issue on the Echo repository: labstack/echo#685

@cxjava
Copy link
Author

cxjava commented Oct 24, 2016

@alexedwards Thanks for your work!

@alexedwards
Copy link
Owner

@cxjava You're welcome.

There's no changes that need to be made to SCS on this (just Echo), so I'll close this.

@cxjava
Copy link
Author

cxjava commented Nov 17, 2016

@alexedwards I try to use this one into echo v3. For the example 1, works fine, the result as expected. But for the example 2, I can't get the expected message result Hello world! from path /put. I know there are some external work and code I need to do, Can you give me any suggestions?

package main

import (
    "context"
    "net/http"

    "github.com/labstack/echo"
)

func fooMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := context.WithValue(r.Context(), "foo", "bar")
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func main() {
    e := echo.New()
    e.Use(echo.WrapMiddleware(fooMiddleware))

    e.GET("/", func(c echo.Context) error {
        foo, ok := c.Request().Context().Value("foo").(string)
        if !ok {
            return c.String(http.StatusInternalServerError, "could not find foo")
        }

        return c.String(http.StatusOK, foo)
    })

    e.Start(":1323")
}
package main

import (
    "log"
    "net/http"

    "github.com/alexedwards/scs/engine/cookiestore"
    "github.com/alexedwards/scs/session"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

// HMAC authentication key (hexadecimal representation of 32 random bytes)
var hmacKey = []byte("f71dc7e58abab014ddad2652475056f185164d262869c8931b239de52711ba87")

// AES encryption key (hexadecimal representation of 16 random bytes)
var blockKey = []byte("911182cec2f206986c8c82440adb7d17")

func main() {
    // Create a new keyset using your authentication and encryption secret keys.
    keyset, err := cookiestore.NewKeyset(hmacKey, blockKey)
    if err != nil {
        log.Fatal(err)
    }
    // Create a new CookieStore instance using the keyset.
    engine := cookiestore.New(keyset)
    sessionManager := session.Manage(engine)
    e := echo.New()
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(echo.WrapMiddleware(sessionManager))

    e.GET("/put", func(c echo.Context) error {
        err := session.PutString(c.Request(), "message", "Hello world!")
        if err != nil {
            http.Error(c.Response().Writer(), err.Error(), 500)
        }
        return c.String(http.StatusOK, "put")
    })
    e.GET("/get", func(c echo.Context) error {
        msg, err := session.GetString(c.Request(), "message")
        if err != nil {
            http.Error(c.Response().Writer(), err.Error(), 500)
        }
        return c.String(http.StatusOK, "result "+msg)
    })

    e.Start(":1323")
}

@dxvgef
Copy link

dxvgef commented Dec 24, 2016

@cxjava Did you solve the problem? I also want to integrate echo v3 and SCS.

@cxjava
Copy link
Author

cxjava commented Dec 24, 2016

@dxvgef no...

@alexedwards
Copy link
Owner

Sorry for the (very) slow reply.

This is happening because the session cookie is never being written by the SCS middleware. I think the session cookie isn't being written because Echo's WrapMiddleware doesn't actually pass the underlying http.ResponseWriter onwards -- it passes it's own c.Response() object instead. So the session manager's Write method is never being called.

func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc {
	return func(next HandlerFunc) HandlerFunc {
		return func(c Context) (err error) {
			m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				c.SetRequest(r)
				err = next(c)
			})).ServeHTTP(c.Response(), c.Request())
			return
		}
	}
}

The only 'fix' I can see is to call session.Save manually in your handlers which modify the session data, like so:

	e.GET("/put", func(c echo.Context) error {
		err := session.PutString(c.Request(), "message", "Hello world!")
		if err != nil {
			log.Println(err.Error())
		}
		err = session.Save(c.Response(), c.Request())
		if err != nil {
			log.Println(err.Error())
		}
		return c.String(http.StatusOK, "put")
	})

This essentially writes the session data and cookie, instead of relying on the middleware to do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants