Hi,
I am working on a simple external to do HTTP requests from PD. The idea is to implement a simple way to send and receive simple data to a web server. This is intended to work together with a set of classes that will interpret the GET variables as key and values pairs where the values can be similar to FUDI protocol messages. The web page would then answer with a FUDI message or so. I don't use Python because I want it to be very fast, totally portable and easy to install. (I also agree with Hans regarding small tools to make a constellation of low level objects)
Here are my bugs : perhaps I am not very good yet with strings in C, but I have several errors when I try to execute this code from within PD as an external. Well, when I get malloc errors, it must be only about memory size arithmetics, but when it gets into segmentation faults, I was just wondering if it could be because cURL starts a new thread. I think that thread are not much appropriate in PD's context. Do you think this is the problem, or I am just still a bit bad with memory maths ?
Also, if you have ideas about this external, please don't hesitate. I think I will rename it to http, as it is not intended in downloading whole files, but just for simple HTTP requests.
See below for the current code, the makefile and one of the errors it creates.
Alexandre Quessy http://alexandre.quessy.net
============================= error when using it ====================
pd wget-help.pd STACK_END old=0xbf9e5ffc; new=0xbf9efffc PDP: pure data packet PDP: version 0.12.4 * About to connect() to alexandre.quessy.net port 80 * Trying 64.15.134.165... * connected * Connected to alexandre.quessy.net (64.15.134.165) port 80
GET /pd/curlme.php?asd=qwe+123 HTTP/1.1
User-Agent: Pure Data Host: alexandre.quessy.net Accept: */*
< HTTP/1.1 200 OK < Date: Mon, 19 Jun 2006 16:03:44 GMT < Server: Apache < X-Powered-By: PHP/4.3.10-16 < Connection: close < Transfer-Encoding: chunked < Content-Type: text/html * Closing connection #0 socket receive error: Connection reset by peer (104) Erreur de segmentation
================== makefile ====================================
all: wget.pd_linux @echo "making wget"
wget.pd_linux: wget.c gcc -DPD -O2 -funroll-loops -fomit-frame-pointer \ -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch -o wget.o -c wget.c ld -export_dynamic -shared -o wget.pd_linux wget.o -lcurl -lc -lm strip --strip-unneeded wget.pd_linux rm wget.o
================== code : wget.c ================================
/** * [wget] is a Pure Data object for simple HTTP requests. * * @url http://alexandre.quessy.net/ * @author Alexandre Quessy alexandre@quessy.net * @license GNU General Public License 2006 */ #define PDWGET_VERSION "0.1.0"
/* PD includes */ #include "m_pd.h"
/* cURL and standard C libraries */ #include <curl/curl.h> #include <string.h> #include <stdlib.h>
/* Download callback function prototype */ size_t wget_callBack(void *ptr, size_t size, size_t nmemb, void *x);
/** * variables of the pd object */ typedef struct wget { t_object x_ob; /* The instance. Contains inlets and outlets */ t_outlet *x_outlet0; /* result string */ t_outlet *x_outlet1; /* HTTP error codes */ } t_wget;
/** * HTTP request. */ void wget_request(t_wget *x, t_symbol *s, int argc, t_atom *argv) { post("wget : called request."); //curl_global_init(CURL_GLOBAL_NOTHING);//CURL_GLOBAL_ALL
//t_symbol *sym_tmp = atom_getsymbol(&argv[0]); if (argc < 1) { post("wget : Error : needs arguments."); } else { t_symbol *sym_tmp = atom_getsymbolarg(0, argc, argv); char *url = sym_tmp->s_name;
post("wget : requesting : %s", url);
/* cURL context */ CURL *pContext = curl_easy_init(); if (pContext == NULL) { post("wget : Error : Null cURL handle."); } else { /* cURL options */ CURLcode pCode = curl_easy_setopt(pContext, CURLOPT_URL, url); curl_easy_setopt(pContext, CURLOPT_WRITEFUNCTION, wget_callBack, (void *) x); curl_easy_setopt(pContext, CURLOPT_VERBOSE, 1); curl_easy_setopt(pContext, CURLOPT_HEADER, 0); curl_easy_setopt(pContext, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(pContext, CURLOPT_USERAGENT, "Pure Data"); curl_easy_setopt(pContext, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(pContext, CURLOPT_TIMEOUT, (long) 5);
/* cURL performing */ post("wget : Performing the request."); const CURLcode result = curl_easy_perform(pContext); if(CURLE_OK != result) { post("wget : Error from cURL: %s", curl_easy_strerror(result)); } else {
/* HTTP code */ long lHttpCode; curl_easy_getinfo(pContext, CURLINFO_RESPONSE_CODE, &lHttpCode); post("wget : HTTP response code: %ld ", lHttpCode); //outlet_float(x->x_outlet1, (float) lHttpCode); } }
post("[wget] cleaning up..."); /* cURL cleanup */ curl_easy_cleanup(pContext); post("[wget] Local cleaned up."); //curl_global_cleanup(); post("[wget] Global cleaned up."); //outlet_list(x->x_outlet0, gensym("A_FLOAT"), numCols+1, &atoms); // &atoms ?? } // else }
/** * The representation of the class in PD. */ t_class *wget_class;
/** * constructor */ void *wget_new(t_symbol *selector, int argc, t_atom *argv) { post("wget : creating a new object."); t_wget *x = (t_wget *) pd_new(wget_class); x->x_outlet0 = outlet_new(&x->x_ob, gensym("list")); x->x_outlet1 = outlet_new(&x->x_ob, gensym("float")); return (void *)x; }
/** * setup */ void wget_setup(void) { curl_global_init(CURL_GLOBAL_NOTHING); wget_class = class_new(gensym("wget"), (t_newmethod) wget_new, 0, sizeof(t_wget), 0, A_GIMME, 0); class_addmethod(wget_class, (t_method)wget_request, gensym("request"), A_GIMME, 0);
post("----------------------------------------------------"); post(" [wget] %s (c) GPL Copyleft 2006 Alexandre Quessy", PDWGET_VERSION); post(" Simple HTTP requests for Pure Data."); post("----------------------------------------------------"); }
/** * Callback implementation */ size_t wget_callBack(void *ptr, size_t size, size_t nmemb, void *x) { size_t totalSize = nmemb * size; char *dest = malloc(totalSize + 1); //(sizeof(char)) strcpy(dest, (char *) ptr); post("[wget] : Getting data."); dest[totalSize] = '\0'; post("result :"); post("==============================="); post("%s", dest); post("===============================");
//outlet_symbol(((t_wget *)x)->x_outlet0, gensym(dest)); //free(dest); return(totalSize); }
Sounds like a nifty object, but why not aim on making it as flexible as possible? It shouldn't be too hard to make it do everything that libcurl lets you do. One simple thing, for example, is making it possible to set the CURLOPTS_* via Pd messages so people can make their own HTTP headers.
As for crashes, since libcurl wants to use a call back function, you will need have a separate thread, AFAIK. That separate thread could just wait for libcurl to call the callback, then write data to a data structure. The Pd object would then just check the data structure for the status of things. externals/olafmatt/hidin is a example of how to do the threading, even tho its Windows only object, the threading method will be the same.
.hc
On Jun 19, 2006, at 9:18 AM, Alexandre Quessy wrote:
Hi,
I am working on a simple external to do HTTP requests from PD. The idea is to implement a simple way to send and receive simple data to a web server. This is intended to work together with a set of classes that will interpret the GET variables as key and values pairs where the values can be similar to FUDI protocol messages. The web page would then answer with a FUDI message or so. I don't use Python because I want it to be very fast, totally portable and easy to install. (I also agree with Hans regarding small tools to make a constellation of low level objects)
Here are my bugs : perhaps I am not very good yet with strings in C, but I have several errors when I try to execute this code from within PD as an external. Well, when I get malloc errors, it must be only about memory size arithmetics, but when it gets into segmentation faults, I was just wondering if it could be because cURL starts a new thread. I think that thread are not much appropriate in PD's context. Do you think this is the problem, or I am just still a bit bad with memory maths ?
Also, if you have ideas about this external, please don't hesitate. I think I will rename it to http, as it is not intended in downloading whole files, but just for simple HTTP requests.
See below for the current code, the makefile and one of the errors it creates.
Alexandre Quessy http://alexandre.quessy.net
============================= error when using it ====================
pd wget-help.pd STACK_END old=0xbf9e5ffc; new=0xbf9efffc PDP: pure data packet PDP: version 0.12.4
- About to connect() to alexandre.quessy.net port 80
- Trying 64.15.134.165... * connected
- Connected to alexandre.quessy.net (64.15.134.165) port 80
GET /pd/curlme.php?asd=qwe+123 HTTP/1.1
User-Agent: Pure Data Host: alexandre.quessy.net Accept: */*
< HTTP/1.1 200 OK < Date: Mon, 19 Jun 2006 16:03:44 GMT < Server: Apache < X-Powered-By: PHP/4.3.10-16 < Connection: close < Transfer-Encoding: chunked < Content-Type: text/html
- Closing connection #0
socket receive error: Connection reset by peer (104) Erreur de segmentation
================== makefile ====================================
all: wget.pd_linux @echo "making wget"
wget.pd_linux: wget.c gcc -DPD -O2 -funroll-loops -fomit-frame-pointer \ -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch -o wget.o -c wget.c ld -export_dynamic -shared -o wget.pd_linux wget.o -lcurl -lc -lm strip --strip-unneeded wget.pd_linux rm wget.o
================== code : wget.c ================================
/**
- [wget] is a Pure Data object for simple HTTP requests.
- @url http://alexandre.quessy.net/
- @author Alexandre Quessy alexandre@quessy.net
- @license GNU General Public License 2006
*/ #define PDWGET_VERSION "0.1.0"
/* PD includes */ #include "m_pd.h"
/* cURL and standard C libraries */ #include <curl/curl.h> #include <string.h> #include <stdlib.h>
/* Download callback function prototype */ size_t wget_callBack(void *ptr, size_t size, size_t nmemb, void *x);
/**
- variables of the pd object
*/ typedef struct wget { t_object x_ob; /* The instance. Contains inlets and outlets */ t_outlet *x_outlet0; /* result string */ t_outlet *x_outlet1; /* HTTP error codes */ } t_wget;
/**
- HTTP request.
*/ void wget_request(t_wget *x, t_symbol *s, int argc, t_atom *argv) { post("wget : called request."); //curl_global_init(CURL_GLOBAL_NOTHING);//CURL_GLOBAL_ALL
//t_symbol *sym_tmp = atom_getsymbol(&argv[0]); if (argc < 1) { post("wget : Error : needs arguments."); } else { t_symbol *sym_tmp = atom_getsymbolarg(0, argc, argv); char *url = sym_tmp->s_name; post("wget : requesting : %s", url); /* cURL context */ CURL *pContext = curl_easy_init(); if (pContext == NULL) { post("wget : Error : Null cURL handle."); } else { /* cURL options */ CURLcode pCode = curl_easy_setopt(pContext, CURLOPT_URL, url); curl_easy_setopt(pContext, CURLOPT_WRITEFUNCTION,
wget_callBack, (void *) x); curl_easy_setopt(pContext, CURLOPT_VERBOSE, 1); curl_easy_setopt(pContext, CURLOPT_HEADER, 0); curl_easy_setopt(pContext, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(pContext, CURLOPT_USERAGENT, "Pure Data"); curl_easy_setopt(pContext, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(pContext, CURLOPT_TIMEOUT, (long) 5);
/* cURL performing */ post("wget : Performing the request."); const CURLcode result = curl_easy_perform(pContext); if(CURLE_OK != result) { post("wget : Error from cURL: %s", curl_easy_strerror
(result)); } else {
/* HTTP code */ long lHttpCode; curl_easy_getinfo(pContext, CURLINFO_RESPONSE_CODE,
&lHttpCode); post("wget : HTTP response code: %ld ", lHttpCode); //outlet_float(x->x_outlet1, (float) lHttpCode); } }
post("[wget] cleaning up..."); /* cURL cleanup */ curl_easy_cleanup(pContext); post("[wget] Local cleaned up."); //curl_global_cleanup(); post("[wget] Global cleaned up."); //outlet_list(x->x_outlet0, gensym("A_FLOAT"), numCols+1,
&atoms); // &atoms ?? } // else }
/**
- The representation of the class in PD.
*/ t_class *wget_class;
/**
- constructor
*/ void *wget_new(t_symbol *selector, int argc, t_atom *argv) { post("wget : creating a new object."); t_wget *x = (t_wget *) pd_new(wget_class); x->x_outlet0 = outlet_new(&x->x_ob, gensym("list")); x->x_outlet1 = outlet_new(&x->x_ob, gensym("float")); return (void *)x; }
/**
- setup
*/ void wget_setup(void) { curl_global_init(CURL_GLOBAL_NOTHING); wget_class = class_new(gensym("wget"), (t_newmethod) wget_new, 0, sizeof(t_wget), 0, A_GIMME, 0); class_addmethod(wget_class, (t_method)wget_request, gensym ("request"), A_GIMME, 0);
post("----------------------------------------------------"); post(" [wget] %s (c) GPL Copyleft 2006 Alexandre Quessy",
PDWGET_VERSION); post(" Simple HTTP requests for Pure Data."); post("----------------------------------------------------"); }
/**
- Callback implementation
*/ size_t wget_callBack(void *ptr, size_t size, size_t nmemb, void *x) { size_t totalSize = nmemb * size; char *dest = malloc(totalSize + 1); //(sizeof(char)) strcpy(dest, (char *) ptr); post("[wget] : Getting data."); dest[totalSize] = '\0'; post("result :"); post("==============================="); post("%s", dest); post("===============================");
//outlet_symbol(((t_wget *)x)->x_outlet0, gensym(dest)); //free(dest); return(totalSize);
}
PD-dev mailing list PD-dev@iem.at http://lists.puredata.info/listinfo/pd-dev
------------------------------------------------------------------------
News is what people want to keep hidden and everything else is publicity. - Bill Moyers
On 6/22/06, Hans-Christoph Steiner hans@eds.org wrote:
As for crashes, since libcurl wants to use a call back function, you will need have a separate thread, AFAIK. That separate thread could just wait for libcurl to call the callback, then write data to a data structure. The Pd object would then just check the data structure for the status of things. externals/olafmatt/hidin is a example of how to do the threading, even tho its Windows only object, the threading method will be the same.
Interesting ! Would that shared data structure be a global variable ? If so, could I make it unique so that we can create multiple instances of that pd object ?
I will look at that object's code. Thanks !
Sorry for replying late... :)