Curl support RFC

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sun Mar 13 15:44:13 PDT 2011


On 3/11/11 9:20 AM, Jonas Drewsen wrote:
> Hi,
>
> So I've spent some time trying to wrap libcurl for D. There is a lot of
> things that you can do with libcurl which I did not know so I'm starting
> out small.
>
> For now I've created all the declarations for the latest public curl C
> api. I have put that in the etc.c.curl module.

Great! Could you please create a pull request for that?

> On top of that I've created a more D like api as seen below. This is
> located in the 'etc.curl' module. What you can see below currently works
> but before proceeding further down this road I would like to get your
> comments on it.
>
> //
> // Simple HTTP GET with sane defaults
> // provides the .content, .headers and .status
> //
> writeln( Http.get("http://www.google.com").content );

Sweet. As has been discussed, often the content is not text so you may 
want to have content return ubyte[] and add a new property such as 
"textContent" or "text".

> //
> // GET with custom data receiver delegates
> //
> Http http = new Http("http://www.google.dk");

You'll probably need to justify the existence of a class hierarchy and 
what overridable methods there are. In particular, since you seem to 
offer hooks via delegates, probably classes wouldn't be needed at all. 
(FWIW I would've done the same; I wouldn't want to inherit just to 
intercept the headers etc.)

> http.setReceiveHeaderCallback( (string key, string value) {
> writeln(key ~ ":" ~ value);
> } );
> http.setReceiveCallback( (string data) { /* drop */ } );
> http.perform;

As discussed, properties may be better here than setXxx and getXxx. The 
setReceiveCallback hook should take a ubyte[]. The 
setReceiveHeaderCallback should take a const(char)[]. That way you won't 
need to copy all headers, leaving safely that option to the client.

> //
> // POST with some timouts
> //
> http.setUrl("http://www.testing.com/test.cgi");
> http.setReceiveCallback( (string data) { writeln(data); } );
> http.setConnectTimeout(1000);
> http.setDataTimeout(1000);
> http.setDnsTimeout(1000);
> http.setPostData("The quick....");
> http.perform;

setPostData -> setTextPostData, and then changing everything to 
properties would make it something like textPostData. Or wait, there 
could be some overloading going on... Anyway, the basic idea is that 
generally get and post data could be raw bytes, and the user could elect 
to transfer strings instead.

> //
> // PUT with data sender delegate
> //
> string msg = "Hello world";
> size_t len = msg.length; /* using chuncked transfer if omitted */
>
> http.setSendCallback( delegate size_t(char[] data) {
> if (msg.empty) return 0;
> auto l = msg.length;
> data[0..l] = msg[0..$];
> msg.length = 0;
> return l;
> },
> HttpMethod.put, len );
> http.perform;

The callback would take ubyte[].

> //
> // HTTPS
> //
> writeln(Http.get("https://mail.google.com").content);
>
> //
> // FTP
> //
> writeln(Ftp.get("ftp://ftp.digitalmars.com/sieve.ds",
> "./downloaded-file"));
>
>
> // ... authenication, cookies, interface select, progress callback
> // etc. is also implemented this way.
>
>
> /Jonas

This is all very encouraging. I think this API covers nicely a variety 
of needs. We need to make sure everything interacts well with threads, 
in particular that one can shut down a transfer (or the entire library) 
from a thread or callback and have the existing transfer(s) throw an 
exception immediately.

Regarding a range interface, it would be great if you allowed e.g.

foreach (line; Http.get("https://mail.google.com").byLine()) {
    ...
}

The data transfer should happen concurrently with the foreach code. The 
type of line is char[] or const(char)[]. Similarly, there would be a 
byChunk interface that transfers in ubyte[] chunks.

Also we need a head() method for the corresponding command.


Andrei


More information about the Digitalmars-d mailing list