You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Mark Lundquist <ml...@comcast.net> on 2005/04/01 20:54:54 UTC

Proposed fix: crazy infinite loop w/

Hi,

I tried passing the continuation ID as a hidden form parameter, using 
<ft:continuation-id>.  Until now, I've always put continuation IDs in 
the URI, but in this application I really need to do it this way (and 
if I can get this to work right, I'm going to do it this way again in 
the future!).

The problem is that after the continuation is resumed, the response to 
that request is generated by an internal redirect (via sendPage()) of 
the request, which is of course matched against the sitemap, but since 
the request still contains the "continuation-id" parameter, it hits on 
that matcher and re-invokes the continuation, and it's "déjà vu all 
over again", forever and ever, amen.

So, (you smugly say :-), I should just move my continuation-id matcher 
to the end of the pipeline, so that it only matches if nothing else 
did.

Well, the way I have designed the page flow in this app — which I think 
is the Right Way, BTW — absolutely depends on always matching on a 
continuation-id parameter in the "flow-originating request", and having 
this in turn work in a "sane" manner.  And there is absolutely no use 
whatsoever for the continuation-id parameter, once the continuation has 
been resumed.  I don't think I should have to redesign my page flow to 
something "worse", just to work around an onerous constraint on 
matching the continuation-id! :-)

See below for an example scenario, representative of the sort of page 
flow I'm implementing.
But first, the proposed fix:

I've added this method to the oac.environment.Request interface:

	public void killContinuation();

In an HttpRequest, this sets a flag which causes 
getParameter("continuation-id") to return null.  This is a workaround 
for the lack of a removeParameter() method in HttpServletRequest (the 
delegate).

Now, over to the flowscript side of CForms... in 
Form.prototype.showForm(), after the call to sendPageAndWait(), I call 
cocoon.request.killContinuation().

Works like a charm.  No more infinitous loopage!

So, my questions:

• Is this a good fix?  There isn't some better one that I am missing, 
is there?

• My fix might seem like hacky special-case-ism.  But it also seems 
like adding a general removeParameter() method to these wrappers, while 
certainly possible (it'd just be a HashSet), would be overkill to solve 
a general problem that doesn't really exist (after all there is a 
reason that HttpServletRequest has gotten along nicely all this time 
without such a method).  But maybe I'm wrong about that, and someone 
will tell my why I really should add this as a removeParameter() 
method...  WDYT?

I've prototyped this in the v2 version of Form, but I'll add it to "v1" 
and v3 before submitting a patch.

-=-=-=--=

A simple example of why I need this: Suppose we are doing "lightweight 
authentication"  — i.e., using pure flowscript, w/o the AuthFW.  This 
is super-simple, but to break it way down in excruciating detail:

1) The user requests a protected resource "/foo".
2) The sitemap dispatches to a flowscript controller function that 
handles this request
3) Since this is a protected resource, the controller calls a 
flowscript authorize() function
3) The user isn't logged in, so authorize() invokes a login form (a 
CForms form).  (N.B.: the browser is not redirected to "the login 
page".  The address bar shows "/foo").
3) The users fills out and submits the form, which is a <form 
action=""> so it requests "/foo" again, this time with some 
(additional?) POST parameters: userName, password, and continuation-id.
4) The continuation matcher in the sitemap hits, and the continuation 
is resumed.
5) The login was successful, so the authorize() function returns 
(otherwise, it just loops and reissues the login form, i.e. return == 
success).
6) The caller of of authorize() proceeds and generates the reply to the 
request for "/foo".
7) The browser receives the reply and renders the page.  The address 
bar shows "/foo", the resource that was originally requested.

Notes:

•  (3) is the essential reason why I need this change.  If "/foo" is 
requested with a continuation-id POST parameter, the continuation must 
be resumed.

• (7) Has important implications.  I say "the address bar shows 
'/foo'", but what I really mean is that "/foo" was the URI in the 
reply.  We may not care what the user sees in their address bar, but 
the other thing this controls is the browser's interpretation of 
relative links, and we may care very much about this!

• A side benefit of this more flow-oriented approach is that the 
application becomes a little more crisply responsive, since we're not 
issuing external redirects all over the place in order to get various 
pages rendered.


Re: Proposed fix: crazy infinite loop w/

Posted by Ben Pope <be...@gmail.com>.
Hi,

