* The Web, Continuations, and All That
@ 2012-01-22 18:46 Tobias Gerdin
2012-01-22 20:46 ` Ian Price
0 siblings, 1 reply; 13+ messages in thread
From: Tobias Gerdin @ 2012-01-22 18:46 UTC (permalink / raw)
To: Guile Users
Hello,
To get better acquainted with continuations I have been playing with
them in the context of web programming a bit. Much has been written
on the topic (in particular I find the Racket tutorials very
instructive), and here I would just like to share the small
experiments I did using delimited continuations and the Guile (web
server). Kudos to Andy for providing a very nice foundation.
The ingredients:
(use-modules (web server)
(web request)
(web response)
(web uri)
(sxml simple)
(ice-9 control))
To begin with, a simple request handler making use of a table to map
between request URI paths and other request handlers (also functions
as a continuation table):
(define dispatch-table (make-hash-table))
(define (handler request body)
(let* ((path (split-and-decode-uri-path (uri-path (request-uri request))))
(h (hash-ref dispatch-table (string->symbol (car path)))))
(if h
(% (h request body))
(values '((content-type . (text/plain)))
(string-append "Unknown page: " (car path))))))
So a prompt is installed just before the matching request handler is
dispatched to.
Next there is Christian Queinnec's `show' operator (see Christian
Queinnec's seminal "The Influence of Browsers on Evaluators or,
Continuations to Program Web Servers"), although here I've call it
`send-suspend', which is what it is called in Racket (actually, they
call it `send/suspend'):
(define (send-suspend make-response)
(abort (lambda (k)
(let ((k-uri-id (gensym "k"))) ; Use something more
sophisticated here!
(hash-set! dispatch-table k-uri-id k)
(make-response (format #f "/~a" k-uri-id))))))
So it's a something that when called will save the state of the
computation (the continuation `k') in the table and associate it with
a generated uri, and then apply that uri to a procedure that returns a
response object. The comment is there to draw attention to the fact
that security hinges on the generated uri being difficult to predict,
or else the user's session is open to hijacking. Additional security
measures could be put in place as well.
The point of the above is to able to write request handlers spanning
several requests in this style:
(define (hello-world r b)
(send-suspend
(lambda (k-url)
(values '((content-type . (text/html)))
(string-append "<a href=\"" k-url "\">hello</a>"))))
(values '((content-type . (text/html)))
"world!"))
To try it the handler needs to be mounted and the web server fired up,
pointed at the dispatching handler:
(hash-set! dispatch-table 'hello hello-world)
(run-server handler)
Visiting http://localhost:8080/hello should give a "hello" link
yielding a second page saying "world!". Since constructing the
response objects in the above way is tedious and verbose I made use of
the `respond' helper from the "Web Examples" section in the Guile
reference manual:
(define (hello-world2 r b)
(send-suspend
(lambda (k-uri)
(respond `((a (@ (href ,k-uri)) "hello")))))
(respond '("world!")))
(hash-set! dispatch-table 'hello2 hello-world)
Lexical scope is preserved over requests:
(define (count req body)
(let loop ((n 0))
(send-suspend
(lambda (k-uri)
(respond `((a (@ (href ,k-uri)) ,(number->string n))))))
(loop (1+ n))))
(hash-set! dispatch-table 'count count)
An common use-case is to return values from pages (such as data
provided by the user through forms):
;; Dirty hack to extract a single param from encoded form data
(define (get-name form-data)
(uri-decode (cadr (string-split form-data #\=))))
(define (get-greet req body)
(let ((req (send-suspend
(lambda (k-uri)
(respond `((form (@ (action ,k-uri) (method "GET))
"Your name: "
(input (@ (type text) (name name)))
(input (@ (type submit))))))))))
(respond `("Hi " ,(get-name (uri-query (request-uri req)))))))
(hash-set! dispatch-table 'greet get-greet)
Since the stored continuation is invoked with both the request and the
body you access the latter by making use of some multiple-values
binding form:
(use-modules (srfi srfi-11)) ; let-values
(use-modules (rnrs bytevectors)) ; utf8->string
(define (post-greet req body)
(let-values (((req body) (send-suspend
(lambda (k-uri)
(respond `((form (@ (action ,k-uri)
(method "POST"))
"Your name: "
(input (@ (type text)
(name name)))
(input (@ (type submit))))))))))
(respond `("Hi " ,(get-name (utf8->string body))))))
(hash-set! dispatch-table 'greet2 post-greet)
Happy hacking.
-Tobias
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-22 18:46 The Web, Continuations, and All That Tobias Gerdin
@ 2012-01-22 20:46 ` Ian Price
2012-01-23 21:11 ` Andy Wingo
` (2 more replies)
0 siblings, 3 replies; 13+ messages in thread
From: Ian Price @ 2012-01-22 20:46 UTC (permalink / raw)
To: Tobias Gerdin; +Cc: Guile Users
Tobias Gerdin <tgerdin@gmail.com> writes:
> Hello,
>
> To get better acquainted with continuations I have been playing with
> them in the context of web programming a bit. Much has been written
> on the topic (in particular I find the Racket tutorials very
> instructive), and here I would just like to share the small
> experiments I did using delimited continuations and the Guile (web
> server). Kudos to Andy for providing a very nice foundation.
It's always nice to see someone playing with continuations. Your example
is very similar to one I did a while back https://gist.github.com/1381107.
It would be if someone(nudge nudge) were to take the effort to make one
of these experiments practical, since a guile web framework seems to be
a common request.
--
Ian Price
"Programming is like pinball. The reward for doing it well is
the opportunity to do it again" - from "The Wizardy Compiled"
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-22 20:46 ` Ian Price
@ 2012-01-23 21:11 ` Andy Wingo
2012-01-30 19:17 ` Tobias Gerdin
2012-01-25 8:59 ` Antono Vasiljev
[not found] ` <4F1FC437.8020801@antono.info>
2 siblings, 1 reply; 13+ messages in thread
From: Andy Wingo @ 2012-01-23 21:11 UTC (permalink / raw)
To: Ian Price; +Cc: Guile Users
On Sun 22 Jan 2012 21:46, Ian Price <ianprice90@googlemail.com> writes:
> Tobias Gerdin <tgerdin@gmail.com> writes:
>
>> To get better acquainted with continuations I have been playing with
>> them in the context of web programming a bit. Much has been written
>> on the topic (in particular I find the Racket tutorials very
>> instructive), and here I would just like to share the small
>> experiments I did using delimited continuations and the Guile (web
>> server). Kudos to Andy for providing a very nice foundation.
>
> It's always nice to see someone playing with continuations.
Yes, that was beautiful. I'm happy you were able to get something
going, Tobias. Did you ever run into problems with non-resumable
continuations?
> Your example is very similar to one I did a while back
> https://gist.github.com/1381107.
Ah, thanks for that note, Ian.
> It would be if someone(nudge nudge) were to take the effort to make
> one of these experiments practical, since a guile web framework seems
> to be a common request.
Indeed!
Andy
--
http://wingolog.org/
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-22 20:46 ` Ian Price
2012-01-23 21:11 ` Andy Wingo
@ 2012-01-25 8:59 ` Antono Vasiljev
[not found] ` <4F1FC437.8020801@antono.info>
2 siblings, 0 replies; 13+ messages in thread
From: Antono Vasiljev @ 2012-01-25 8:59 UTC (permalink / raw)
To: guile-user
On 01/22/2012 11:46 PM, Ian Price wrote:
>> To get better acquainted with continuations I have been playing with
>> them in the context of web programming a bit. Much has been written
>> on the topic (in particular I find the Racket tutorials very
>> instructive), and here I would just like to share the small
>> experiments I did using delimited continuations and the Guile (web
>> server). Kudos to Andy for providing a very nice foundation.
>
> It's always nice to see someone playing with continuations. Your example
> is very similar to one I did a while back https://gist.github.com/1381107.
>
> It would be if someone(nudge nudge) were to take the effort to make one
> of these experiments practical, since a guile web framework seems to be
> a common request.
>
While i like the idea i should note that it does not scale. What if you
have 5 application servers (guile (import (web server)) running behind
reverse proxy. User may never fire needed continuation. Overall scaling
strategy for web is "avoid per node state" (see read-only filesystems on
Heroku).
PS: Ian, sorry for double reply.
amike,
antno
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
[not found] ` <4F1FC437.8020801@antono.info>
@ 2012-01-25 10:32 ` Ian Price
2012-01-30 19:03 ` Tobias Gerdin
0 siblings, 1 reply; 13+ messages in thread
From: Ian Price @ 2012-01-25 10:32 UTC (permalink / raw)
To: Antono Vasiljev; +Cc: guile-user
Antono Vasiljev <self@antono.info> writes:
>> It would be if someone(nudge nudge) were to take the effort to make one
>> of these experiments practical, since a guile web framework seems to be
>> a common request.
>
> While i like the idea i should note that it does not scale. What if you
> have 5 application servers (guile (import (web server)) running behind
> reverse proxy. User may never fire needed continuation. Overall scaling
> strategy for web is "avoid per node state" (see read-only filesystems on
> Heroku).
Well, no-one suggested that the current approach is practical. It isn't.
--
Ian Price
"Programming is like pinball. The reward for doing it well is
the opportunity to do it again" - from "The Wizardy Compiled"
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-25 10:32 ` Ian Price
@ 2012-01-30 19:03 ` Tobias Gerdin
2012-01-31 9:06 ` Ian Price
0 siblings, 1 reply; 13+ messages in thread
From: Tobias Gerdin @ 2012-01-30 19:03 UTC (permalink / raw)
To: Ian Price; +Cc: Guile Users
2012/1/25 Ian Price <ianprice90@googlemail.com>:
>>> It would be if someone(nudge nudge) were to take the effort to make one
>>> of these experiments practical, since a guile web framework seems to be
>>> a common request.
>>
>> While i like the idea i should note that it does not scale. What if you
>> have 5 application servers (guile (import (web server)) running behind
>> reverse proxy. User may never fire needed continuation. Overall scaling
>> strategy for web is "avoid per node state" (see read-only filesystems on
>> Heroku).
>
> Well, no-one suggested that the current approach is practical. It isn't.
As long as you have some way to redirect the client back to the same
application server things would scale quite well I think. You could
embed some token identifying the application server in the URL which
the reverse proxy could use to forward the request to the same
application server instance. New user sessions would still be
load-balanced using a round-robin scheme or something more
sophisticated.
Something even cooler would be to have serializable continuations like
in Racket (but it requires limiting the language somewhat), in which
case you could store the continuations in the URL (if they are not too
big), or perhaps in some distributed hashtable that all application
server instances could access.
-Tobias
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-23 21:11 ` Andy Wingo
@ 2012-01-30 19:17 ` Tobias Gerdin
2012-01-31 10:00 ` Andy Wingo
0 siblings, 1 reply; 13+ messages in thread
From: Tobias Gerdin @ 2012-01-30 19:17 UTC (permalink / raw)
To: Andy Wingo, Guile Users
2012/1/23 Andy Wingo <wingo@pobox.com>:
> On Sun 22 Jan 2012 21:46, Ian Price <ianprice90@googlemail.com> writes:
>> It's always nice to see someone playing with continuations.
>
> Yes, that was beautiful. I'm happy you were able to get something
> going, Tobias. Did you ever run into problems with non-resumable
> continuations?
At times I was scratching my head and wondering what was going on but
if you mean continuations that wouldn't resume due to a bug I do not
think so.
>> It would be if someone(nudge nudge) were to take the effort to make
>> one of these experiments practical, since a guile web framework seems
>> to be a common request.
>
> Indeed!
What I think would be nice would be to add a (web server control)
library making the most common web programming continuation operators
available for use in any framework built on top of (web server). This
would probably require installing a prompt in `run-server' somewhere,
(but that is not too expensive I think in the case it's never used?)
and having some policy on how the continuation table is to be managed.
One simple way would be to make use of a weak hashtable, although that
may not be ideal in all situations.
-Tobias
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-30 19:03 ` Tobias Gerdin
@ 2012-01-31 9:06 ` Ian Price
2012-02-04 9:53 ` Ian Price
[not found] ` <CAJiKzQMETO7g6YyqCbeoV0GD09gena6RK6q6bYhjTOe+wNq+Uw@mail.gmail.com>
0 siblings, 2 replies; 13+ messages in thread
From: Ian Price @ 2012-01-31 9:06 UTC (permalink / raw)
To: Tobias Gerdin; +Cc: Guile Users
Tobias Gerdin <tgerdin@gmail.com> writes:
> As long as you have some way to redirect the client back to the same
> application server things would scale quite well I think. You could
> embed some token identifying the application server in the URL which
> the reverse proxy could use to forward the request to the same
> application server instance. New user sessions would still be
> load-balanced using a round-robin scheme or something more
> sophisticated.
>
> Something even cooler would be to have serializable continuations like
> in Racket (but it requires limiting the language somewhat), in which
> case you could store the continuations in the URL (if they are not too
> big), or perhaps in some distributed hashtable that all application
> server instances could access.
A whole bunch of ideas were discussed on IRC a while back[1], and
serialisation was one of the issues I was most concerned about. As it
stands, when the program stops you lose all your existing continuations,
which may be undesirable.
Another important issue was brought up by Antono, which is that
continuation URLs effectively form a gc root, and you can't safely get
rid of continuations without possibly breaking some url. And even though
it you can just say "well, tough", you at least need some strategy for
periodically pruning your set of continuations.
I'd like to see you, or I, or someone, do something like this for guile,
but I think the first step is to look at existing continuation based
frameworks and see how they handle some of these issues.
1. http://rotty.yi.org/irclogs/freenode/%23guile/2012-01-10/
--
Ian Price
"Programming is like pinball. The reward for doing it well is
the opportunity to do it again" - from "The Wizardy Compiled"
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-30 19:17 ` Tobias Gerdin
@ 2012-01-31 10:00 ` Andy Wingo
2012-02-06 19:49 ` Tobias Gerdin
0 siblings, 1 reply; 13+ messages in thread
From: Andy Wingo @ 2012-01-31 10:00 UTC (permalink / raw)
To: Tobias Gerdin; +Cc: Guile Users
Hi Tobias,
On Mon 30 Jan 2012 20:17, Tobias Gerdin <tgerdin@gmail.com> writes:
> 2012/1/23 Andy Wingo <wingo@pobox.com>:
>> Did you ever run into problems with non-resumable continuations?
>
> At times I was scratching my head and wondering what was going on but
> if you mean continuations that wouldn't resume due to a bug I do not
> think so.
If a partial continuation includes a trip through C and then back to the
VM, it won't be resumable. Basically if you call a function that is
implemented in C and then that function does a scm_call(...), and you
abort from within the scm_call(...), the abort works but the
continuation will not be resumable.
This is an implementation restriction. We're trying to get around it by
having more things implemented in Scheme rather than C.
The reason for this restriction is that you can't capture part of the C
stack, then compose it with some other C continuation (i.e., splat it at
some other stack position).
E.g.:
scheme@(guile-user)> (call-with-prompt 'foo (lambda () (call-with-output-string (lambda args (abort-to-prompt 'foo)))) (lambda (k) k))
$1 = #<partial-continuation 2b63200>
scheme@(guile-user)> ($1)
ERROR: In procedure #<partial-continuation 2b63200>:
ERROR: Throw to key `vm-error' with args `(vm-run "Unrewindable partial continuation" (#<vm-continuation 2b73aa0>))'.
Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]>
I asked you this because I was wondering I wanted to know how we were
doing in practice: if you happened to run into this. So it's nice that
you didn't run into it :)
> What I think would be nice would be to add a (web server control)
> library making the most common web programming continuation operators
> available for use in any framework built on top of (web server). This
> would probably require installing a prompt in `run-server' somewhere,
> (but that is not too expensive I think in the case it's never used?)
> and having some policy on how the continuation table is to be managed.
> One simple way would be to make use of a weak hashtable, although that
> may not be ideal in all situations.
There is a prompt in the server. The handler is applied to the request,
body, and state values in a thunk, and that thunk is called in one of
these:
(define (with-stack-and-prompt thunk)
(call-with-prompt (default-prompt-tag)
(lambda () (start-stack #t (thunk)))
(lambda (k proc)
(with-stack-and-prompt (lambda () (proc k))))))
Andy
--
http://wingolog.org/
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-31 9:06 ` Ian Price
@ 2012-02-04 9:53 ` Ian Price
[not found] ` <CAJiKzQMETO7g6YyqCbeoV0GD09gena6RK6q6bYhjTOe+wNq+Uw@mail.gmail.com>
1 sibling, 0 replies; 13+ messages in thread
From: Ian Price @ 2012-02-04 9:53 UTC (permalink / raw)
To: guile-user
As a follow up to this, I'd just like to point out this blog post on
racket-lang.org.
http://blog.racket-lang.org/2012/02/zack-gallers-experience-with-stateful.html
--
Ian Price
"Programming is like pinball. The reward for doing it well is
the opportunity to do it again" - from "The Wizardy Compiled"
^ permalink raw reply [flat|nested] 13+ messages in thread
* Fwd: The Web, Continuations, and All That
[not found] ` <CAJiKzQMETO7g6YyqCbeoV0GD09gena6RK6q6bYhjTOe+wNq+Uw@mail.gmail.com>
@ 2012-02-06 19:40 ` Tobias Gerdin
0 siblings, 0 replies; 13+ messages in thread
From: Tobias Gerdin @ 2012-02-06 19:40 UTC (permalink / raw)
To: Guile Users
2012/1/31 Ian Price <ianprice90@googlemail.com>:
> [...]
>
> A whole bunch of ideas were discussed on IRC a while back[1], and
> serialisation was one of the issues I was most concerned about. As it
> stands, when the program stops you lose all your existing continuations,
> which may be undesirable.
>
> Another important issue was brought up by Antono, which is that
> continuation URLs effectively form a gc root, and you can't safely get
> rid of continuations without possibly breaking some url. And even though
> it you can just say "well, tough", you at least need some strategy for
> periodically pruning your set of continuations.
There are various solutions to this problem, such as associating a
time-to-live with each entry in the continuation table (like the
typical handling of session objects in traditional web programming),
or make use of a weak hashtable.
> I'd like to see you, or I, or someone, do something like this for guile,
> but I think the first step is to look at existing continuation based
> frameworks and see how they handle some of these issues.
Yes, could be fruitful to study other systems like the Racket web
server and Seaside. But I think that at this point it would just be
nice with something working and hackable, but maybe not a
super-scalable production framework.
-Tobias
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-01-31 10:00 ` Andy Wingo
@ 2012-02-06 19:49 ` Tobias Gerdin
2012-02-07 8:48 ` Andy Wingo
0 siblings, 1 reply; 13+ messages in thread
From: Tobias Gerdin @ 2012-02-06 19:49 UTC (permalink / raw)
To: Andy Wingo; +Cc: Guile Users
2012/1/31 Andy Wingo <wingo@pobox.com>:
> Hi Tobias,
>
> On Mon 30 Jan 2012 20:17, Tobias Gerdin <tgerdin@gmail.com> writes:
>
>> 2012/1/23 Andy Wingo <wingo@pobox.com>:
>>> Did you ever run into problems with non-resumable continuations?
>>
>> At times I was scratching my head and wondering what was going on but
>> if you mean continuations that wouldn't resume due to a bug I do not
>> think so.
>
> If a partial continuation includes a trip through C and then back to the
> VM, it won't be resumable. Basically if you call a function that is
> implemented in C and then that function does a scm_call(...), and you
> abort from within the scm_call(...), the abort works but the
> continuation will not be resumable.
>
> This is an implementation restriction. We're trying to get around it by
> having more things implemented in Scheme rather than C.
>
> The reason for this restriction is that you can't capture part of the C
> stack, then compose it with some other C continuation (i.e., splat it at
> some other stack position).
>
> E.g.:
>
> scheme@(guile-user)> (call-with-prompt 'foo (lambda () (call-with-output-string (lambda args (abort-to-prompt 'foo)))) (lambda (k) k))
> $1 = #<partial-continuation 2b63200>
> scheme@(guile-user)> ($1)
> ERROR: In procedure #<partial-continuation 2b63200>:
> ERROR: Throw to key `vm-error' with args `(vm-run "Unrewindable partial continuation" (#<vm-continuation 2b73aa0>))'.
>
> Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
> scheme@(guile-user) [1]>
>
> I asked you this because I was wondering I wanted to know how we were
> doing in practice: if you happened to run into this. So it's nice that
> you didn't run into it :)
So if a Scheme primitive the program calls happens to be implemented
in C you would run into this problem? As you saw my examples were
quite minimal and maybe not representative of what you do "in
practice". But I agree that given this implementing more things in
Scheme definitely makes sense.
>> [...]
>
> There is a prompt in the server. The handler is applied to the request,
> body, and state values in a thunk, and that thunk is called in one of
> these:
>
> (define (with-stack-and-prompt thunk)
> (call-with-prompt (default-prompt-tag)
> (lambda () (start-stack #t (thunk)))
> (lambda (k proc)
> (with-stack-and-prompt (lambda () (proc k))))))
Great, all set then!
-Tobias
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: The Web, Continuations, and All That
2012-02-06 19:49 ` Tobias Gerdin
@ 2012-02-07 8:48 ` Andy Wingo
0 siblings, 0 replies; 13+ messages in thread
From: Andy Wingo @ 2012-02-07 8:48 UTC (permalink / raw)
To: Tobias Gerdin; +Cc: Guile Users
Hi Tobias,
Clarifying one point:
On Mon 06 Feb 2012 20:49, Tobias Gerdin <tgerdin@gmail.com> writes:
>> scheme@(guile-user)> (call-with-prompt 'foo (lambda () (call-with-output-string (lambda args (abort-to-prompt 'foo)))) (lambda (k) k))
>> $1 = #<partial-continuation 2b63200>
>> scheme@(guile-user)> ($1)
>> ERROR: In procedure #<partial-continuation 2b63200>:
>> ERROR: Throw to key `vm-error' with args `(vm-run "Unrewindable partial continuation" (#<vm-continuation 2b73aa0>))'.
>
> So if a Scheme primitive the program calls happens to be implemented
> in C you would run into this problem?
Only if that C primitive invokes a Scheme procedure (for example, using
scm_call), and you capture the continuation from within that
recursively-called Scheme procedure. Like `call-with-output-string'
above.
Cheers,
Andy
--
http://wingolog.org/
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2012-02-07 8:48 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-01-22 18:46 The Web, Continuations, and All That Tobias Gerdin
2012-01-22 20:46 ` Ian Price
2012-01-23 21:11 ` Andy Wingo
2012-01-30 19:17 ` Tobias Gerdin
2012-01-31 10:00 ` Andy Wingo
2012-02-06 19:49 ` Tobias Gerdin
2012-02-07 8:48 ` Andy Wingo
2012-01-25 8:59 ` Antono Vasiljev
[not found] ` <4F1FC437.8020801@antono.info>
2012-01-25 10:32 ` Ian Price
2012-01-30 19:03 ` Tobias Gerdin
2012-01-31 9:06 ` Ian Price
2012-02-04 9:53 ` Ian Price
[not found] ` <CAJiKzQMETO7g6YyqCbeoV0GD09gena6RK6q6bYhjTOe+wNq+Uw@mail.gmail.com>
2012-02-06 19:40 ` Fwd: " Tobias Gerdin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).