See_other http_status_reply failing unexpectedly

#1

On 8.1.0

I am using a redirect to set a persistent identity cookie to implement
‘remember me’ functionality. Chased bug down to http_status_reply.
Am I misunderstanding what it’s expecting?

Expected behavior of this - start server with go/0. browse http://localhost:9000/ and be redirected to http://localhost:9000/foo

Observed behavior - throws Illegal HTTP parameter: HTTP/1.1 303 See Other

:- module(seeotherdemo, [go/0]).

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/html_write)).
:- use_module(library(http/http_session)).
:- use_module(library(http/http_header)).

go :-
    current_prolog_flag(version, X),
    X >= 80100,
    http_set_session_options(
        [ create(noauto),
          timeout(1800)  % half hour sessions
        ]),
    http_server(http_dispatch, [port(9000)]).
go :-
    writeln('Need to be on SWI-Prolog 8.1.0 or better, you are on'),
    version.


:- http_handler(root(.), root_handler, [id(home)]).
:- http_handler(root(foo), foo_handler, [id(foo)]).

root_handler(_Request) :-
      http_status_reply(
          see_other('http://localhost:9000/foo'),
                        current_output,
                 [], %       ['Set-Cookie'(Contents)],   DEBUGGING
                        _).

foo_handler(_Request) :-
       reply_html_page(
           title(foo),
           h1('the foo page')).
#2

http_status_reply is not for the user. Use http_redirect/3. Note that there is no reason to redirect to set a cookie. Only if you want to know the user really accepted the cookie you may want to use a redirect. And, of course you can use a relative URL rather than an absolute one. The latter is strongly advised as it is generally hard to figure out the real public address of a server.

1 Like
#3

I do need to set a cookie, and then redirect. Otherwise things get messy. This is something that should just work. I can imagine many
reasons one might do this.

Suppose where.com geolocates users for it’s B2B customers. BigCo uses where.com. When they want an API to be annotated, they point the browser/mobile app at where.com and add a ‘redirect_to’ field.
where.com figures out the geo location by it’s proprietary method, and
adds a custom header. Then they redirect.

I thought you added http_status_reply because I was struggling with some other similar issue. It’s public, has a pldoc comment, and
appears to be a reasonable API.
I know you have a model of how this should work, but not every use case is SWISH. I don’t want to rebuild my program - I want to be able to do something that would be trivial in PHP. Redirects are inherently oddball. Sending me to http_redirect/3 is not a solution.

I’m logging the user in persistently. This code is the do_login handler
that is the action for the login form. If the user checked ‘remember me’, we need to make a session cookie, and we need a second cookie that is persistent. When the user revisits later, the user gets logged in transparently.

I am using a relative URL in the actual code. The absolute URL in
the demo program is to ensure that it was resolving correctly.
Using a relative URL produces same results.

If this is just a bug, say so and I’ll try to fix it. But I take it from your reply that I’m abusing http_status_reply and this isn’t a bug?

#4

The module docs say “Its functionality is normally hidden by the other parts of the HTTP server and client libraries”. I can agree that is not very explicit.

http_redirect/3 is for redirection and the question is thus what is wrong with it. I think you suggest you cannot write a cookie header and then redirect, no? That is probably true and should be fixed. A redirect is an explicit action and thus may use the already written header (unlike errors).

A work around is to simply write the full header (set cookie, location and status) and close the document.

#5

OK - yes, there should be some way to add extra headers to a redirect.

I’m not sure how I’ll manage to send a 302 status.

Here, for example, (in fact taken from the old web tutorial I made), is typical manual writing of headers:

:- http_handler(/, say_hi, []).

say_hi(_Request) :-
        format('Content-type: text/plain~n~n'),
        format('Hello World!~n').

This is fine, but how am I going to make it be non 200 status?

This has always been my frustration with how non 200 statuses work.

#6

this doesn’t work - it’s baffled by the status line

roof_handler(_Request) :-
        format('HTTP/1.1 303 See Other~n'),
        format('Content-type: text/plain~n'),
        format('Location: http://localhost:9000/foo~n~n'),
        format('Hello World!~n'),
        close(current_output).
#7

You need Status: 303 See Other. Actually only the code matters; the rest is done by the handler. You should also not close current output. The base line is that the handler simply writes a document according to the CGI standard and returns.

#8

AAAaaaaah! The dim light bulb of Annie’s little brain goes on!
:bulb::bulb::bulb::bulb::bulb::bulb::bulb::bulb::bulb::bulb:

I’ve been seeing the document as a raw HTTP document, not a CGI document. Yikes! OK, major confusion. My lack of familiarity with the CGI spec (I was making operating systems at the time of it’s rise and fall) a contributing factor.

~&8cO <-- Annie, with her little brain (smoking)

PS - looks like the web tutorial gets revved today.