I saw that approach in the samples, but thought it would be nice to be 
able to navigate the site passing parameters with POST sometimes, so 
instead I came up with this:

<map match ...>
    <map:select type="request-parameter">
       <map:parameter name="parameter-name" value="continuation-id"/>
          <map:when test="{request-param:continuation-id}">
             <map:call continuation="{request-param:continuation-id}"/>
          </map:when>
          <map:otherwise>
             <map:call function...


If the continuation parameter is not present, the test returns null, and 
goes to otherwise, which calls the form handler.  If the parameter 
exists (regardless of whether GET/POST) it calls the continuation.

Just a thought...

Ben Pope.


Eric E. Meyer wrote:
> This is how I work with hidden continuation ids since my form posts.
> 
> You can select based upon the request:method - continuing only on POST:
> 
>            <map:select type="oacl-simple">
>                <map:parameter name="value" value="{request:method}"/>
>                <map:when test="GET">
>                    <map:call function="functionName">
>                        <map:parameter name="searchType"
>                            value="{0}" />
>                    </map:call>
>                </map:when>
>                <map:when test="POST">
>                    <map:call
>                        continuation="{request-param:continuation-id}"
>                    />
>                </map:when>
>            </map:select>
> 
> Regards,
> Eric

Re: Proposed fix: crazy infinite loop w/

Posted by Ben Pope <be...@gmail.com>.
Hi,

I saw that approach in the samples, but thought it would be nice to be 
able to navigate the site passing parameters with POST sometimes, so 
instead I came up with this:

<map match ...>
    <map:select type="request-parameter">
       <map:parameter name="parameter-name" value="continuation-id"/>
          <map:when test="{request-param:continuation-id}">
             <map:call continuation="{request-param:continuation-id}"/>
          </map:when>
          <map:otherwise>
             <map:call function...


If the continuation parameter is not present, the test returns null, and 
goes to otherwise, which calls the form handler.  If the parameter 
exists (regardless of whether GET/POST) it calls the continuation.

Just a thought...

Ben Pope.


Eric E. Meyer wrote:
> This is how I work with hidden continuation ids since my form posts.
> 
> You can select based upon the request:method - continuing only on POST:
> 
>            <map:select type="oacl-simple">
>                <map:parameter name="value" value="{request:method}"/>
>                <map:when test="GET">
>                    <map:call function="functionName">
>                        <map:parameter name="searchType"
>                            value="{0}" />
>                    </map:call>
>                </map:when>
>                <map:when test="POST">
>                    <map:call
>                        continuation="{request-param:continuation-id}"
>                    />
>                </map:when>
>            </map:select>
> 
> Regards,
> Eric

Re: Proposed fix: crazy infinite loop w/

Posted by "Eric E. Meyer" <er...@quoininc.com>.
This is how I work with hidden continuation ids since my form posts.

You can select based upon the request:method - continuing only on POST:

            <map:select type="oacl-simple">
                <map:parameter name="value" value="{request:method}"/>
                <map:when test="GET">
                    <map:call function="functionName">
                        <map:parameter name="searchType"
                            value="{0}" />
                    </map:call>
                </map:when>
                <map:when test="POST">
                    <map:call
                        continuation="{request-param:continuation-id}"
                    />
                </map:when>
            </map:select>

Regards,
Eric

Vadim Gritsenko wrote:

