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