There isn't much context with this because it really is a situation where something should work, but it just doesn't.
I am using the martini framework. In one handler I'm using this:
session.Set("deployed", "true")
r.Redirect("/myOtherURL", http.StatusSeeOther)
Whereby 'session' is the sessions.Session object passed through to the handler.
In the handler that loads myOtherURL, I'm using session.Get but nothing is being returned. I printed out all of the session content and the 'deployed' is not present.
What could be causing this problem? What could I possibly be missing out? I would give more context if I could but it really is as simple as this.
Just to extend on my comment/help others in the future:
When you set a cookie without an explicit Path value, the cookie takes on the current path.
Cookies are only sent for that path and paths below - not above - e.g.
Cookie set for /modules when you implicitly set it for the first time via session.Set(val, key)
Cookie is sent for /modules, /modules/all and /modules/detail/12
Cookie is NOT sent for /about or /
This can be fixed by explicitly setting the path:
var store = sessions.NewCookieStore([]byte("secret123"))
func main() {
store.Options.Path = "/"
...
}
Be aware that you may not want to send a cookie for all routes (which is what / will do) - so use judgement.
Related
I recently started rewriting some of my Python services in Go to speed them up and came across this section of the gin documentation:
https://github.com/gin-gonic/gin#goroutines-inside-a-middleware
So I understand the instructions, but I'm trying to understand why? What is the significance of making a copy, and what problem is introduced if I do not make a copy of context for goroutines within handlers?
Gin-gonic is async itself which makes it great.
If you are using concurrency within your handlers it's very likely you will face situation when Context, which is struct goes
outdated since it's holding the data from each request (parameters, keys protected by mutex)
empty, which will cause a fallback to default Context
here's how it looks like:
type Context struct {
writermem responseWriter
Request *http.Request
Writer ResponseWriter
Params Params
handlers HandlersChain
index int8
fullPath string
engine *Engine
params *Params
skippedNodes *[]skippedNode
// This mutex protects Keys map.
mu sync.RWMutex
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]any
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
Accepted []string
// queryCache caches the query result from c.Request.URL.Query().
queryCache url.Values
// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
// or PUT body parameters.
formCache url.Values
// SameSite allows a server to define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests.
sameSite http.SameSite
}
In order to foresee such issues and race conditions - you must use Copy whenever your handlers are using go concurrency
here's a quote from gin-gonic repository:
// Copy returns a copy of the current context that can be safely used outside the request's scope.
// This has to be used when the context has to be passed to a goroutine.
I have a subrouter with prefix /api/a and I want to redirect, all requests to endopoint with prefix api/b/{aid}, to api/a after calling a function (which sets context.) How to achieve this?
When there is a call to /api/a/<whatever>, I take aid from cookies and add to request context but there are other endpoints where cookies doesn't contain aid parameter so I take it as a route param. What I want is any call to /api/b/{aid}/<whatever> is redirected to /api/a/<whatever> after I set aid in its request context (from route param).
I absolutely don't want to use client side redirects, he doesn't need to know.
Also I know that I can pass directly to any function with http.Handler signature. Its exactly what I want to avoid.
There are a fixed set of endpoints in which cookie won't be set (and thats intended, so I don't want to set one) and this set would always be prefixed by say /api/b/{aid}.
Since there are 150+ endpoints and handlers already configured to the subrouter prefixed /api/a (lets call it A). There is another subrouter B with prefix /api/b/{aid}. I want to redirect all calls to subrouter B to subrouter A after calling a function. It won't be logical to copy the entire list of handlers for separate subrouter if I want the exact same thing.
You cannot set a value in the request context, then redirect to some other URL and expect to get that value. You have to pass that value as part of the redirect using a query param, or a cookie, etc. Or you can do this without a redirect, and call the handler for the redirected URL directly.
If you want to do this with a cookie, you can get the aid in a handler, set a cookie containing aid using http.SetCookie, and redirect using http.Redirect. The other handler should receive the cookie.
If you want to do this using a query param, you can write the redirect URL with the added query parameter, redirect using http.Redirect, and parse it in the other handler.
If you want to do this without a redirect, you can get aid in one handler, put it into the request context, and call the other handler directly:
request.WithContext(context.WithValue(request.Context(),key,aid))
otherHandler(writer,request)
You can automate forwarding to the other caller using Router.Match with a single handler:
Register a handler for pathprefix /api/b/{aid}/
Handler parses aid
Handler rewrites a url /api/a/<remaining path>, sets request.URL to it, and uses router.Match to find the corresponding path under /api/a subtree
Handler calls the handler for /api/a/<remaining path>
Experiencing really weird behavior on a site I maintain, https://conservationx.com/
1) Global.asax Session_Start, simply checks for a cookie of a specific name (visitor) and if not present, we issue one.
protected void Session_Start()
{
var req = HttpContext.Current.Request;
var cookie = request.Cookies["visitor"];
if (cookie == null)
{
var cookie = new HttpCookie("visitor", sguid);
cookie.Shareable = false; // Do not store in outputcache
cookie.Expires = DateTime.UtcNow.AddYears(1);
response.SetCookie(cookie);
2) In local Debug, I can visit and step into it just fine, first request - Incognito window consistently gets me Set-Cookie header on first request.
3) Once deployed, I will never, ever see that Cookie issued on first request. I consistently see it on second request.
4) In addition to seeing Set-Cookie header not appear until the second request in DevTools Network tab, I've also added logging to the Session_Start body, and wrapped it all in try/catch, logging any exceptions. That likewise consistently fails to fire on first visit. This particular site checks if you're logged in once the page is done loading, with a call to /account/navaccountinfo - fresh browsers/Incognito windows will always fail to get a cookie set landing on any page on the site, then see the cookie finally set on that secondary load, and sure enough our logs are filled with cookies being set on the request to that secondary request URL.
Is this a known issue with deploying ASP.Net MVC to IIS 7.5? We are using OWIN with Identity Framework if that has any impact?
Speculating, I wonder if this is related:
OutputCache VaryByCustom cookie value
I set cookie.Shareable = false because the visitor Id is meant to be unique per browser. Can't be giving it out to multiple people via the Server's OutputCache. The initial page visited, like / or /about, has an OutputCacheAttribute set and is visited via GET. The followup, /account/navaccountinfo, is visited via POST, and so, obviously never cached. So I wonder if this is actually a bad interaction between OutputCache and cookie.Shareable = false.
This is not a GOOD answer, but this is an answer and I'm eagerly interested in other more elegant solutions.
The Problem
We don't cache much or at all in Dev, we do on the Server, thus the Session not firing until a Post fired - the user was seeing a quick cached response from IIS, which means most of the Asp.Net pipeline never spins up, especially Session_Start. Posts always bust through the cache and voila, second call - which was always a Post - always saw the cookie set, and always saw a log entry for the Session.
In addition, the fact that setting HttpCookie.Shareable = false essentially throws a firebomb into your caching by default is very poorly documented, and by not using much caching in Dev, we weren't really seeing the major damage this flag does.
The Bad Solution (Elegant Proposals Welcome)
We're already making heavy use of OutputCache VaryByCustom, where we do some somewhat creative things like serving the general public one version of the site, and logged-in members each their own. The details of how this works are beyond the scope of this issue, but suffice to say we use VaryByCustom in Global.asax:
public override string GetVaryByCustomString(HttpContext context, string key)
{
return VaryByCustomKeyHandler.HandleCacheKey(key, context, Application);
}
The key is just our compile-safe way of indicating what kind of variance we want for a Controller Action, and I'll skip the details of that, but, the HandleCacheKey method now looks like this:
string uidInSetVisitorCookie = context.Request.ServerVariables["SetVisitorCookie"];
if (uidInSetVisitorCookie != null)
return uidInSetVisitorCookie; // Force vary
So, if the user is supposed to receive a Cookie, we basically tell the OutputCache that this user and this user alone should get their very own copy of this page, for now. We do that by providing a cache key that happens to be their randomly generated Uid. That Uid is generated in BeginRequest, which, I've verified, does fire even when OutputCache is ultimately going to handle the Response:
protected void Application_BeginRequest(object sender, EventArgs ev)
{
var req = (Brass9.Web.Visiting.Req)HttpContext.Current.Request;
string uid = AnonCookieManager.O.GetUid(req);
bool hadNoCookie = String.IsNullOrEmpty(uid);
if (hadNoCookie)
{
uid = (Brass9.ShortGuid.NewGuid()).ToString();
AnonCookieManager.O.PutCookie(HttpContext.Current.Response, uid);
req.ServerVariables["SetVisitorCookie"] = uid;
}
So there's a number of light library calls in there, but quickly:
Req is a simple wrapper class for the 2 annoying Request classes Asp.Net uses that have a ton of overlap yet fail to share a base class.
GetUid() gets the visitor cookie out of the Request cookies, if any.
ShortGuid is a lot like this
Most importantly, PutCookie() has been modified to participate in this workaround, like so:
var cookie = new HttpCookie(CookieName, sguid);
//cookie.Shareable = false;
// Cookies shouldn't be shared, but, we signal to vary the OutputCache instead
// of blowing up OutputCache
cookie.Expires = DateTime.UtcNow.AddYears(1);
response.SetCookie(cookie);
I get sessions, coming from PHP I used to
<?php
session_start();
$_SESSION["key"] = "val";
echo $_SESSION["key"];
?>
Set one or more keys and their values serverside and be able to retrieve or overwrite it until the session expires.
The same with gorilla/sessions
var(
sessionStore *sessions.CookieStore
sessionSecret []byte = make([]byte, 64)
session *sessions.Session
)
func init(){
sessionSecret = []byte("12345678901234567890123456789012")
sessionStore = sessions.NewCookieStore(sessionSecret)
session = sessions.NewSession(sessionStore, "session_name")
}
func SetSessionHandler(w http.ResponseWriter, r *http.Request) {
session, _ = sessionStore.Get(r, "session_name")
session.Values["key"] = "val"
session.Save(r, w)
}
func GetSessionHandler(w http.ResponseWriter, r *http.Request) {
session, _ = sessionStore.Get(r, "session_name")
fmt.FPrintln(session.Values["key"])
}
Now I don't get what the point of gorilla/context is.
I know what a context is but... I don't know how it fits in the big picture.
It says that it's bound to the current request. Another question here on stackoverflow said that "simply using gorilla/context should suffice" in the context of Writing Per-Handler Middleware.
But if it's request bound... err.. syntax error, does not compute. If a duck floats on water then witches are made from wood. And because ducks also float on water if her weight is the same as that of a duck she must be a witch. Or something like that ;)
And how could this be useful as a middleware "manager" when it's request-bound, I can't set it globally. Could you perhaps show an example of how a gorilla/sessions could be used with gorilla/context?
As the person who asked that other question:
gorilla/context allows you to store data in the request. If you have some middleware that does some pre-processing on a request before deciding to continue (i.e. anti-CSRF), you might want to store a token in the request so your handler can pass it to the template. The gorilla/context documentation explains it well:
... a router can set variables extracted from the URL and later application handlers can access those values, or it can be used to store sessions values to be saved at the end of a request. There are several others common uses.
You may also want to store data in the session: error messages from form submissions, a user ID, or the 'canonical' version of the CSRF token for that visitor will likely be stored here. If you try to store an error message in the request context, and then re-direct the user, you'll lose it (that's a new request).
So why would you use context over sessions? It's lighter, and allows you to de-couple parts of your application (often, HTTP middleware!) from each other.
Example:
Request comes in
CSRF middleware checks the session for an existing CSRF token. Does not exist, so it sets one.
It also passes this new token (via the request context!) to the handler that renders your form, so it can render it in the template (otherwise you would have to pull the token from the session again, which is wasted effort)
Request is done.
New request on form submission
The token still persists in the session, so we can compare it to the submitted token from the form.
If it checks out, we proceed to process the form
If not, we can save an error in the session (a flash message; i.e. one that is erased after reading) and re-direct.
This re-direction is a new request, and therefore we can't pass the error message via the request context here.
An example.
I'm writing this multi-community-forum software.
Now I have a gorilla/mux router that serves different content for the main domain and another router that serves different content filtered by subdomain.domain.tld.
Context is very useful here, else you would repeat yourself over and over again. So if I know that for the subdomain router every request would do string operations to find out the subdomain name and check if it exists in the database I could just do this here (in context) for every request and then store the subdomain name in a context variable.
And likewise if a forum's category slug or forum slug or a thread slug is set pass it to the handler, keep the processing that needs to be done in "context" and have less code in your handlers.
So the advantage of context is essentially to keep code DRY.
Like elihrar wrote, his example of a CSRF token. If you know that you need to check for the CSRF token on each request - don't duplicate this check in every handler that needs to do this, instead write a context wrapper ( / http.Handler) and do this for every request.
I've looked around here but I can't find a matching solution to my problem here.
What I want to do is 1) clean up the URLs with GET params in them and 2) save those GET params in session variables.
This will also need to be done on all files on the site, so the GET params could get passed to any file on the server in any order.
So, for example, if one of the files is:
http://mydomain.com/page.php?a=1&b=2&c=3
and another is:
http://mydomain.com/anotherpage.php?b=2&a=1
I'd need those rewritten or redirected to /page.php and /anotherpage.php, respectively, while storing the GET params in session, so having $_SESSION['a'] = 1, etc.
I've managed to do this (kind of) by including a function called rewrite() in every file's header (before anything else) and going through all the variables in there, storing them in a session and then redirecting the file through header() to $_SERVER['SCRIPT_NAME'].
And it kinda works, but what I'm seeing now is issues with tracking scripts out there - when I try to integrate user tracking scripts I get errors because of a lot of redirects. Postbacks from other apps/websites are throwing 301/302 errors because of redirects as well.
So I was wondering is there a slicker method of just taking a QUERY_STRING for each called URL, storing all the key/value pairs from there in session cookies with key being the name of the session, value being the value of the session and just simply loading the clean SCRIPT_NAME instead without a possibility of endless redirection and all?
Thanks!