> Mark Lundquist wrote:
>
>>
>> I tried passing the continuation ID as a hidden form parameter, using 
>> <ft:continuation-id>. Until now, I've always put continuation IDs in 
>> the URI, but in this application I really need to do it this way (and 
>> if I can get this to work right, I'm going to do it this way again in 
>> the future!).
>>
>> The problem is that after the continuation is resumed, the response 
>> to that request is generated by an internal redirect (via sendPage()) 
>> of the request, which is of course matched against the sitemap, but 
>> since the request still contains the "continuation-id" parameter, it 
>> hits on that matcher and re-invokes the continuation, and it's "/déjà 
>> vu/ all over again", forever and ever, amen.
>>
>> So, (you smugly say :-), I should just move my continuation-id 
>> matcher to the end of the pipeline, so that it only matches if 
>> nothing else did.
>>
>> Well, the way I have designed the page flow in this app — which I 
>> think is the Right Way, BTW — absolutely depends on always matching 
>> on a continuation-id parameter in the "flow-originating request", and 
>> having this in turn work in a "sane" manner. And there is absolutely 
>> no use whatsoever for the continuation-id parameter, once the 
>> continuation has been resumed. I don't think I should have to 
>> redesign my page flow to something "worse", just to work around an 
>> onerous constraint on matching the continuation-id! :-)
>>
>> See below for an example scenario, representative of the sort of page 
>> flow I'm implementing.
>> But first, the proposed fix:
>>
>> I've added this method to the oac.environment.Request interface:
>>
>> public void killContinuation();
>>
>> In an HttpRequest, this sets a flag which causes 
>> getParameter("continuation-id") to return null. This is a workaround 
>> for the lack of a removeParameter() method in HttpServletRequest (the 
>> delegate).
>>
>> Now, over to the flowscript side of CForms... in 
>> Form.prototype.showForm(), after the call to sendPageAndWait(), I 
>> call cocoon.request.killContinuation().
>>
>> Works like a charm. No more infinitous loopage!
>>
>> So, my questions:
>>
>> • Is this a good fix? There isn't some better one that I am missing, 
>> is there?
>
>
> No, it's horrible.
>
>
>> • My fix might seem like hacky special-case-ism. But it also seems 
>> like adding a general removeParameter() method to these wrappers, 
>> while certainly possible (it'd just be a HashSet), would be overkill 
>> to solve a general problem that doesn't really exist (after all there 
>> is a reason that HttpServletRequest has gotten along nicely all this 
>> time without such a method). But maybe I'm wrong about that, and 
>> someone will tell my why I really should add this as a 
>> removeParameter() method... WDYT?
>>
>> I've prototyped this in the v2 version of Form, but I'll add it to 
>> "v1" and v3 before submitting a patch.
>>
>> -=-=-=--=
>>
>> A simple example of why I need this: Suppose we are doing 
>> "lightweight authentication" — i.e., using pure flowscript, w/o the 
>> AuthFW. This is super-simple, but to break it way down in 
>> excruciating detail:
>>
>> 1) The user requests a protected resource "/foo".
>> 2) The sitemap dispatches to a flowscript controller function that 
>> handles this request
>> 3) Since this is a protected resource, the controller calls a 
>> flowscript authorize() function
>> 3) The user isn't logged in, so authorize() invokes a login form (a 
>> CForms form). (*N.B.:* the browser is /not/ redirected to "the login 
>> page". The address bar shows "/foo").
>> 3) The users fills out and submits the form, which is a <form 
>> action=""> so it requests "/foo" again, this time with some 
>> (additional?) POST parameters: userName, password, and continuation-id.
>> 4) The continuation matcher in the sitemap hits, and the continuation 
>> is resumed.
>> 5) The login was successful, so the authorize() function returns 
>> (otherwise, it just loops and reissues the login form, i.e. return == 
>> success).
>
>
> Note: flow function can not return. It can do sendPage,redirect - but 
> it can't simply return.
>
>
>> 6) The caller of of authorize() proceeds and generates the reply to 
>> the request for "/foo".
>> 7) The browser receives the reply and renders the page. The address 
>> bar shows "/foo", the resource that was originally requested.
>
>
> I see several possible ways of solving your issue without crazy hacks.
>
>  * Move view rendering into the internal-only pipeline. That's how it
>    (usually) should be with the flow.
>
>  * If you don't like above, issue a redirect in the step (5) - you'll
>    loose continuation ID parameter in the process.
>
>  * Third option. The best approach to implement your login scenario
>    is to write custom auth action. If user is authenticated, you
>    return a map, sitemap shows the view (nested in the map:act).
>    If he is not, you return null and sitemap shows the login form
>    (you can use flow and forms there as it is now):
>
>    <map:act type="myauth">
>      <map:match pattern="*">
>        <!-- generate page here -->
>      </map:match>
>    </map:act>
>    <map:call function="login"
>              continuation-id="{request-param:continuation}"/>
>
>
> Vadim



Re: Proposed fix: crazy infinite loop w/

