Can't get http_parameters to work

I cannot seem to get http_parameters to work and I’m not sure why (swipl 8.0.3 64bit). I’ve created a tiny server example below, which I test from the linux commandline with httpie (like curl, but nicer interface). When I debug the Request data, it looks like the JSON data has disappeared, but I’m not how that happens.

Here is the code for the tiny server (and the interchange with the server below that):

% http libraries
:- use_module(library(settings)).
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_path)).
:- use_module(library(http/http_parameters)).
:- use_module(library(http/json)).
:- use_module(library(http/json_convert)).
:- use_module(library(http/http_json)).
:- use_module(library(debug)).

% handler declarations
:- http_handler(root(.), frontpage,[prefix]).

% server
server:-
    open('log.txt',append, ErrStream,[]),
    debug(server>ErrStream),
    http_server(http_dispatch,[port(8080)]).

stopserver:- http_stop_server(8080,[]).

frontpage(Request):-
    http_parameters(Request,[
        email(Email,   [optional(true)]),
        password(_Passwd, [optional(true)])
    ]),
    debug(server,'~w',[Request]),
    (var(Email)->Email='empty email field';true),
    reply_json( json([user=Email]) ),!.

With httpie, on the commandline, I fire a POST request (also tried GET and PUT) as follows:

http -v POST :8080 email=john@mail.com password=fubar

This causes httpie to send the following to the server:

POST / HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 47
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/0.9.8

{
    "email": "john@mail.com",
    "password": "fubar"
}

And the server responds as follows:


HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 28
Content-Type: application/json; charset=UTF-8
Date: Fri, 20 Mar 2020 06:47:03 GMT

{
    "user": "empty email field"
}

When I inspect the log file, the contents of the Request are as follows:

% [Thread httpd@8080_13] [protocol(http),peer(ip(127,0,0,1)),pool(client(httpd@8080,user:http_dispatch,<stream>(0x7f07b0
023070),<stream>(0x7f07b0011af0))),input(<stream>(0x7f07b0023070)),method(post),request_uri(/),path(/),http_version(1-1)
,host(localhost),port(8080),user_agent(HTTPie/0.9.8),accept_encoding(gzip, deflate),accept([media(application/json,[],1.
0,[]),media(_2460/_2462,[],1.0,[])]),connection(keep-alive),content_type(application/json),content_length(47)]

Copied your code in a local folder, and started ?- server. .

Now I can access it from the browser at the url http://localhost:8080/?email=john@mail.com to get the JSON reply {"user":"john@mail.com"} .

So, GET parameters are working as expected. To show that POST parameters works as well, have run this PHP snippet:

<?php

// Your POST data
$data = http_build_query(array(
    'email' => 'john@mail.com',
    'password' => 'fubar'
));

// Create HTTP stream context
$context = stream_context_create(array(
    'http' => array(
        'method' => 'POST',
        'header' => 'Content-Type: application/x-www-form-urlencoded',
        'content' => $data
    )
));

// Make POST request
$response = file_get_contents('http://localhost:8080/', false, $context);
print_r($response);

and got the expected {"user":"john@mail.com"} .

Seems that httpie doesn’t encode the FORM data as expected…

The log.txt (but you forgot the enabling directive :- use_module(library(http/http_log)).) relevant entries entries are

% [Thread httpd@8080_5] [protocol(http),peer(ip(127,0,0,1)),pool(client(httpd@8080,user:http_dispatch,<stream>(0x7f77040108d0),<stream>(0x7f7704010a10))),input(<stream>(0x7f77040108d0)),method(post),request_uri(/?email=cc@cc.cc),path(/),search([email=cc@cc.cc]),http_version(1-0),host(localhost),port(8080),connection(close),content_length(36),content_type(application/x-www-form-urlencoded)]
% [Thread httpd@8080_3] [protocol(http),peer(ip(127,0,0,1)),pool(client(httpd@8080,user:http_dispatch,<stream>(0x7f770c0127c0),<stream>(0x7f770c0128d0))),input(<stream>(0x7f770c0127c0)),method(post),request_uri(/),path(/),http_version(1-0),host(localhost),port(8080),connection(close),content_length(36),content_type(application/x-www-form-urlencoded)]

Guess httpie should be instructed to pass content_type(application/x-www-form-urlencoded)

2 Likes

Yip. On the other hand it might get time to make http_parameters/2 also understand JSON POST content instead of only x-www-form-urlencoded. This is slightly different as both GET and worm-encoded POST provide the parameters as untyped text and this http_parameters/2,3 does the necessary translation to e.g. a number. Using JSON we already have dynamically typed numbers. Still, I guess it would fit nicely and may simplify simple JSON POST handlers.

2 Likes

Thanks CapelliC – changing the content_type solved the issue.

FYI, the way to get httpie to use the correct content_type is:

http --form POST :8080/login email='john@mail.com'

(btw, I used debug instead of http_log – that’s why you didn’t see me use the http_log library).

Thanks again!
PS. Jan, I agree with your suggestion that it would be nice to have http_parameters accept json data more directly.

You’re welcome!

Oops, I just noticed log.txt was empty after a couple requests, and missed the debug call(s)… guess the inclusion of http_log didn’t changed anything wrt logging, but probably I restarted the server and got log.txt flushed… not sure…