/* *********************************************************************
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;;