Posted by Vadim Gritsenko <va...@reverycodes.com>.
Mark Lundquist wrote:
> 
> I tried passing the continuation ID as a hidden form parameter, using 
> <ft:continuation-id>. Until now, I've always put continuation IDs in the 
> URI, but in this application I really need to do it this way (and if I 
> can get this to work right, I'm going to do it this way again in the 
> future!).
> 
> The problem is that after the continuation is resumed, the response to 
> that request is generated by an internal redirect (via sendPage()) of 
> the request, which is of course matched against the sitemap, but since 
> the request still contains the "continuation-id" parameter, it hits on 
> that matcher and re-invokes the continuation, and it's "/déjà vu/ all 
> over again", forever and ever, amen.
> 
> So, (you smugly say :-), I should just move my continuation-id matcher 
> to the end of the pipeline, so that it only matches if nothing else did.
> 
> Well, the way I have designed the page flow in this app — which I think 
> is the Right Way, BTW — absolutely depends on always matching on a 
> continuation-id parameter in the "flow-originating request", and having 
> this in turn work in a "sane" manner. And there is absolutely no use 
> whatsoever for the continuation-id parameter, once the continuation has 
> been resumed. I don't think I should have to redesign my page flow to 
> something "worse", just to work around an onerous constraint on matching 
> the continuation-id! :-)
> 
> See below for an example scenario, representative of the sort of page 
> flow I'm implementing.
> But first, the proposed fix:
> 
> I've added this method to the oac.environment.Request interface:
> 
> public void killContinuation();
> 
> In an HttpRequest, this sets a flag which causes 
> getParameter("continuation-id") to return null. This is a workaround for 
> the lack of a removeParameter() method in HttpServletRequest (the 
> delegate).
> 
> Now, over to the flowscript side of CForms... in 
> Form.prototype.showForm(), after the call to sendPageAndWait(), I call 
> cocoon.request.killContinuation().
> 
> Works like a charm. No more infinitous loopage!
> 
> So, my questions:
> 
> • Is this a good fix? There isn't some better one that I am missing, is 
> there?

No, it's horrible.


> • My fix might seem like hacky special-case-ism. But it also seems like 
> adding a general removeParameter() method to these wrappers, while 
> certainly possible (it'd just be a HashSet), would be overkill to solve 
> a general problem that doesn't really exist (after all there is a reason 
> that HttpServletRequest has gotten along nicely all this time without 
> such a method). But maybe I'm wrong about that, and someone will tell my 
> why I really should add this as a removeParameter() method... WDYT?
> 
> I've prototyped this in the v2 version of Form, but I'll add it to "v1" 
> and v3 before submitting a patch.
> 
> -=-=-=--=
> 
> A simple example of why I need this: Suppose we are doing "lightweight 
> authentication" — i.e., using pure flowscript, w/o the AuthFW. This is 
> super-simple, but to break it way down in excruciating detail:
> 
> 1) The user requests a protected resource "/foo".
> 2) The sitemap dispatches to a flowscript controller function that 
> handles this request
> 3) Since this is a protected resource, the controller calls a flowscript 
> authorize() function
> 3) The user isn't logged in, so authorize() invokes a login form (a 
> CForms form). (*N.B.:* the browser is /not/ redirected to "the login 
> page". The address bar shows "/foo").
> 3) The users fills out and submits the form, which is a <form action=""> 
> so it requests "/foo" again, this time with some (additional?) POST 
> parameters: userName, password, and continuation-id.
> 4) The continuation matcher in the sitemap hits, and the continuation is 
> resumed.
> 5) The login was successful, so the authorize() function returns 
> (otherwise, it just loops and reissues the login form, i.e. return == 
> success).

Note: flow function can not return. It can do sendPage,redirect - but it can't 
simply return.


> 6) The caller of of authorize() proceeds and generates the reply to the 
> request for "/foo".
> 7) The browser receives the reply and renders the page. The address bar 
> shows "/foo", the resource that was originally requested.

I see several possible ways of solving your issue without crazy hacks.

  * Move view rendering into the internal-only pipeline. That's how it
    (usually) should be with the flow.

  * If you don't like above, issue a redirect in the step (5) - you'll
    loose continuation ID parameter in the process.

  * Third option. The best approach to implement your login scenario
    is to write custom auth action. If user is authenticated, you
    return a map, sitemap shows the view (nested in the map:act).
    If he is not, you return null and sitemap shows the login form
    (you can use flow and forms there as it is now):

    <map:act type="myauth">
      <map:match pattern="*">
        <!-- generate page here -->
      </map:match>
    </map:act>
    <map:call function="login"
              continuation-id="{request-param:continuation}"/>


Vadim