/* ********************************************************************* This source file is a part of the standard library of Scol For the latest info, see http://www.scolring.org Copyright (c) 2013 Stephane Bisaro aka Iri. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt ********************************************************************* */ /* * Standard functions for HTTP client request * See http://redmine.scolring.org/projects/tutorials/wiki/Scol_usage * for more informations */ /*! \file http.pkg * \author Scol team * \version 0.1 * \copyright GNU Lesser General Public License 2.0 or later * \brief Scol Standard Library - HTTP client request API * * \details This API provides some options to write HTTP client request routines for Scol * This is older than the Libcurl API ( lib/std/curl.pkg ). * \remark This API requires the list API ( lib/std/list.pkg ) and the string API * ( lib/std/string.pkg ) * * \remark This API uses the legacy INET Scol API. **/ /*! \struct STD_HTTP_CLIENT * * \brief Internal structure. You should not call it directly, use * API instead ! * **/ struct STD_HTTP_CLIENT = [ stdurl : [[S S S] r1], /*!< urls/headers/datas to send list to process */ stdinet : INET, /*!< current INET object */ stdinetverb : I, /*!< verb, such as "GET", "POST", ... */ stdcbinetok : fun [STD_HTTP_CLIENT S] I, /*!< success callback (fun [STD_HTTP_CLIENT S] I) */ stdcbinetdownloading : fun [STD_HTTP_CLIENT I] I,/*!< callback while a partial response is received (fun [STD_HTTP_CLIENT I] I) */ stdcbineterror : fun [STD_HTTP_CLIENT I] I, /*!< fail callback (fun [STD_HTTP_CLIENT I] I) */ stdinettmpdata : S, /*!< temporary downloading datas */ stdinetenabled : I, /*!< object is enabled (1, default) or disabled/cancelled (0) */ stdinetpersistent : I, /*!< the object must be kept at th eend of the request */ stdinetrunning : I, /*!< an operation is running */ stdintremoveinsuccess : I, /*!< the url is removed if the request finish successfully (1, default) or not (0) */ stdinetcontinue : I, /*!< all urls are processed, one by one (1, default) or not (0) */ stdinetislocal : I /*!< don't test the distant connection, local only : yes 1, no 0 (default) */ ] mkSTD_HTTP_CLIENT;; typeof StdHttpClientList = [STD_HTTP_CLIENT r1];; var StdHttpClientUrlTest = "http://www.google.com/";; //" var StdHttpSupportedVerb = "GET" :: "POST" :: "PUT" :: nil;; var STD_HTTPERRUNKNOWN = -1;; /*!< internal and undefined failure */ var STD_HTTPSUCCESS = 0;; /*!< success */ var STD_HTTPNOTCONNECTED = 1;; /*!< client not connected (or connction failure) */ var STD_HTTPDISABLED = 2;; /*!< object is disabled */ var STD_HTTPSTILLENBLED = 3;; /*!< a deleting is asked where as object is enabled yet */ var STD_HTTPCANCELLED = 4;; /*!< request cancelled */ var STD_HTTPNOTPERSISTENT = 5;; /*!< the current object is not persistent */ var STD_HTTPALREADYRUNNING = 6;; /*!< an operation is already running */ var STD_HTTPALLDONE = 98;; /*!< all urls has been processed */ var STD_HTTPNOTHING = 99;; /*!< request ended */ var STD_HTTPGET = 0;; var STD_HTTPPOST = 1;; var STD_HTTPPUT = 2;; var STD_HTTPVERB = -1;; proto std_httprun = fun [STD_HTTP_CLIENT] STD_HTTP_CLIENT;; fun std_httpnew (szUrl, inet, verb, cbOk, cbCur, cbError, enabled, persistent)= set StdHttpClientList = (mkSTD_HTTP_CLIENT [ if szUrl != nil then szUrl :: nil else nil inet verb cbOk cbCur cbError "" enabled persistent 0 1 1 1 ]) :: StdHttpClientList; hd StdHttpClientList;; fun std_httpobjcheck (o)= if o == nil then 0 else if !o.stdinetpersistent then 0 else 1;; fun std_httpconvverb (str)= if !strcmpi str "GET" then STD_HTTPGET else if !strcmpi str "POST" then STD_HTTPPOST else if !strcmpi str "PUT" then STD_HTTPPUT else STD_HTTPVERB;; fun std_httpconvverb2 (n)= if n == 0 then "GET" else if n then "POST" else if n == 2 then "PUT" else nil;; fun std_httpdelete (o, err)= if o == nil then ( exec o.stdcbineterror with [o STD_HTTPERRUNKNOWN]; STD_HTTPERRUNKNOWN ) else if o.stdinetpersistent then ( STD_HTTPNOTHING ) else if (o.stdinetenabled) && (err != STD_HTTPSUCCESS) then ( exec o.stdcbineterror with [o STD_HTTPSTILLENBLED]; STD_HTTPSTILLENBLED ) else ( if err != STD_HTTPSUCCESS then INETStopURL o.stdinet else nil; exec o.stdcbineterror with [o err]; set StdHttpClientList = std_lRemoveElt StdHttpClientList o; set o = nil; err );; fun std_httpcbinet (inet, o, data, code)= // if o.stdinet == nil then set o.stdinet = inet else nil; set o.stdinet = inet; /*if inet != o.stdinet then exec o.stdcbineterror with [o STD_HTTPERRUNKNOWN] else*/ if !o.stdinetenabled then ( exec o.stdcbineterror with [o STD_HTTPDISABLED]; std_httpdelete o STD_HTTPCANCELLED ) else if code == 0 then // downloading ( set o.stdinettmpdata = strcat o.stdinettmpdata data; exec o.stdcbinetdownloading with [o strlen data]; 0 ) else if code then // downloaded ( set o.stdinetrunning = 0; exec o.stdcbinetok with [o o.stdinettmpdata]; if o.stdintremoveinsuccess then set o.stdurl = tl o.stdurl else nil; if o.stdinetcontinue then // next url std_httprun o else ( std_httpdelete o STD_HTTPSUCCESS; nil ); STD_HTTPSUCCESS ) else // an error occurs ( exec o.stdcbineterror with [o 100+code]; std_httpdelete o 100+code );; fun std_httpcbisconnected (url, o, res)= if res then // ok, is connected let hd o.stdurl -> [szUrl szHeader szDatas] in if o.stdinetverb == STD_HTTPGET then ( set o.stdinetrunning = 1; set o.stdinettmpdata = ""; INETGetURL _channel szUrl 0 @std_httpcbinet o; STD_HTTPSUCCESS; ) else // if o.stdinetverb != STD_HTTPGET then ( set o.stdinetrunning = 1; set o.stdinettmpdata = ""; INETGetURLex2 _channel std_httpconvverb2 o.stdinetverb szUrl szHeader szDatas 0 @std_httpcbinet o; STD_HTTPSUCCESS; ) else // error, is not connected ( exec o.stdcbineterror with [o STD_HTTPNOTCONNECTED]; STD_HTTPNOTCONNECTED; );; fun std_httprun (o)= if (!o.stdinetislocal) && (o.stdurl != nil) then ( std_httpcbisconnected nil o 1; o ) else if o.stdurl != nil then ( _rflINETisConnected StdHttpClientUrlTest @std_httpcbisconnected o; o ) else ( exec o.stdcbineterror with [o STD_HTTPALLDONE]; o );; /*! \brief GET http asynchronous request * * \ingroup std_http * Prototype : fun [S fun [STD_HTTP_CLIENT S] I fun [STD_HTTP_CLIENT I] I fun [STD_HTTP_CLIENT I] I] STD_HTTP_CLIENT * * \param S : url to get * \param fun [STD_HTTP_CLIENT S] I : function to call when the response is whole. The string argument * is the datas received from the server. * \param fun [STD_HTTP_CLIENT I] I : function to call when a part of the response is received. The * integer argument is the size of this part. * \param fun [STD_HTTP_CLIENT I] I : function to call when an error occurs (and when the response * is complete). The integer argument is a following value. * - STD_HTTPSUCCESS : request finished with success * - STD_HTTPERRUNKNOWN : internal and undefined error, nothing to do ... * - STD_HTTPNOTCONNECTED : client is not connected or the connection fails. Check this ... * - STD_HTTPDISABLED : the request object has been disabled * - STD_HTTPSTILLENBLED : the request can not be stopped because it is still enabled * - STD_HTTPCANCELLED : the request has been cancelled * * \return STD_HTTP_CLIENT : the new request object **/ fun std_httpGetSimple (szUrl, cbOk, cbCur, cbError)= let std_httpnew [szUrl nil nil] nil std_httpconvverb "GET" cbOk cbCur cbError 1 0 -> o in ( std_httprun o; o );; /*! \brief Create a new persistent request object * * \details A such object can be prepared and reused. By default, it is enabled and persistent. * No callback are defined by default. * * Prototype : fun [S] STD_HTTP_CLIENT * * \ingroup std_http * \param S : an url (can be nil) * * \return STD_HTTP_CLIENT : this new object. * * \see std_httpDownload to perform a simple download * \see std_httpSetCallbacks to add callbacks * \see std_httpSetUrl to change the url * \see std_httpSetVerb to define the request type **/ fun std_httpNew (szUrl)= std_httpnew [szUrl nil nil] nil nil nil nil nil 1 1;; /*! \brief Set the urls / headers / datas to send * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT [S r1]] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param [[S S S] r1] : a list of urls/headers/datas to send, headers and datas * should be nil if the used verb is GET. * \remark The last added element will be the first processed element. * * \return STD_HTTP_CLIENT : the same object or nil if error (object is not valid) * * \remark this set doesn't change anything to the current operation, if any. **/ fun std_httpSetUrl (o, lUrl)= if !std_httpobjcheck o then nil else ( set o.stdurl = lUrl; o );; /*! \brief Add an url / header / data to send * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT [S S S]] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param [S S S] : an url, an header, any datas to send * * \return STD_HTTP_CLIENT : the same object or nil if error (object is not valid) * * \remark this set doesn't change anything to the current operation, if any. **/ fun std_httpAddUrl (o, p)= if !std_httpobjcheck o then nil else ( set o.stdurl = p :: o.stdurl; o );; /*! \brief Add an url only, without header nor data to send. These two last parameters * will be nil, thus, this conivience function should be use to a GET request only. * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT S] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param S : an url * * \return STD_HTTP_CLIENT : the same object or nil if error (object is not valid) * * \remark this set doesn't change anything to the current operation, if any. **/ fun std_httpAddUrlOnly (o, szUrl)= if !std_httpobjcheck o then nil else ( set o.stdurl = [szUrl nil nil] :: o.stdurl; o );; /*! \brief Return the current url, defined in a request object * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] [S r1] * * \param STD_HTTP_CLIENT : a request object * * \return [S r1] : the urls **/ fun std_httpGetUrl (o)= o.stdurl;; /*! \brief Set the callbacks to a persistent request object. * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT fun [STD_HTTP_CLIENT S] I fun [STD_HTTP_CLIENT I] fun [STD_HTTP_CLIENT I]] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param fun [STD_HTTP_CLIENT S] I : the callback (success) * \param fun [STD_HTTP_CLIENT I] I : the calback (ongoing) * \param fun [STD_HTTP_CLIENT I] I : the callback (errors) * * \return STD_HTTP_CLIENT : the same object or nil if error (object is not valid) **/ fun std_httpSetCallbacks (o, cbOk, cbCur, cbError)= if !std_httpobjcheck o then nil else ( set o.stdcbinetok = cbOk; set o.stdcbinetdownloading = cbCur; set o.stdcbineterror = cbError; o );; /*! \brief Return the current callbacks, defined in a persistent request object * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] [fun [STD_HTTP_CLIENT S] I fun [STD_HTTP_CLIENT I] fun [STD_HTTP_CLIENT I]] * * \param STD_HTTP_CLIENT : a persistent request object * * \return [fun [STD_HTTP_CLIENT S] I fun [STD_HTTP_CLIENT I] fun [STD_HTTP_CLIENT I]] : the three callbacks or nil if the object is invalid **/ fun std_httpGetCallbacks (o)= if !std_httpobjcheck o then nil else [o.stdcbinetok o.stdcbinetdownloading o.stdcbineterror];; /*! \brief Set the verb to the all process in this object. * * \ingroup std_http * \remark It is possible but not recommended to the beginner to change this * parameter more once time * * \see std_httpSupportedVerb * * Prototype : fun [STD_HTTP_CLIENT S] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param S : a new valid verb. * * \return STD_HTTP_CLIENT : the same object or nil if error (object not persistent or unknown verb) **/ fun std_httpSetVerb (o, szVerb)= if !std_httpobjcheck o then nil else if !std_lStriIsInList StdHttpSupportedVerb szVerb then ( set o.stdinetverb = std_httpconvverb szVerb; o ) else nil;; /*! \brief Return the verb to the given request object * * \ingroup std_http * \see std_httpSupportedVerb * * Prototype : fun [STD_HTTP_CLIENT] S * * \param STD_HTTP_CLIENT : a request object * * \return S : the verb **/ fun std_httpGetVerb (o)= std_httpconvverb2 o.stdinetverb;; /*! \brief Set if a request object is enabled or disabled. * * If the request object is persistent, no operation can be done while it is * disabled. * If the request object is not persistent, the current operation will be * stopped and destroyed if the new state is 'disabled'. * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT I] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param I : a new state (1 : enabled, 0 disabled, all other values are ignored. * * \return STD_HTTP_CLIENT : the same object **/ fun std_httpSetEnabled (o, state)= set state = if (state != 1) && (state != 0) then o.stdinetenabled else state; set o.stdinetenabled = state; o;; /*! \brief Return if a request object is enabled (1) or disabled (0) * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] I * * \param STD_HTTP_CLIENT : a request object * * \return I : the state **/ fun std_httpGetEnsabled (o)= o.stdinetenabled;; /*! \brief Return if a request object is persistent (1) or not (0) * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] I * * \param STD_HTTP_CLIENT : a request object * * \return I : this state * * \remark this parameter can't be set after its initialization. **/ fun std_httpGetPersistent (o)= o.stdinetpersistent;; /*! \brief Set if the urls should be removed in the list if the end * of the request is a success. * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT I] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param I : a new state (1 : enabled (default), 0 disabled, all other values are ignored. * * \return STD_HTTP_CLIENT : the same object or nil if the object is invalid **/ fun std_httpSetRemoveInSuccess (o, state)= if !std_httpobjcheck o then nil else ( set state = if (state != 1) && (state != 0) then o.stdintremoveinsuccess else state; set o.stdintremoveinsuccess = state; o );; /*! \brief Return if urls should be removed if the process is a success. * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] I * * \param STD_HTTP_CLIENT : a request object * * \return I : this state **/ fun std_httpGetRemoveInSuccess (o)= o.stdintremoveinsuccess;; /*! \brief Set if the connection must be testes before each request or not. * This is helpful in a local request for example. * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT I] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param I : a new state (1 : yes (default), 0 no, all other values are ignored. * * \return STD_HTTP_CLIENT : the same object or nil if the object is invalid **/ fun std_httpSetTestConection (o, state)= set state = if (state != 1) && (state != 0) then o.stdinetislocal else state; set o.stdinetislocal = state;; /*! \brief Return if the connection must be testes before each request or not. * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] I * * \param STD_HTTP_CLIENT : a request object * * \return I : this state (1 : yes, 0 : no) **/ fun std_httpGetTestConection (o)= o.stdinetislocal;; /*! \brief Set if the all urls should be processed one by one (1) or not (0). * If 0, std_httpRun should be called manually after each previous result. * * \ingroup std_http * \remark if state is 0 and std_httpGetRemoveInSuccess is at 0 too, the behavior * can be undefined. * * Prototype : fun [STD_HTTP_CLIENT I] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * \param I : a new state (1 : one by one, state by default, 0 disabled, all other values are ignored. * * \return STD_HTTP_CLIENT : the same object or nil if the object is invalid **/ fun std_httpSetContinueAll (o, state)= if !std_httpobjcheck o then nil else ( set state = if (state != 1) && (state != 0) then o.stdinetcontinue else state; set o.stdinetcontinue = state; o );; /*! \brief Return if all urls should be processed one by one (1) or not (0). * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] I * * \param STD_HTTP_CLIENT : a request object * * \return I : this state **/ fun std_httpGetContinueAll (o)= o.stdinetcontinue;; /*! \brief Run the process according to the choosen parameters (see others functions in this API) * * \ingroup std_http * Prototype : fun [STD_HTTP_CLIENT] STD_HTTP_CLIENT * * \param STD_HTTP_CLIENT : a persistent request object * * \return STD_HTTP_CLIENT : the same object or nil if the object is invalid **/ fun std_httpRun (o)= if !std_httpobjcheck o then ( exec o.stdcbineterror with [o STD_HTTPNOTPERSISTENT]; nil ) else if o.stdinetrunning then ( exec o.stdcbineterror with [o STD_HTTPALREADYRUNNING]; nil ) else std_httprun o;; /*! \brief Return the current url to test the connection. * * \ingroup std_http * Prototype : fun [] S * * \return S : the url **/ fun std_httpGetTest ()= StdHttpClientUrlTest;; /*! \brief Set the url to test the connection. * * \ingroup std_http * Prototype : fun [S] S * * \param S : an url * * \return S : the same url or nil if error (empty url) **/ fun std_httpSetTest (szUrl)= if std_szIsEmpty szUrl then nil else set StdHttpClientUrlTest = szUrl;; /*! \brief Return all request objects. * * \ingroup std_http * Prototype : fun [] [STD_HTTP_CLIENT r1] * * \return [STD_HTTP_CLIENT r1] : the list **/ fun std_httpGetObjects ()= StdHttpClientList;; /*! \brief Return the last request object added. * * \ingroup std_http * Prototype : fun [] STD_HTTP_CLIENT * * \return STD_HTTP_CLIENT : this last object **/ fun std_httpGetLastObject ()= hd StdHttpClientList;; /*! \brief Return a list of supported request (GET, POST, ...) * * \ingroup std_http * Prototype : fun [] [S r] * * \return [S r] : this list **/ fun std_httpSupportedVerb ()= StdHttpSupportedVerb;;