web2 response.setCookie and request.getCookie shortcuts?
I'm still having fun with web2, it is such a huge improvment over twisted.web and I'm absolutely thrilled with it. However, I have a few suggestions: 1. It would be supreme if render() and locateChild() just provided me the Request object; the incantation to ctx.locate() seems a tedious an unnecessary step. 2. I would love a request.getCookie('cookie-name') and response.setCookie('cookie-name','cookie-value', path = '/') helper; that adds the given cookie to the current list. 3. Make the 'path' in the Cookie constructor and setCookie default to '/'. Almost all of the time you want your cookies to be site-wide; adding path = '/' to each request adds a point of failure that makes the system less usable. Kind Regards, Clark P.S. Following is sample cookie code that works with web2 --- from twisted.internet import reactor, defer from twisted.web2 import server, http, resource, channel, iweb from twisted.web2.http_headers import Cookie class Toplevel(resource.Resource): addSlash = True def render(self, ctx): request = ctx.locate(iweb.IRequest) found = False for cookie in request.headers.getHeader('cookie',[]): if cookie.name == 'testing': print "found", cookie.value found = True response = http.Response(stream="Hello monkey!") if not found: print "not-found" cookie = Cookie("testing","womble",path="/") response.headers.setHeader('Set-Cookie',(cookie,)) return response # Standard twisted application Boilerplate from twisted.application import service, strports application = service.Application("demoserver") site = server.Site(Toplevel()) s = strports.service('tcp:8080', channel.HTTPFactory(site)) s.setServiceParent(application)
On Wed, 2005-11-16 at 17:42 -0500, Clark C. Evans wrote:
I'm still having fun with web2, it is such a huge improvment over twisted.web and I'm absolutely thrilled with it. However, I have a few suggestions:
1. It would be supreme if render() and locateChild() just provided me the Request object; the incantation to ctx.locate() seems a tedious an unnecessary step.
You're in luck context is going away. Far far away never to be seen again. As soon as an appropriate method for per-request data can be decided upon.
2. I would love a request.getCookie('cookie-name') and response.setCookie('cookie-name','cookie-value', path = '/') helper; that adds the given cookie to the current list.
I'm personally against polluting the request and response interfaces with this kind of stuff, in my opinion cookies are just headers, they shouldn't be special. I wouldn't be opposed to higher level cookie management that presented a similar interface to both the client and the server developer, but I probably won't have any interest in writing it.
3. Make the 'path' in the Cookie constructor and setCookie default to '/'. Almost all of the time you want your cookies to be site-wide; adding path = '/' to each request adds a point of failure that makes the system less usable.
I'm all for sane-defaults, but I'm not sure this is one. I'll let foom decide. -David
On Wed, 16 Nov 2005 16:22:19 -0800, David Reid <dreid@dreid.org> wrote:
On Wed, 2005-11-16 at 17:42 -0500, Clark C. Evans wrote:
I'm still having fun with web2, it is such a huge improvment over twisted.web and I'm absolutely thrilled with it. However, I have a few suggestions:
You're in luck context is going away. Far far away never to be seen again. As soon as an appropriate method for per-request data can be decided upon.
Do we have a list of use-cases somewhere that detail what "per-request data" is really a requirement? Most places I've seen the context actually *used* in this manner it has been a disaster...
2. I would love a request.getCookie('cookie-name') and response.setCookie('cookie-name','cookie-value', path = '/') helper; that adds the given cookie to the current list.
I'm personally against polluting the request and response interfaces with this kind of stuff, in my opinion cookies are just headers, they shouldn't be special. I wouldn't be opposed to higher level cookie management that presented a similar interface to both the client and the server developer, but I probably won't have any interest in writing it.
"content-length" and "connection" are just headers too: but the code has to support them because they're in the RFC. Technically speaking cookies aren't in the *same* RFC, but they do have a standard of their own, rfc2109. If there were multiple possible implementations of cookies, I'd say there should be an adapter or something to keep it separate. Since there is just the one way to do them, though, I think that it's reasonable to treat the standards as merged, and just put the methods on request to handle it. Pragmatically speaking cookies are not an "optional" extension to HTTP/1.1.
3. Make the 'path' in the Cookie constructor and setCookie default to '/'. Almost all of the time you want your cookies to be site-wide; adding path = '/' to each request adds a point of failure that makes the system less usable.
I'm all for sane-defaults, but I'm not sure this is one. I'll let foom decide.
I can speak from experience that cce's right; when you need a path, you know it, and you can pass it. When you don't need a path, what you mean (in every case I've ever worked on) is /.
Glyph, On Thu, Nov 17, 2005 at 06:57:11AM -0500, glyph@divmod.com wrote: | Do we have a list of use-cases somewhere that detail what "per-request | data" is really a requirement? Most places I've seen the context | actually *used* in this manner it has been a disaster... The system that I've layered over Twisted that I'm now porting to web2 is a request re-write system. Basically, I view request handling as a series of stages which _alter_ the request (I don't particularly like the special status given to path segments in web2, btw; this should be much higher-level logic). For example: 1. I have one stage that converts /k=v/ path segments into cookie entries, so that user agents lacking a good cookie mechansim can still use the parts of my application that require "context" handling. 2. I have another stage that simply looks for a in-memory session object, and attaches it to the request (for further down-stream stages). 3. My next stage does authentication; if a session object is already marked authorized, this step is a no-op. Otherwise, it handles one of many authentication protocols. 4. My application has many many ways to ask for the same thing, so my next stage simply 'canonicalizes' the request. This replaces various short-cuts in the request's path segments or arguments with more tedious but explicit request. 5. Following is an 'authorization' step; which verifies that the current identity is allowed to perform the activity expressed by the (now canonicalized) request. 6. At this point; depending on many factors, my processing tree has many branches -- but at all stages, I'm passing around a request that is being continually modified to reflect the status of the request and where it goes next. I use a state-transition graph to make sure that my request gets in the correct queue. So, this is what I mean by per-request state. Since each stage of execution happens by a distinct Resource, a great deal of information needs to be stored in the Request object (or what ever you want to call it). Now if a ClarkRequest extends a HttpRequest to add what I need, so be it; right now my code is sloppy -- I just modify the request object's dict. Perhaps it is poor pratice; but with a decent regression test it really isn't a problem at all. I only have a handful of variables that are needed. Anyway Glyph, I'm not quite certain I understand what you're saying or recommending. If you could detail a bit more, it would be wonderful. | >I'm personally against polluting the request and response interfaces | >with this kind of stuff, in my opinion cookies are just headers, they | >shouldn't be special. I wouldn't be opposed to higher level cookie | >management that presented a similar interface to both the client and the | >server developer, but I probably won't have any interest in writing | >it. You got to put it this stuff somewhere. An adapter is quite a kludgy, but I guess workable. The current solution, however, is ugly. | Pragmatically speaking cookies are not an "optional" extension to HTTP/1.1. Hear! Hear! Clark
On Thu, 17 Nov 2005 15:55:37 -0500, "Clark C. Evans" <cce@clarkevans.com> wrote:
Anyway Glyph, I'm not quite certain I understand what you're saying or recommending. If you could detail a bit more, it would be wonderful.
I'll try to provide more detail later. I'm also trying to digest your "stage"-based approach here. Although certainly possible to implement in terms of t.w/w2/nevow abstractions it seems rather alien to the mindset involved.
On Thu, Nov 17, 2005 at 03:55:37PM -0500, Clark C. Evans wrote:
So, this is what I mean by per-request state. Since each stage of execution happens by a distinct Resource, a great deal of information needs to be stored in the Request object (or what ever you want to call it). Now if a ClarkRequest extends a HttpRequest to add what I need, so be it; right now my code is sloppy -- I just modify the request object's dict. Perhaps it is poor pratice; but with a decent regression test it really isn't a problem at all. I only have a handful of variables that are needed.
I'm also clobbering the req dict namespace for similar reasons: def start_timer(self, req): req.start_time = time.time() def render(self, req): self.start_timer(req) and I'm also doing session authentication by hand with the cookie + db, so I plan to clobber more of the req for the session. Using interfaces would avoid the clobbering but it's slower. And what was the final decision about getCookie/setCookie? If they're added I can change my code to them too ;). Thanks.
On Nov 17, 2005, at 6:57 AM, glyph@divmod.com wrote:
"content-length" and "connection" are just headers too: but the code has to support them because they're in the RFC. Technically speaking cookies aren't in the *same* RFC, but they do have a standard of their own, rfc2109.
If there were multiple possible implementations of cookies, I'd say there should be an adapter or something to keep it separate. Since there is just the one way to do them, though, I think that it's reasonable to treat the standards as merged, and just put the methods on request to handle it. Pragmatically speaking cookies are not an "optional" extension to HTTP/1.1.
Agreed, cookies are important. But, there's not just one possible implementation. To start with, you've got three specs: netscape, RFC2109, and RFC2965. I believe RFC2109 is essentially dead, and shouldn't be cared about. The code is there to parse and unparse all the cookie headers in both Netscape and RFC2965 format, in the same way the code is there to parse and unparse content-length. As cce indicated, you can directly manipulate the parsed header representation. However, a higher-level interface to cookies should be implemented. However, this higher level implementation is not entirely trivial, which is why I haven't done it yet. As an example of some of the non- trivialities: The Netscape cookie spec is insecure, often allowing random other hosts on the internet to set cookies for your host(*). Uptake of the RFC2965 spec is slow but gaining somewhat, and supporting it would be a good move. It adds the ability to determine what host/path the cookie was set for, so your application can verify that it isn't getting cookies from an unexpected source. So, the high level interface has to be more than a simple dictionary, it has to be filterable by acceptable source host/port/path. But only when you know you're talking to a client that supports RFC2965, which you can tell because of various attributes. And when setting a cookie, probably both Set-Cookie and Set-Cookie2 should be sent. And stuff like that. All doable, and should be done, but to say there's only one obvious way to do things is not exactly true. :) (*): Most common example: User follows a link to http:// attacker.co.uk/. Said webpage makes a request in the background to http://myserver.co.uk/ and I setup a new session and send back a cookie with the session id: SID=1234. Attacker then sends back to user a cookie: SID=1234, domain=.co.uk and redirects user to http:// myserver.co.uk/. Myserver gets the SID cookie, and uses that session. Attacker now has access to all data available through user's session. D'oh! This was originally supposed to be prevented by the rule implemented in browsers that you cannot set a prefix cookie for domains without a dot, but that only protects domains which sell the second level (e.g. .com, .org, .net). James
On Wed, 16 Nov 2005 17:42:34 -0500, "Clark C. Evans" <cce@clarkevans.com> wrote:
I'm still having fun with web2, it is such a huge improvment over twisted.web and I'm absolutely thrilled with it. However, I have a few suggestions:
1. It would be supreme if render() and locateChild() just provided me the Request object; the incantation to ctx.locate() seems a tedious an unnecessary step.
2. I would love a request.getCookie('cookie-name') and response.setCookie('cookie-name','cookie-value', path = '/') helper; that adds the given cookie to the current list.
3. Make the 'path' in the Cookie constructor and setCookie default to '/'. Almost all of the time you want your cookies to be site-wide; adding path = '/' to each request adds a point of failure that makes the system less usable.
Agreed on all points. Especially that first one. ;)
:> >I'm still having fun with web2, it is such a huge improvment over :> >twisted.web and I'm absolutely thrilled with it. However, I have :> >a few suggestions: :> :> > 1. It would be supreme if render() and locateChild() just provided :> > me the Request object; the incantation to ctx.locate() seems a :> > tedious an unnecessary step. i also noticed this talks about removing context from the web2, and nevow. i dont know how much is this connected, but i would like to hear what other people think about this. currently i am working on one multilingual finance application (that somebody else wrote and i have to maintain). it is one big beast that was working in Woven and now is changed to work in Nevow. so obviously from the start it is not designed in the best possible way. it uses nevow.i18n and i18n uses context to store the information about current language. and some of my renderers are using context to get information about the language and blah blah.i am just interested, the moment context goes away, where will this kind of information be stored? i am aware it depends from application and design, but i would like to hear some suggestions or something. for instance urls are: /ngode/en/account/1021/report /ngode/account/1021/report /ngode/hr/account/1021/report will i be forced to carry my language flag around in my data model and learn my loaders and renderers to get from data model what template to load or maybe you guys are thinking that Request object could be used to carry this kind of information? so far context looked like a nice place where to put it and data does not change (just representation) according to the selected language. aco -- http://aco.mi2.hr/
On Thu, 17 Nov 2005 13:23:26 +0100, Aleksandar Erkalovic <aerkalov@mi2.hr> wrote:
will i be forced to carry my language flag around in my data model and learn my loaders and renderers to get from data model what template to load or maybe you guys are thinking that Request object could be used to carry this kind of information? so far context looked like a nice place where to put it and data does not change (just representation) according to the selected language.
This is a good use case, but your language flag *should* be in your data model. It's simply a mistake to put it in the context. For example: user A (who speaks en_US) is browsing your site. They perform an action which impacts user B (who speaks fr_FR). You need to generate an email to user B. You look at the context: I guess the email should be in english (whoops!). If you don't know more about a user other than that they've selected a language to view your site in, then you should have a "virtual user", an anonymous user object that pages can refer to, which does not implement the full range of behavior of a persistent user with an account, but does implement language-related methods.
On Thu, Nov 17, 2005 at 08:26:36AM -0500, glyph@divmod.com wrote: | On Thu, 17 Nov 2005 13:23:26 +0100, Aleksandar Erkalovic wrote: | >will i be forced to carry my language flag around in my data model and | >learn my loaders and renderers to get from data model what template to | >load or maybe you guys are thinking that Request object could be used | >to carry this kind of information? The acceptable languages that the _response_ is allowed to take can be found in the 'Accept-Language' header. This has the advantage of allowing more than one language choice, eighted by preference and typically auto-configured by your browser from the operating system defaults. In most browsers this setting can be changed; for example, in Firefox go to about:config and navigate to intl.accept_languages. That said, if you have an authenticated user, you might want to provide a server-side override mechanism so that people using an internet cafe' in a foreign country are given their primary language. However, this is more of a 'Avatar' property. You can use a path segment to do this I suppose; but it is not standard and is quite ugly [1]. | This is a good use case, but your language flag *should* be in your data | model. It's simply a mistake to put it in the context. For example: | user A (who speaks en_US) is browsing your site. They perform an action | which impacts user B (who speaks fr_FR). You need to generate an email | to user B. You look at the context: I guess the email should be in | english (whoops!). This is totally unrelated. Of course, if an action is going to be carried out _beyond_ the response; then the application needs to know about the users and respond appropriately. *winks* Best, Clark [1] On a related note, I use a generic PathArgs resource that "consumes" path segments that have an equal sign in them. This resource then modifies the cookies of the incoming request as if they were sent via a Cookie header. The advantage of this approach is that an arbitrary number of /key=value/ path segments can occur in the top of the URL if the browser doesn't support cookies. Then a SessionManager resource sits right below the PathArgs and looks for the 'session' cookie, etc.
On Thu, 17 Nov 2005 15:29:34 -0500, "Clark C. Evans" <cce@clarkevans.com> wrote:
The acceptable languages that the _response_ is allowed to take can be found in the 'Accept-Language' header. This has the advantage of allowing more than one language choice, eighted by preference and typically auto-configured by your browser from the operating system defaults. In most browsers this setting can be changed; for example, in Firefox go to about:config and navigate to intl.accept_languages.
In this case, since the 'request' object is available, simply pass the appropriate data from the header in question down to the object which needs to be rendered. In this case there is even less need for the context: a regular HTTP request encapsulates all the data you need!
On Nov 17, 2005, at 9:50 PM, glyph@divmod.com wrote:
On Thu, 17 Nov 2005 15:29:34 -0500, "Clark C. Evans" <cce@clarkevans.com> wrote:
The acceptable languages that the _response_ is allowed to take can be found in the 'Accept-Language' header. This has the advantage of allowing more than one language choice, eighted by preference and typically auto-configured by your browser from the operating system defaults. In most browsers this setting can be changed; for example, in Firefox go to about:config and navigate to intl.accept_languages.
In this case, since the 'request' object is available, simply pass the appropriate data from the header in question down to the object which needs to be rendered. In this case there is even less need for the context: a regular HTTP request encapsulates all the data you need!
Of course as noted before, it is often a requirement that a user be able to override this setting via an interface in the web application itself. Ideally that wouldn't be required, but idealism isn't often achievable when web browsers are involved. :) The override may take the form of a cookie, data in a session, part of the url, or whatever else app specific. In the way I'd envision creating an application, processing the language pref inputs into the single "preferred languages order" attribute takes place in a resource near the top of the graph, for use by all resources under it in the application. Said resource has to communicate the information somehow. I like CCE's description of his processing stages -- it seems to match the way I think as well, although he takes it to more of an extreme than I would. James
participants (6)
-
Aleksandar Erkalovic
-
Andrea Arcangeli
-
Clark C. Evans
-
David Reid
-
glyph@divmod.com
-
James Y Knight