/* ********************************************************************* 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 a Simple HTTP server * See http://redmine.scolring.org/projects/tutorials/wiki/Scol_usage * for more informations */ /*! \file httpsrv.pkg * \author Scol team * \version 0.1 * \copyright GNU Lesser General Public License 2.0 or later * \brief Scol Standard Library - Simple HTTP server API * * \details This API provides some options to write a simple HTTP server for Scol * \remark This API requires the list API ( lib/std/list.pkg ) and the string API * ( lib/std/string.pkg ) * **/ proto std_httpsrvcbAsync = fun [HTTPcon STD_HTTP_SERVER S] S;; /*! \struct STD_HTTP_SERVER * * \brief Internal http server structure. You should not call it directly, use * API instead ! * **/ struct STD_HTTP_SERVER = [ stdhttpsrvid : I, /*!< Id number */ stdhttpsrv : HTTPserver, /*!< http server Scol object */ stdhttpsrvtype : I, /*!< synchronous (STD_HTTPSRV_SYNC) or asynchronous (STD_HTTPSRV_ASYNC) */ stdhttpsrvport : I, /*!< tcp-ip port number (80 by default) */ stdhttpsrvcb : fun [STD_HTTP_SERVER_REQUEST] S,/*!< callback called when a request is received */ stdhttpsrvcb2 : fun [STD_HTTP_SERVER_REQUEST] I,/*!< callback called when a connection is accepted */ stdhttpsrvheader : S, /*!< http header for the response, by default is "HTTP/1.0 200 OK\13\10Server: SCOL HTTP server\13\10Content-Type: text/html\13\10\13\10" */ stdhttpsrvmaxsizepost : I, /*!< max size of a POST request. If larger, the request will be ignored */ stdhttpsrvlogged : I, /*!< the srv activity is logged (1) or not (0, default) */ stdhttpsrvreqs : [STD_HTTP_SERVER_REQUEST r1], /*!< opened connections list */ stdhttpsrvreqn : I /*!< total number of received request from the starter of the server, nil if "infinite" */ ] mkSTD_HTTP_SERVER;; /*! \struct STD_HTTP_SERVER_REQUEST * * \brief Internal http connection request structure. You should not call it directly, use * API instead ! * **/ struct STD_HTTP_SERVER_REQUEST = [ stdhttpsrvreqid : I, /*! internal id connection */ stdhttpsrvreq : S, /*!< received request */ stdhttpsrvcon : HTTPcon, /*!< current connection object */ stdhttpsrvreqclose : fun [I] I, /*!< called when a connection is closed */ stdhttpsrvanswerhead : S, /*!< specific header response */ stdhttpsrvanswerS : S, /*!< srring to send from this server to answer to this request */ stdhttpsrvanswerP : P /*!< file to send from this server to answer to this request */ ] mkSTD_HTTP_SERVER_REQUEST;; var STD_HTTPSRV_SYNC = 0;; var STD_HTTPSRV_ASYNC = 1;; var STD_HTTPSRV_ID = -1;; var STD_HTTPSRV_IDCON = -1;; fun std_httpsrv_itoh8 (i)= let itoh i -> s in strcat substr "00000000" 0 8-strlen s s;; fun std_httpsrv_itoh4 (i)= let itoh i -> s in strcat substr "0000" 0 4-strlen s s;; fun std_httpsrvdate ()= let gmtime time -> [sec mn h d m y _ _] in strcatn "--"::(itoa y)::" "::(itoa m)::" "::(itoa d)::"-"::(itoa h)::"h "::(itoa mn)::"mn "::(itoa sec)::"s"::nil;; fun std_httpsrvfoo (o, s)= if o.stdhttpsrvlogged then _fooS strcatn std_httpsrvdate ::"\n---"::"HTTP SERVER "::(itoa o.stdhttpsrvid)::" "::s::"\n"::nil else nil; o.stdhttpsrvlogged;; fun std_httpsrvtypestr (o)= if o.stdhttpsrvtype == STD_HTTPSRV_SYNC then "synchronous" else "asynchronous";; fun std_httpsrvreqcmp (r, c)= r.stdhttpsrvcon == c;; fun std_httpsrvreqinc (o)= if (o.stdhttpsrvreqn >= 2147483647) || (o.stdhttpsrvreqn == nil) then set o.stdhttpsrvreqn = nil else set o.stdhttpsrvreqn = o.stdhttpsrvreqn + 1;; /* a connection is closing ... */ fun std_httpsrvrequestcbclose (con, oreq)= exec oreq.stdhttpsrvreqclose with [oreq.stdhttpsrvreqid]; set oreq = nil; 0;; fun std_httpsrvrequestnew (con, o, request)= std_httpsrvreqinc o; let std_lEltPosFunc o.stdhttpsrvreqs @std_httpsrvreqcmp con -> n in if n == nil then let mkSTD_HTTP_SERVER_REQUEST [ set STD_HTTPSRV_IDCON = STD_HTTPSRV_IDCON + 1 request con nil o.stdhttpsrvheader nil nil ] -> oreq in ( set o.stdhttpsrvreqs = oreq :: o.stdhttpsrvreqs; rflHTTPclose con @std_httpsrvrequestcbclose oreq; oreq ) else let nth_list o.stdhttpsrvreqs n -> oreq in ( set oreq.stdhttpsrvreq = request; oreq );; fun std_httpsrvcbSync (con, o, request)= let std_httpsrvrequestnew con o request -> req in // let mkSTD_HTTP_SERVER_REQUEST [request con o.stdhttpsrvheader nil nil] -> req in let exec o.stdhttpsrvcb with [req] -> str in ( let strcatn "received id request "::(itoa req.stdhttpsrvreqid)::" : "::request::nil -> szLog in std_httpsrvfoo o szLog; let strcatn "id connection = "::(itoa req.stdhttpsrvreqid)::" synchronous answer : "::req.stdhttpsrvanswerhead::"\n"::str::nil -> szLog in std_httpsrvfoo o szLog; set o.stdhttpsrvreqs = std_lRemoveElt o.stdhttpsrvreqs req; strcat req.stdhttpsrvanswerhead str );; fun std_httpsrvcbAsync (con, o, request)= let std_httpsrvrequestnew con o request -> req in // let mkSTD_HTTP_SERVER_REQUEST [request con o.stdhttpsrvheader nil nil] -> req in ( exec o.stdhttpsrvcb with [req]; let strcatn "received id request "::(itoa req.stdhttpsrvreqid)::" : "::request::nil -> szLog in std_httpsrvfoo o szLog; nil );; fun std_httpsrvcb2 (con, o)= if o.stdhttpsrvcb2 == nil then nil else let std_httpsrvrequestnew con o nil -> req in // let mkSTD_HTTP_SERVER_REQUEST [nil con o.stdhttpsrvheader nil nil] -> req in let strcat ": a connection has been accepted by this server from " getHTTPclientIP con -> szLog in ( std_httpsrvfoo o szLog; exec o.stdhttpsrvcb2 with [req] );; fun std_httpsrvcloseallcon (oreq, o)= let closeHTTPcon oreq.stdhttpsrvcon -> r in let if !r then "OK" else "KO" -> str in let strcatn "Close connection from "::oreq.stdhttpsrvreq::" : "::str::nil -> szLog in std_httpsrvfoo o szLog; 0;; fun std_httpsrvnew (port, cbfun, type)= if (type != STD_HTTPSRV_ASYNC) && (type != STD_HTTPSRV_SYNC) then nil else ( set port = if (port == nil) || (port <= 0) || (port >= 65635) then 80 else port; mkSTD_HTTP_SERVER [ set STD_HTTPSRV_ID = STD_HTTPSRV_ID + 1 nil type port cbfun nil "HTTP/1.0 200 OK\13\10Server: SCOL HTTP server\13\10Content-Type: text/html\13\10\13\10" 0 1 nil 0 ] );; /*! \brief Create a new synchronous Simple HTTP server. * * \ingroup std_httpsrv * Prototype : fun [Chn I fun [STD_HTTP_SERVER_REQUEST] S] STD_HTTP_SERVER * * \param Chn : the channel where the server will be created. Generally, * it is the current channel ( _channel ). * \param I : the TCP-IP listen port. By default, it is on 80. * \param fun [STD_HTTP_SERVER_REQUEST] S : each time that a request is received, * this function is called. Its argument is a request object. Its API is in this * same package. The function must return a string. This string is the response * to the client. Don't forget to include a comprehensive header in the answer. * * \return STD_HTTP_SERVER : a new server object or nil if an error is occured. **/ fun std_httpsrvNewSync (chn, port, cbfun)= let std_httpsrvnew port cbfun STD_HTTPSRV_SYNC -> o in ( set o.stdhttpsrv = startHTTPserver chn o.stdhttpsrvport @std_httpsrvcbSync o; if o.stdhttpsrv == nil then let strcatn "failed : type : "::(std_httpsrvtypestr o)::" port : "::(itoa o.stdhttpsrvport)::nil -> szLog in ( std_httpsrvfoo o szLog; set o = nil ) else let strcatn "started : type : "::(std_httpsrvtypestr o)::" port : "::(itoa o.stdhttpsrvport)::nil -> szLog in ( rflHTTPaccept o.stdhttpsrv @std_httpsrvcb2 o; std_httpsrvfoo o szLog; set o.stdhttpsrvlogged = 0; o ) );; /*! \brief Create a new asynchronous Simple HTTP server. * * \ingroup std_httpsrv * Prototype : fun [Chn I fun [STD_HTTP_SERVER_REQUEST] S] STD_HTTP_SERVER * * \param Chn : the channel where the server will be created. Generally, * it is the current channel ( _channel ). * \param I : the TCP-IP listen port. By default, it is on 80. * \param fun [STD_HTTP_SERVER_REQUEST] S : each time that a request is received, * this function is called. Its argument is a request object. Its API is in this * same package. The function should return nil. * To answser, see in this same API. As the server will be asynchronous, the application * send the response and manage the connection manually. * * \return STD_HTTP_SERVER : a new server object or nil if an error is occured. **/ fun std_httpsrvNewAsync (chn, port, cbfun)= let std_httpsrvnew port cbfun STD_HTTPSRV_ASYNC -> o in ( set o.stdhttpsrv = startHTTPserver chn o.stdhttpsrvport @std_httpsrvcbAsync o; if o.stdhttpsrv == nil then let strcatn "failed : type : "::(std_httpsrvtypestr o)::" port : "::(itoa o.stdhttpsrvport)::nil -> szLog in ( std_httpsrvfoo o szLog; set o = nil ) else let strcatn "started : type : "::(std_httpsrvtypestr o)::" port : "::(itoa o.stdhttpsrvport)::nil -> szLog in ( std_httpsrvfoo o szLog; set o.stdhttpsrvlogged = 0; o ) );; /*! \brief Close a server and its opened conections. * All active connections will be closed too. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] I * * \param STD_HTTP_SERVER : a server object * * \return I : always 0 **/ fun std_httpsrvClose (o)= closeHTTPserver o.stdhttpsrv; std_lApplyFunc o.stdhttpsrvreqs @std_httpsrvcloseallcon o; o.stdhttpsrvreqs == nil; let "is closing ..." -> szLog in std_httpsrvfoo o szLog; set o = nil; 0;; /*! \brief Return if the server is synchronous or asynchronous. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] I * * \param STD_HTTP_SERVER : a valid server object * * \return I : STD_HTTPSRV_SYNC or STD_HTTPSRV_ASYNC (nil if the object is not valid) */ fun std_httpsrvGetType (o)= o.stdhttpsrvtype;; /*! \brief Return the listened port. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] I * * \param STD_HTTP_SERVER : a valid server object * * \return I : the port number (nil if the object is not valid) */ fun std_httpsrvGetPort (o)= o.stdhttpsrvport;; /*! \brief Return if the operations are (or not) currently logged in the * application log file (depending on the Scol settings). * * \ingroup std_httpsrv * \remark The std error API ( lib/std/error.pkg ) allows a better way for this. * * Prototype :fun [STD_HTTP_SERVER] I * * \param STD_HTTP_SERVER : a valid server object * * \return I : yes (1) or no, value by default (0) (nil if the object is not valid) */ fun std_httpsrvGetLogged (o)= o.stdhttpsrvlogged;; /*! \brief Return the current called function when a request is received. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] fun [STD_HTTP_SERVER_REQUEST] S * * \remark Call this function doesn't execute the callback ! * * \param STD_HTTP_SERVER : a valid server object * * \return fun [STD_HTTP_SERVER_REQUEST] S : the callback (nil if the object is not valid) */ fun std_httpsrvGetCallback (o)= o.stdhttpsrvcb;; /*! \brief Return the current called function when a connection is accepted. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] fun [STD_HTTP_SERVER_REQUEST] I * * \remark Call this function doesn't execute the callback ! * \remark This callback is defined to nil by default. * * \param STD_HTTP_SERVER : a valid server object * * \return fun [STD_HTTP_SERVER_REQUEST] I : the callback (nil if the object is not valid) */ fun std_httpsrvGetCallbackAccept (o)= o.stdhttpsrvcb2;; /*! \brief Return the current default header for the answers. * * \ingroup std_httpsrv * \remark This header will be automatically set in each new STD_HTTP_SERVER_REQUEST * object. If another header is needed, you can change it. * \remark By default this header is "HTTP/1.0 200 OK\13\10Server: SCOL HTTP server\13\10Content-Type: text/html\13\10\13\10" * \see std_httpsrvSetRequestHeader * * Prototype :fun [STD_HTTP_SERVER] S * * \param STD_HTTP_SERVER : a valid server object * * \return S : the default header (nil if the object is not valid) */ fun std_httpsrvGetHeader (o)= o.stdhttpsrvheader;; /*! \brief Return the current max size for a POST request. * * \ingroup std_httpsrv * \remark POST requests are bufferized on server side until the whole request is received. * This function defines the maximum size of the buffer. Larger requests will be ignored. * By default, this limit is undefined (0). * * Prototype :fun [STD_HTTP_SERVER] I * * \param STD_HTTP_SERVER : a valid server object * * \return I : this limit (nil if the object is not valid) */ fun std_httpsrvGetMaxSizePost (o)= o.stdhttpsrvmaxsizepost;; /*! \brief Get the current number of active (opened) connections. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] I * * \param STD_HTTP_SERVER : a valid server object * * \return I : the number of opened connections */ fun std_httpsrvGetActives (o)= sizelist o.stdhttpsrvreqs;; /*! \brief Returns few stats about this server. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] [I I I I] * * \param STD_HTTP_SERVER : a valid server object * * \return [I I I I] : a tuple : * - the total number of received request from the starter (or nil if "infinite " (NaN)) * - the number of the active connections * - the number of incoming bytes * - the number og the outgoing bytes */ fun std_httpsrvGetStats (o)= let getHTTPstats o.stdhttpsrv -> [a b c] in [o.stdhttpsrvreqn a b c];; /*! \brief Get the internal id of a server. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER] I * * \param STD_HTTP_SERVER : a valid server object * * \return I : the identifiant */ fun std_httpsrvGetId (o)= o.stdhttpsrvid;; /*! \brief Set if the operations are (or not) currently logged in the * application log file (depending on the Scol settings). * * \ingroup std_httpsrv * \remark The std error API ( lib/std/error.pkg ) allows a better way for this. * * Prototype :fun [STD_HTTP_SERVER I] STD_HTTP_SERVER * * \param STD_HTTP_SERVER : a valid server object * \param I : the new state : 0 (default) or 1. Another value is ignored. * * \return STD_HTTP_SERVER : the same object */ fun std_httpsrvSetLogged (o, state)= set state = if (state == 1) || (state == 0) then state else o.stdhttpsrvlogged; set o.stdhttpsrvlogged = state; o;; /*! \brief Set the reflex function which is called when a request is received. * This callback can be changed as you want. * * \ingroup std_httpsrv * \remark If the server is synchronous, the callback must return a string. it is * the answer, immediately sent to the client. If the server is asynchronous, the * callback should be return nil and the application send mannuyaly the answer. * * \remark If it set to nil, no response will be sent and the server will be "mute". * \remark If your code need more arguments, you should use the mkfunX functions. * * Prototype :fun [STD_HTTP_SERVER fun [STD_HTTP_SERVER_REQUEST] S] STD_HTTP_SERVER * * \param STD_HTTP_SERVER : a valid server object * \param fun [STD_HTTP_SERVER_REQUEST] S : the new callback. * * \return STD_HTTP_SERVER : the same object */ fun std_httpsrvSetCallback (o, cbfun)= set o.stdhttpsrvcb = cbfun; o;; /*! \brief Set the reflex function which is called when a connection is accepted. The * request object given by the callback doesn't contain the string request yet. This * string will be present in the request object in the received callback only. * This callback can be changed as you want. * * \ingroup std_httpsrv * \remark By default, this set to nil. * \remark If your code need more arguments, you should use the mkfunX functions. * * Prototype :fun [STD_HTTP_SERVER fun [STD_HTTP_SERVER_REQUEST] I] STD_HTTP_SERVER * * \param STD_HTTP_SERVER : a valid server object * \param fun [STD_HTTP_SERVER_REQUEST] I : the new callback. * * \return STD_HTTP_SERVER : the same object */ fun std_httpsrvSetCallbackAccept (o, cbfun)= set o.stdhttpsrvcb2 = cbfun; o;; /*! \brief Set the default header for the answers. * \see std_httpsrvGetHeader * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER S] STD_HTTP_SERVER * * \param STD_HTTP_SERVER : a valid server object * \param S : the new default header. * * \return STD_HTTP_SERVER : the same object */ fun std_httpsrvSetHeader (o, szDefaultHeader)= set o.stdhttpsrvheader = szDefaultHeader; o;; /*! \brief Set the max size for a POST request. * \see std_httpsrvGetMaxSizePost * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER I] STD_HTTP_SERVER * * \param STD_HTTP_SERVER : a valid server object * \param I : the new positive size (a negative value will be ignored) * * \return STD_HTTP_SERVER : the same object */ fun std_httpsrvSetMaxSizePost (o, size)= set size = if size >= 0 then size else o.stdhttpsrvmaxsizepost; set o.stdhttpsrvmaxsizepost = size; o;; /*! \brief Get the full received request. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] S * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return S : the received request */ fun std_httpsrvGetRequest (oreq)= oreq.stdhttpsrvreq;; /*! \brief Get the current defined header for a request object. * \see std_httpsrvSetRequestHeader * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] S * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return S : the header */ fun std_httpsrvGetRequestHeader (oreq)= oreq.stdhttpsrvanswerhead;; /*! \brief Return the IP address of the client (or its proxy, ...). * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] S * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return S : the IP */ fun std_httpsrvGetRequestIP (oreq)= getHTTPclientIP oreq.stdhttpsrvcon;; /*! \brief Return the size of the buffer request. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] I * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return I : the size * * \remark This function can crash the VM, so while it is not fixed, * it does nothing and return always 0 */ fun std_httpsrvGetRequestSize (oreq)= 0;; // getHTTPbufferSize oreq.stdhttpsrvcon;; /*! \brief Return the id of a connection. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] I * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return I : the id **/ fun std_httpsrvGetRequestId (oreq)= oreq.stdhttpsrvreqid;; /*! \brief Return the defined callback when a connection is closed. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] fun [I] I * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return fun [I] I : the callback **/ fun std_httpsrvGetRequestClose (oreq)= oreq.stdhttpsrvreqclose;; /*! \brief Set the callback when a connection is closed. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST fun [I] I] STD_HTTP_SERVER_REQUEST * * \param STD_HTTP_SERVER_REQUEST : a valid request object * \param fun [I] I : function to call. Its argument is the id of the connection. * * \return STD_HTTP_SERVER_REQUEST : the same request object **/ fun std_httpsrvSetRequestClose (oreq, cbfun)= set oreq.stdhttpsrvreqclose = cbfun; oreq;; /*! \brief Set the header answer of a specific received request. * By default, it is the default server header. * \see std_httpsrvGetHeader * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST S] STD_HTTP_SERVER_REQUEST * * \param STD_HTTP_SERVER_REQUEST : a valid request object * \param S : an header * * \return STD_HTTP_SERVER_REQUEST : the same object request */ fun std_httpsrvSetRequestHeader (oreq, szHeader)= set oreq.stdhttpsrvanswerhead = szHeader; oreq;; /*! \brief Set the answer of a specific received request. The header will be * automatically added when the answser is sent. * \see std_httpsrvSetRequestHeader * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST S] STD_HTTP_SERVER_REQUEST * * \param STD_HTTP_SERVER_REQUEST : a valid request object * \param S : an answer * * \return STD_HTTP_SERVER_REQUEST : the same object request * * \remark to send a file, see std_httpsrvSetResponseP * * \remark This function sould be used with a asynchronous server only */ fun std_httpsrvSetResponseS (oreq, szResponse)= set oreq.stdhttpsrvanswerS = szResponse; oreq;; /*! \brief Set the file to send to a specific received request. The header will be * automatically added when the answser is sent. The content file won't loaded in * Scol memory. * \see std_httpsrvSetRequestHeader * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST P] STD_HTTP_SERVER_REQUEST * * \param STD_HTTP_SERVER_REQUEST : a valid request object * \param P : a read reference file * * \return STD_HTTP_SERVER_REQUEST : the same object request * * \remark to send a normal string, see std_httpsrvSetResponseS * * \remark This function sould be used with a asynchronous server only */ fun std_httpsrvSetResponseP (oreq, pFile)= set oreq.stdhttpsrvanswerP = pFile; oreq;; /*! \brief Send an anwser to a client by an opened connection request. * You should set std_httpsrvSetResponseS or std_httpsrvSetResponseP * before use this function. * \see std_httpsrvSetResponseS * \see std_httpsrvSetResponseP * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER STD_HTTP_SERVER_REQUEST] I * * \param STD_HTTP_SERVER : a server to send the asynchronous answer * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return I : 0 if success, another value if error * * \remark This function sould be used with a asynchronous server only */ fun std_httpsrvSend (o, oreq)= if oreq.stdhttpsrvanswerS != nil then let HTTPsend oreq.stdhttpsrvcon strcat oreq.stdhttpsrvanswerhead oreq.stdhttpsrvanswerS -> res in let strcatn "Connection "::(itoa oreq.stdhttpsrvreqid)::" asynchronous answer = "::oreq.stdhttpsrvanswerhead::"\n"::oreq.stdhttpsrvanswerS::nil -> szLog in ( std_httpsrvfoo o szLog; set oreq.stdhttpsrvanswerS = nil; res ) else if oreq.stdhttpsrvanswerP != nil then let if nil == _checkpack _PtoScol oreq.stdhttpsrvanswerP then nil else 1 -> res in // check again if res == nil then res else ( set res = HTTPsend oreq.stdhttpsrvcon oreq.stdhttpsrvanswerhead; if !res then let strcatn "Connection "::(itoa oreq.stdhttpsrvreqid)::" asynchronous answer = "::oreq.stdhttpsrvanswerhead::"\nfile = "::(_PtoScol oreq.stdhttpsrvanswerP)::nil -> szLog in ( set res = HTTPsendFile oreq.stdhttpsrvcon oreq.stdhttpsrvanswerP; res ) else res ) else nil;; /*! \brief Close an opened connection request * * Prototype :fun [STD_HTTP_SERVER STD_HTTP_SERVER_REQUEST] I * * \ingroup std_httpsrv * \param STD_HTTP_SERVER : server * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return I : 0 if success, another value if fail */ fun std_httpsrvCloseRequest (o, oreq)= let closeHTTPcon oreq.stdhttpsrvcon -> res in ( if !res then ( let strcatn "Connection "::(itoa oreq.stdhttpsrvreqid)::" is closing"::nil -> szLog in std_httpsrvfoo o szLog; set o.stdhttpsrvreqs = std_lRemoveElt o.stdhttpsrvreqs oreq ) else ( let strcatn "Connection "::(itoa oreq.stdhttpsrvreqid)::" can not be closed : "::(itoa res)::nil -> szLog in std_httpsrvfoo o szLog; nil ); res );; /*! \brief Parse a received request. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] [S S S S S S S S S S [[S S] r1]] * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return [S S S S S S S S S S [[S S] r1]] : a tuple : * - S : the "host", * - S : the "port" (80 by default), * - S : the "user agent", * - S : the used HTTP "protocol" (generally 1.0 or 1.1), * - S : the "verb" (GET, POST, ...), * - S : the data type (= the value of "Content-Type" header), * - S : the language (= the value of "Content-Language" or "Accept-Language" header), * - S : the size(lenght) of : * -# the "url", if the "verb" is "GET", * -# the "body" (exactly the value of the "Content-Lenght" header), if the "verb" is "POST", * - S : the asked "url" (without the host), * - S : the "body", the body content, if the "verb" is "POST" (should be nil with "GET"), * - [[S S] r1] : a list with key, value which have been received in the request. If unable * to find keys,values but the string is not empty, the string is returned in the first item * of the tuple (in this case, the list has one element (tuple) only). * * \remark If a value is not found, it will be nil, except for the port. * \see std_netParseRequest **/ fun std_httpsrvParseRequest (oreq)= let oreq.stdhttpsrvreq -> str in std_netParseRequest str;; /*! \brief Return the header of a received request object. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] S * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return S : the header or nil if the request is invalid * \see std_netGetHeader */ fun std_httpsrvRequestHeader (oreq)= let oreq.stdhttpsrvreq -> str in std_netGetHeader str;; /*! \brief Return the body of a received request object. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST] S * * \param STD_HTTP_SERVER_REQUEST : a valid request object * * \return S : the body or nil if no body found (with "GET" by example) * \see std_netGetBody */ fun std_httpsrvRequestBody (oreq)= let oreq.stdhttpsrvreq -> str in std_netGetBody str;; /*! \brief Function to convert a string parameters like a=b&c=d ... * to a list key,value. * * \ingroup std_httpsrv * Prototype :fun [STD_HTTP_SERVER_REQUEST S] [[S S] r1] * * \param STD_HTTP_SERVER_REQUEST : a valid request object * \param S : the verb ("GET", "POST", ...) * * \return [[S S] r1] : this list or nil if the request is invalid * \remark If this function is unable to find keys,values, it returns * a list with one tuple only, the full string is in the first item of * the tuple. * \see std_netBuildListFromParams */ fun std_httpsrvKeyValue (str, verb)= let if !strcmpi "GET" verb then 0 else 1 -> flag in std_netparseurlparams str flag;;