See_other http_status_reply failing unexpectedly


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,
        [ 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'),

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

root_handler(_Request) :-
                 [], %       ['Set-Cookie'(Contents)],   DEBUGGING

foo_handler(_Request) :-
           h1('the foo page')).

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

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 geolocates users for it’s B2B customers. BigCo uses When they want an API to be annotated, they point the browser/mobile app at and add a ‘redirect_to’ field. 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?


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.


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.


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'),

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.


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

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.