| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // Copyright (c) 2000-2019 Ericsson Telecom AB // |
| // // |
| // All rights reserved. This program and the accompanying materials // |
| // are made available under the terms of the Eclipse Public License v2.0 // |
| // which accompanies this distribution, and is available at // |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| /////////////////////////////////////////////////////////// |
| // Module: EPTF_CLL_DsRestAPI_HTTPServer_Functions |
| // |
| // Purpose: |
| // This module contains functions of EPTF_CLL_DsRestAPI_HTTPServer. |
| // |
| // Module depends on: |
| // <EPTF_CLL_DsRestAPI_HTTPServer_Definitions> |
| // <EPTF_CLL_DsRestAPI_DSServer_Functions> |
| // <EPTF_CLL_Base_Functions> |
| // <IPL4asp_Types> |
| // <IPL4asp_PortType> |
| // <TCCConversion_Functions> |
| // <TCCFileIO_Functions> |
| // <EPTF_CLL_HashMapInt2Int_Functions> |
| // <EPTF_CLL_FBQ_Functions> |
| // <EPTF_CLL_Common_Definitions> |
| // <EPTF_CLL_Common_Functions> |
| // <HTTPmsg_MessageLen> |
| // |
| // Current Owner: |
| // Tamas Kis (ekistam) |
| // |
| // Last Review Date: |
| // - |
| // |
| // Detailed Comments: |
| // - |
| // |
| /////////////////////////////////////////////////////////////// |
| module EPTF_CLL_DsRestAPI_HTTPServer_Functions { |
| |
| //========================================================================= |
| // Import Part |
| //========================================================================= |
| import from EPTF_CLL_DsRestAPI_HTTPServer_Definitions all; |
| import from EPTF_CLL_DsRestAPI_DSServer_Functions all; |
| import from EPTF_CLL_Base_Functions all; |
| import from IPL4asp_Types all; |
| import from IPL4asp_PortType all; |
| import from TCCConversion_Functions all; |
| import from TCCFileIO_Functions all; |
| import from EPTF_CLL_HashMap_Functions all; |
| import from EPTF_CLL_HashMapInt2Int_Functions all; |
| import from EPTF_CLL_FBQ_Functions all; |
| import from EPTF_CLL_Common_Definitions all; |
| import from EPTF_CLL_Common_Functions all; |
| import from HTTPmsg_MessageLen all; |
| |
| friend module EPTF_CLL_DsRestAPI_Functions; |
| |
| private external function ef_DsRestAPI_enc_FileInfoList(in EPTF_DsRestAPI_HTTPServer_FileInfoList_Wrapper pl_par) return octetstring with { extension "prototype(convert) encode(JSON) errorbehavior(ALL:WARNING)" } |
| |
| group FriendFunctions { |
| friend function f_EPTF_DsRestAPI_HTTPServer_init_CT(in charstring pl_selfName) runs on EPTF_DsRestAPI_HTTPServer_CT { |
| if (not v_DsRestAPI_HTTPServer_initialized) { |
| f_EPTF_HashMap_init_CT(f_EPTF_Base_selfName()); |
| v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Hash := f_EPTF_int2int_HashMap_New(cg_EPTF_DsRestAPI_Browser_chunk_FromConnId_HashMapName); |
| f_EPTF_FBQ_init_CT(pl_selfName); |
| f_EPTF_FBQ_initFreeBusyQueue(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Queue); |
| f_EPTF_FBQ_initFreeBusyQueue(v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| |
| f_EPTF_Base_init_CT(pl_selfName); |
| f_EPTF_DsRestAPI_DSServer_init_CT(pl_selfName); |
| f_EPTF_Base_registerCleanup(refers(f_EPTF_DsRestAPI_HTTPServer_cleanup_CT)); |
| v_EPTF_DsRestAPI_HTTPServer_directory := ""; |
| v_EPTF_DsRestAPI_HTTPServer_path := ""; |
| map(self:v_DsRestAPI_HTTPServer_IPL4_PCO_PT, system:v_DsRestAPI_HTTPServer_IPL4_PCO_PT); |
| var f_IPL4_getMsgLen v_getMsg_Func := refers(f_EPTF_DsRestAPI_HTTPServer_GetMsgLen); |
| f_IPL4_setGetMsgLen(v_DsRestAPI_HTTPServer_IPL4_PCO_PT,-1, v_getMsg_Func, {}); |
| t_bufferTimer.start; |
| v_DsRestAPI_HTTPServer_mainHandler := activate(as_EPTF_DsRestAPI_HTTPServer_mainHandler()); |
| v_DsRestAPI_HTTPServer_initialized := true; |
| } |
| } |
| |
| friend function f_EPTF_DsRestAPI_HTTPServer_listen(in charstring pl_hostIPAddress, in integer pl_hostPort) runs on EPTF_DsRestAPI_HTTPServer_CT return integer{ |
| var integer vl_result := -1; |
| f_EPTF_Common_user("DsRestAPI HTTP Server is trying to listen on " & pl_hostIPAddress & ":" & int2str(pl_hostPort)); |
| var Result vl_res := f_IPL4_listen(v_DsRestAPI_HTTPServer_IPL4_PCO_PT, pl_hostIPAddress, pl_hostPort, { tcp := {} }, { {reuseAddress := { enable := true } } }); |
| if (vl_res.errorCode == omit) { |
| vl_result := 0; |
| v_EPTF_DsRestAPI_HTTPServer_connId := vl_res.connId; |
| } |
| return vl_result; |
| } |
| |
| friend function f_EPTF_DsRestAPI_HTTPServer_close() runs on EPTF_DsRestAPI_HTTPServer_CT return integer{ |
| var integer vl_result := -1; |
| if (v_EPTF_DsRestAPI_HTTPServer_connId != -1) { |
| var Result vl_res := f_IPL4_close(v_DsRestAPI_HTTPServer_IPL4_PCO_PT, v_EPTF_DsRestAPI_HTTPServer_connId); |
| if (vl_res.errorCode == omit) { |
| vl_result := 0; |
| } |
| } |
| return vl_result; |
| } |
| |
| friend function f_EPTF_DsRestAPI_HTTPServer_setDir(in charstring pl_HTTPServerDir) runs on EPTF_DsRestAPI_HTTPServer_CT { |
| if(lengthof(pl_HTTPServerDir) > 0 and pl_HTTPServerDir[lengthof(pl_HTTPServerDir)-1] != cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash){ |
| pl_HTTPServerDir := pl_HTTPServerDir & cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash; |
| } |
| v_EPTF_DsRestAPI_HTTPServer_directory := pl_HTTPServerDir; |
| if(lengthof(pl_HTTPServerDir) == 0) { |
| v_EPTF_DsRestAPI_HTTPServer_path := ""; |
| } else { |
| v_EPTF_DsRestAPI_HTTPServer_path := f_EPTF_DsRestAPI_HTTPServer_directory(); |
| } |
| } |
| |
| friend external function ef_EPTF_DsRestAPI_FIO_deleteResource(in charstring pl_path) return integer; |
| |
| } // ~ group FriendFunctions |
| |
| group PrivateFunction { |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_cleanup_CT() runs on EPTF_DsRestAPI_HTTPServer_CT { |
| if (v_DsRestAPI_HTTPServer_initialized) { |
| deactivate(v_DsRestAPI_HTTPServer_mainHandler); |
| unmap(self:v_DsRestAPI_HTTPServer_IPL4_PCO_PT, system:v_DsRestAPI_HTTPServer_IPL4_PCO_PT); |
| f_EPTF_FBQ_deleteFreeBusyQueue(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Queue); |
| f_EPTF_int2int_HashMap_Delete(cg_EPTF_DsRestAPI_Browser_chunk_FromConnId_HashMapName); |
| f_EPTF_FBQ_deleteFreeBusyQueue(v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| v_EPTF_DsRestAPI_Browser_Buffer_Messages := {}; |
| v_DsRestAPI_HTTPServer_initialized := false; |
| } |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_GetMsgLen( |
| in octetstring stream, |
| inout ro_integer args) |
| return integer { |
| return f_HTTPMessage_len(stream); |
| }; |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_getExtension(in charstring pl_inputStr) return charstring { |
| var integer vl_extensionStarts := -1; |
| var integer vl_searchOffset := 0; |
| do { |
| var integer vl_extensionStartsL := f_strstr(pl_inputStr, cg_EPTF_DsRestAPI_HTTPServer_HTTP_Dot, vl_searchOffset); |
| if (vl_extensionStartsL > 0) { |
| vl_extensionStarts := vl_extensionStartsL; |
| vl_searchOffset := vl_extensionStartsL + 1; |
| } else { |
| break; |
| } |
| } while (true); |
| |
| var charstring vl_result := ""; |
| if(vl_extensionStarts > 0) { |
| vl_result := substr(pl_inputStr, vl_extensionStarts + 1, lengthof(pl_inputStr) - vl_extensionStarts - 1); |
| } |
| return f_putInLowercase(vl_result); |
| } |
| |
| // f_EPTF_DsRestAPI_HTTPServer_assembleHTTPResponseHeaders: Generate Response headers and the body separator. |
| private function f_EPTF_DsRestAPI_HTTPServer_assembleHTTPResponseHeaders(in integer pl_contentLength := 0, in charstring pl_contentType := "text/html", in charstring pl_statusText := "OK", in charstring pl_statusCode := "200") return octetstring { |
| var charstring vl_result := cg_EPTF_DsRestAPI_HTTPHeaderChunk01 |
| & pl_statusCode |
| & cg_EPTF_DsRestAPI_HTTPServer_HTTP_SP |
| & pl_statusText |
| & cg_EPTF_DsRestAPI_HTTPHeaderChunk02 |
| & int2str(pl_contentLength) |
| & cg_EPTF_DsRestAPI_HTTPHeaderChunk03 |
| & pl_contentType |
| & cg_EPTF_DsRestAPI_HTTPHeaderChunk04 |
| return char2oct(vl_result); |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_encodeHTTP(in charstring pl_message, in charstring pl_contentType := "text/html", in charstring pl_statusText := "OK", in charstring pl_statusCode := "200") return octetstring { |
| var octetstring vl_headers := f_EPTF_DsRestAPI_HTTPServer_assembleHTTPResponseHeaders(lengthof(pl_message), pl_contentType, pl_statusText, pl_statusCode); |
| return vl_headers & char2oct(pl_message); |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_encodeHTTP_oct(in octetstring pl_message, in charstring pl_contentType := "text/html", in charstring pl_statusText := "OK", in charstring pl_statusCode := "200") return octetstring { |
| var octetstring vl_headers := f_EPTF_DsRestAPI_HTTPServer_assembleHTTPResponseHeaders(lengthof(pl_message), pl_contentType, pl_statusText, pl_statusCode); |
| return vl_headers & pl_message; |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_getToken(in octetstring pl_src, in integer pl_offset, in octetstring pl_separator := '20'O) return octetstring { |
| var octetstring vl_token := ''O; |
| var integer vl_sepPosition := f_strstr_oct(pl_src, pl_separator, pl_offset); |
| if (lengthof(pl_src) > pl_offset) { |
| if (vl_sepPosition > 0) { |
| vl_token := substr(pl_src, pl_offset, vl_sepPosition - pl_offset); |
| } else { // last token |
| vl_token := substr(pl_src, pl_offset, lengthof(pl_src) - pl_offset); |
| } |
| } |
| return vl_token; |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_decodeHTTP(in octetstring pl_msg, out charstring pl_method, out charstring pl_uri, out integer pl_bodyLength, out octetstring pl_body, out charstring pl_queryString, in HostName pl_remoteHost) return integer { |
| var integer vl_result := -1; |
| var integer vl_bodyOffset := f_strstr_oct(pl_msg, cg_EPTF_DsRestAPI_HTTPServer_HTTP_CRLFCRLF_oct); |
| var charstring vl_tmp; |
| /* // Commented out as it could be the end of a really screwed up TCP chunk. |
| if (lengthof(pl_msg) < 16) { // Smaller than shortest possible request line. |
| f_EPTF_Common_user("Suspicious HTTP Request: '" & oct2str(pl_msg) & "'O"); |
| } |
| */ |
| pl_body := ''O; |
| if (lengthof(pl_msg) != 0 and f_oct2char_safe(pl_msg, vl_tmp) and f_strstr_oct(pl_msg, cg_EPTF_DsRestAPI_HTTPServer_HTTP_CRLF_oct) >= 0) { |
| var octetstring vl_requestLine := substr(pl_msg, 0, f_strstr_oct(pl_msg, cg_EPTF_DsRestAPI_HTTPServer_HTTP_CRLF_oct)); |
| pl_bodyLength := 0; |
| pl_queryString := ""; |
| pl_method := oct2char(f_EPTF_DsRestAPI_HTTPServer_getToken(vl_requestLine, 0)); |
| var charstring vl_uri := oct2char(f_EPTF_DsRestAPI_HTTPServer_getToken(vl_requestLine, lengthof(pl_method) + 1)); |
| var charstring vl_version := oct2char(f_EPTF_DsRestAPI_HTTPServer_getToken(vl_requestLine, lengthof(pl_method) + lengthof(vl_uri) + 2)); |
| var integer vl_sepPosition := f_strstr(vl_uri, "?", 0); |
| if (vl_sepPosition > 0) { |
| pl_uri := substr(vl_uri, 0, vl_sepPosition); |
| pl_queryString := substr(vl_uri, vl_sepPosition+1, lengthof(vl_uri)-vl_sepPosition-1); |
| } else { |
| pl_uri := vl_uri; |
| } |
| if (lengthof(pl_method) == 0 or lengthof(pl_uri) == 0 or lengthof(vl_version) == 0) { // Doublecheck |
| vl_result := 400; // 400 Bad Request. It is empty. |
| } else if (lengthof(pl_uri) > 2000) { |
| vl_result := 414; // Sanity check. Use POST for sending a lot of data. |
| } else { |
| if (vl_version == cg_EPTF_DsRestAPI_HTTPServer_HTTP_HTTP10VER or vl_version == cg_EPTF_DsRestAPI_HTTPServer_HTTP_HTTP11VER) { |
| if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_HEAD |
| or pl_method == cg_EPTF_DsRestAPI_HTTPServer_GET |
| or pl_method == cg_EPTF_DsRestAPI_HTTPServer_LSDIR // comment this out to allow listing everywhere |
| or pl_method == cg_EPTF_DsRestAPI_HTTPServer_MKDIR |
| or pl_method == cg_EPTF_DsRestAPI_HTTPServer_RMDIR |
| or pl_method == cg_EPTF_DsRestAPI_HTTPServer_OPTIONS |
| ) { |
| vl_result := 200; |
| } else if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_PUT or pl_method == cg_EPTF_DsRestAPI_HTTPServer_POST) { //to allow listing everything: or pl_method == cg_EPTF_DsRestAPI_HTTPServer_LSDIR |
| if(vl_bodyOffset > 0) { |
| var integer vl_contentLength := -1; |
| var integer vl_contentLengthOffset := f_strstr_oct(pl_msg, cg_EPTF_DsRestAPI_HTTPServer_HTTP_ContentLength_Name_oct); |
| if (vl_contentLengthOffset != -1) { |
| vl_contentLengthOffset := vl_contentLengthOffset + lengthof(cg_EPTF_DsRestAPI_HTTPServer_HTTP_ContentLength_Name) + 2; |
| vl_contentLength := str2int(oct2char(f_EPTF_DsRestAPI_HTTPServer_getToken(pl_msg, vl_contentLengthOffset, cg_EPTF_DsRestAPI_HTTPServer_HTTP_CRLF_oct))); |
| } |
| pl_body := substr(pl_msg, vl_bodyOffset + 4, lengthof(pl_msg) - vl_bodyOffset - 4); |
| if (lengthof(pl_body) != vl_contentLength) { |
| vl_result := -1; // It is the first chunk. Wait for the rest. |
| } else { |
| vl_result := 200; |
| } |
| } |
| } else { |
| f_EPTF_Common_user("HTTP Request contains unknown or not implemented method: '" & pl_method & "'; Offender: " & pl_remoteHost); |
| vl_result := 501; // Unknown or not implemented method. Maybe a typo. Method is case sensitive! |
| } |
| } else { |
| f_EPTF_Common_user("HTTP Request contains unknown HTTP version: '" & vl_version & "'; Offender: " & pl_remoteHost); |
| vl_result := 400; // Bad Request, as we do support http 1.0 and 1.1. Protocol is case sensitive! |
| } |
| } |
| } else { // Nonparseable request, maybe a chunk, let it be buffered. |
| vl_result := -1; |
| } |
| return vl_result; |
| } |
| |
| // f_EPTF_DsRestAPI_HTTPServer_readFile: returns true if succeeded |
| private function f_EPTF_DsRestAPI_HTTPServer_readFile(in charstring pl_fileName, out octetstring pl_result) return boolean { |
| var integer vl_retval:=-1; |
| pl_result := ''O; |
| if(pl_fileName != "") { |
| var integer vl_file := f_FIO_open_rdonly(pl_fileName); |
| if(vl_file!=-1) { |
| var integer vl_from:=f_FIO_seek_home(vl_file); |
| var integer vl_to:=f_FIO_seek_end(vl_file); |
| if(vl_to>vl_from and f_FIO_seek_home(vl_file)!=-1) { |
| vl_retval := f_FIO_read_data(vl_file, pl_result, vl_to-vl_from); |
| } |
| vl_retval := f_FIO_close(vl_file); |
| } |
| } |
| return (vl_retval != -1); |
| } |
| |
| // f_EPTF_DsRestAPI_HTTPServer_saveFile: returns true if succeeded |
| private function f_EPTF_DsRestAPI_HTTPServer_saveFile(in charstring pl_fileName, in octetstring pl_data) return boolean { |
| // TODO check if dir is outside the Setups dir ?(we might want to edit the config file) |
| var integer vl_retval:=-1; |
| if(pl_fileName != "") { |
| var integer vl_file := f_FIO_open_trunc_wronly(pl_fileName); |
| if(vl_file!=-1) { |
| vl_retval := f_FIO_write_data(vl_file, pl_data); |
| if (vl_retval != -1) { |
| vl_retval := f_FIO_close(vl_file); |
| } |
| } |
| } |
| return (vl_retval != -1); |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_directory() runs on EPTF_DsRestAPI_HTTPServer_CT return charstring { |
| var charstring vl_result := "."; |
| if(lengthof(v_EPTF_DsRestAPI_HTTPServer_directory) > 0) { |
| if(substr(v_EPTF_DsRestAPI_HTTPServer_directory, lengthof(v_EPTF_DsRestAPI_HTTPServer_directory)-1 ,1) == cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash) { |
| v_EPTF_DsRestAPI_HTTPServer_directory := substr(v_EPTF_DsRestAPI_HTTPServer_directory, 0, lengthof(v_EPTF_DsRestAPI_HTTPServer_directory)-1); |
| } |
| if (f_FIO_fileOrDirExists(v_EPTF_DsRestAPI_HTTPServer_directory & cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash)) { |
| vl_result := v_EPTF_DsRestAPI_HTTPServer_directory; |
| } |
| } |
| return vl_result; |
| } |
| |
| private external function ef_EPTF_DsRestAPI_HTTPServer_FIO_listDir(in charstring pl_dirName) return EPTF_CharstringList; |
| |
| type record EPTF_DsRestAPI_CharstringListWrapper { |
| EPTF_CharstringList contentList |
| } with { variant(contentList) "JSON: name as contentList"; } |
| |
| external function ef_EPTF_CharstringList_enc_JSON(in EPTF_DsRestAPI_CharstringListWrapper pl_par) return octetstring with { extension "prototype(convert) encode(JSON) errorbehavior(ALL:WARNING)" } |
| |
| private external function ef_EPTF_DsRestAPI_HTTPServer_FIO_listDirWithStat(in charstring pl_dirName) return EPTF_DsRestAPI_HTTPServer_FileInfoList; |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_listDirectory(in charstring pl_dirName) runs on EPTF_DsRestAPI_HTTPServer_CT return octetstring { |
| var charstring vl_dirName := pl_dirName; |
| var EPTF_DsRestAPI_HTTPServer_FileInfoList vl_dirContent := {}; |
| var octetstring vl_result := cg_EPTF_DsRestAPI_HTTPServer_HTTP_Dot_oct; |
| |
| // insert "/" at the end if it is not there: |
| if (lengthof(vl_dirName) > 0 and vl_dirName[lengthof(vl_dirName)-1] != cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash) { |
| vl_dirName := vl_dirName & cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash; |
| } |
| // truncate the first "/": |
| if (lengthof(vl_dirName) > 1 and vl_dirName[0] == cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash) { |
| vl_dirName := substr(vl_dirName, 1, lengthof(vl_dirName)-1); |
| } |
| |
| if(lengthof(v_EPTF_DsRestAPI_HTTPServer_directory) > 0) { |
| if(substr(v_EPTF_DsRestAPI_HTTPServer_directory, lengthof(v_EPTF_DsRestAPI_HTTPServer_directory)-1 ,1) == cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash) { |
| vl_dirName := v_EPTF_DsRestAPI_HTTPServer_directory & vl_dirName; |
| } else { |
| vl_dirName := v_EPTF_DsRestAPI_HTTPServer_directory & cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash & vl_dirName; |
| } |
| } else { |
| vl_dirName := cg_EPTF_DsRestAPI_HTTPServer_HTTP_Dot & cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash & vl_dirName; |
| } |
| |
| vl_dirContent := ef_EPTF_DsRestAPI_HTTPServer_FIO_listDirWithStat(vl_dirName); |
| // cut server dir from the beginning: |
| var integer vl_serverDirLength := lengthof(v_EPTF_DsRestAPI_HTTPServer_directory); |
| for(var integer vl_i:=0; vl_i<sizeof(vl_dirContent); vl_i:=vl_i+1) { |
| vl_dirContent[vl_i].fileName := substr(vl_dirContent[vl_i].fileName, vl_serverDirLength, lengthof(vl_dirContent[vl_i].fileName)-vl_serverDirLength); |
| if (lengthof(vl_dirContent[vl_i].fileName) > 1 and vl_dirContent[vl_i].fileName[0] == "/") { |
| vl_dirContent[vl_i].fileName := substr(vl_dirContent[vl_i].fileName, 1, lengthof(vl_dirContent[vl_i].fileName) - 1); |
| } |
| } |
| vl_result := ef_DsRestAPI_enc_FileInfoList({fileList := vl_dirContent}); |
| return vl_result; |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_createDirectory(in charstring pl_dirName) runs on EPTF_DsRestAPI_HTTPServer_CT return boolean { |
| // TODO check if dir is outside the Setups dir |
| var charstring vl_dirName; |
| if (pl_dirName[lengthof(pl_dirName) - 1] == cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash) { |
| vl_dirName := substr(pl_dirName, 0, lengthof(pl_dirName) - 1); |
| } else { |
| vl_dirName := pl_dirName; |
| } |
| |
| return f_FIO_mkdir(vl_dirName); |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_deleteDirectory(in charstring pl_dirName) runs on EPTF_DsRestAPI_HTTPServer_CT return boolean { |
| // TODO check if dir is outside the Setups dir |
| var charstring vl_dirName; |
| if (pl_dirName[lengthof(pl_dirName) - 1] == cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash) { |
| vl_dirName := substr(pl_dirName, 0, lengthof(pl_dirName) - 1); |
| } else { |
| vl_dirName := pl_dirName; |
| } |
| |
| return ef_EPTF_DsRestAPI_FIO_deleteResource(vl_dirName) == 0; |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_handleFileRequest(in charstring pl_uri, in charstring pl_method, in charstring pl_extension, out charstring pl_contentType, in octetstring pl_body, out charstring pl_code, out charstring pl_msg) runs on EPTF_DsRestAPI_HTTPServer_CT return octetstring { |
| var octetstring vl_fileContent; |
| var charstring vl_responseText := ""; |
| pl_contentType := "text/html"; |
| pl_code := "200"; |
| pl_msg := "OK"; |
| if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_GET) { |
| if (pl_uri == cg_EPTF_DsRestAPI_HTTPServer_HTTP_Slash) { |
| pl_uri := v_EPTF_DsRestAPI_HTTPServer_mainHtml; |
| pl_extension := "html"; |
| } |
| var boolean vl_fileExists := f_EPTF_DsRestAPI_HTTPServer_readFile(v_EPTF_DsRestAPI_HTTPServer_path & pl_uri, vl_fileContent); |
| if (not vl_fileExists) { |
| vl_fileExists := f_EPTF_DsRestAPI_HTTPServer_readFile(pl_uri, vl_fileContent); |
| } |
| if (vl_fileExists) { |
| if (pl_extension == "html") { |
| pl_contentType := "text/html; charset=UTF-8"; |
| } else if (pl_extension == "css") { |
| pl_contentType := "text/css"; |
| } else if (pl_extension == "js") { |
| pl_contentType := "text/javascript"; |
| } else if (pl_extension == "json") { |
| pl_contentType := "application/json"; |
| } else if ((pl_extension == "jpg") or |
| (pl_extension == "jpeg")) { |
| pl_contentType := "image/jpeg"; |
| } else if (pl_extension == "ico") { |
| pl_contentType := "image/x-icon"; |
| } else if ((pl_extension == "gif") or |
| (pl_extension == "bmp") or |
| (pl_extension == "png")) { |
| pl_contentType := "image/" & pl_extension; |
| } |
| } else { |
| vl_responseText := "Unhandled request: '" & pl_uri & "' in directory: '" & v_EPTF_DsRestAPI_HTTPServer_path & "'"; |
| pl_code := "404"; |
| pl_msg := "Not Found"; |
| pl_contentType := "text/plain"; |
| } |
| } else if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_PUT) { |
| if (f_EPTF_DsRestAPI_HTTPServer_saveFile(v_EPTF_DsRestAPI_HTTPServer_path & pl_uri, pl_body)) { |
| vl_responseText := "File saved"; |
| } else { |
| vl_responseText := "File not saved"; |
| pl_code := "500"; |
| pl_msg := "Internal Server Error"; |
| } |
| } else if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_LSDIR) { |
| vl_fileContent := f_EPTF_DsRestAPI_HTTPServer_listDirectory(pl_uri); |
| //to allow listing everything: vl_fileContent := f_EPTF_DsRestAPI_HTTPServer_listDirectory(oct2char(pl_body)); |
| } else if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_MKDIR) { |
| if (f_EPTF_DsRestAPI_HTTPServer_createDirectory(v_EPTF_DsRestAPI_HTTPServer_path & pl_uri)) { |
| vl_responseText := "Directory created"; |
| } else { |
| vl_responseText := "Error creating directory"; |
| pl_code := "500"; |
| pl_msg := "Internal Server Error"; |
| } |
| } else if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_RMDIR) { |
| if (f_EPTF_DsRestAPI_HTTPServer_deleteDirectory(v_EPTF_DsRestAPI_HTTPServer_path & pl_uri)) { |
| vl_responseText := "Directory deleted"; |
| } else { |
| vl_responseText := "Error deleting directory"; |
| pl_code := "500"; |
| pl_msg := "Internal Server Error"; |
| } |
| } |
| if (lengthof(vl_responseText) > 0) { // Direct textual response was fabricated. |
| vl_fileContent := char2oct(vl_responseText); |
| } |
| if (not isbound(vl_fileContent)) { |
| vl_fileContent := char2oct("error serving " & pl_uri); |
| } |
| return vl_fileContent; |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_handleJSONRequest(in integer pl_connId, in octetstring pl_body) runs on EPTF_DsRestAPI_HTTPServer_CT { |
| if (f_EPTF_FBQ_getLengthOfBusyChain(v_EPTF_DsRestAPI_Browser_Buffer_FBQ) > v_EPTF_DsRestAPI_Browser_Buffer_maxSize) { |
| if (v_EPTF_DsRestAPI_Browser_Buffer_dropAll) { |
| f_EPTF_FBQ_deleteFreeBusyQueue(v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| f_EPTF_FBQ_initFreeBusyQueue(v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| v_EPTF_DsRestAPI_Browser_Buffer_Messages := {}; |
| } else { |
| var integer vl_indexToDrop; |
| if(f_EPTF_FBQ_getBusyTailIdx(vl_indexToDrop, v_EPTF_DsRestAPI_Browser_Buffer_FBQ)) { |
| f_EPTF_FBQ_moveFromBusyToFreeHead(vl_indexToDrop, v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| } |
| } |
| } |
| |
| var integer vl_nextItem := f_EPTF_FBQ_getOrCreateFreeSlot(v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| v_EPTF_DsRestAPI_Browser_Buffer_Messages[vl_nextItem] := {pl_body, pl_connId}; |
| f_EPTF_FBQ_moveFromFreeHeadToBusyTail(v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_processMessage(in integer pl_connId, in charstring pl_uri, in charstring pl_method, in charstring pl_extension, in octetstring pl_body) runs on EPTF_DsRestAPI_HTTPServer_CT { |
| var octetstring vl_resultContent := ''O; |
| var charstring vl_contentType := "text/plain"; |
| var charstring vl_code := "200"; |
| var charstring vl_msg := "OK"; |
| var charstring vl_actionExt := "dsapi"; |
| if (pl_extension == vl_actionExt) { |
| f_EPTF_DsRestAPI_HTTPServer_handleJSONRequest(pl_connId, pl_body); |
| } else { |
| vl_resultContent := f_EPTF_DsRestAPI_HTTPServer_handleFileRequest(pl_uri, pl_method, pl_extension, vl_contentType, pl_body, vl_code, vl_msg); |
| if (pl_method == cg_EPTF_DsRestAPI_HTTPServer_HEAD) { |
| vl_resultContent := ''O; |
| } |
| vl_resultContent := f_EPTF_DsRestAPI_HTTPServer_encodeHTTP_oct(vl_resultContent, vl_contentType, vl_msg, vl_code); |
| var ASP_Send vl_responseMsg := { pl_connId, {tcp := {}}, vl_resultContent }; |
| v_DsRestAPI_HTTPServer_IPL4_PCO_PT.send(vl_responseMsg); |
| } |
| } |
| |
| private function f_EPTF_DsRestAPI_HTTPServer_processHTTPMessage(in integer pl_connId, in octetstring pl_msg, out octetstring pl_parsed, in HostName pl_remoteHost) runs on EPTF_DsRestAPI_HTTPServer_CT return boolean { |
| var charstring vl_method := ""; |
| var charstring vl_uri := ""; |
| var charstring vl_queryString := ""; |
| var integer vl_bodyLength := 0; |
| var octetstring vl_body := ''O; |
| var boolean vl_result := true; |
| var integer vl_decodingResult := f_EPTF_DsRestAPI_HTTPServer_decodeHTTP(pl_msg, vl_method, vl_uri, vl_bodyLength, vl_body, vl_queryString, pl_remoteHost); |
| pl_parsed := ''O; |
| if (vl_decodingResult != -1) |
| { |
| if (vl_decodingResult == 0 or vl_decodingResult == 200) { |
| f_EPTF_DsRestAPI_HTTPServer_processMessage(pl_connId, vl_uri, vl_method, f_EPTF_DsRestAPI_HTTPServer_getExtension(vl_uri), vl_body); |
| } else if (vl_decodingResult == 501) { |
| pl_parsed := f_EPTF_DsRestAPI_HTTPServer_encodeHTTP("Unknown HTTP Method\r\n", "text/plain", "Not Implemented", "501"); |
| } else if (vl_decodingResult == 400) { |
| pl_parsed := f_EPTF_DsRestAPI_HTTPServer_encodeHTTP("Not supported transport type and/or version, or malformed request.\r\n", "text/plain", "Bad Request", "400"); |
| } else if (vl_decodingResult == 414) { |
| pl_parsed := f_EPTF_DsRestAPI_HTTPServer_encodeHTTP("Request-URI Too Long. Limit: 2000 octets; was: " & int2str(lengthof(vl_uri)) & " octets. Please convert your logic to a POST request.\r\n", "text/plain", "Request-URI Too Long", "414"); |
| } else { |
| pl_parsed := f_EPTF_DsRestAPI_HTTPServer_encodeHTTP("Cannot process the request.\r\n", "text/plain", "Internal Server Error", "500"); |
| } |
| } else { |
| vl_result := false; |
| } |
| return vl_result; |
| } |
| |
| private altstep as_EPTF_DsRestAPI_HTTPServer_mainHandler () runs on EPTF_DsRestAPI_HTTPServer_CT { |
| var ASP_RecvFrom vl_ASP_RecvFrom; |
| var ASP_Event vl_ASP_Event; |
| [] v_DsRestAPI_HTTPServer_IPL4_PCO_PT.receive(ASP_RecvFrom:?) -> value vl_ASP_RecvFrom { |
| var integer vl_chunkId := -1; |
| var octetstring vl_responseStr; |
| var boolean vl_msgParsed := f_EPTF_DsRestAPI_HTTPServer_processHTTPMessage(vl_ASP_RecvFrom.connId, vl_ASP_RecvFrom.msg, vl_responseStr, vl_ASP_RecvFrom.remName); |
| if(not vl_msgParsed){ |
| if(f_EPTF_int2int_HashMap_Find(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Hash, vl_ASP_RecvFrom.connId, vl_chunkId)) { |
| vl_msgParsed := f_EPTF_DsRestAPI_HTTPServer_processHTTPMessage(vl_ASP_RecvFrom.connId, v_EPTF_DsRestAPI_Browser_chunk[vl_chunkId] & vl_ASP_RecvFrom.msg, vl_responseStr, vl_ASP_RecvFrom.remName); |
| } |
| } |
| if(vl_msgParsed) { |
| if(vl_chunkId!=-1) { |
| v_EPTF_DsRestAPI_Browser_chunk[vl_chunkId] := ''O; |
| f_EPTF_FBQ_moveFromBusyToFreeHead(vl_chunkId, v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Queue); |
| f_EPTF_int2int_HashMap_Erase(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Hash, vl_ASP_RecvFrom.connId); |
| } |
| } else { |
| vl_chunkId := -1; |
| if(f_EPTF_int2int_HashMap_Find(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Hash, vl_ASP_RecvFrom.connId, vl_chunkId)) { |
| v_EPTF_DsRestAPI_Browser_chunk[vl_chunkId] := v_EPTF_DsRestAPI_Browser_chunk[vl_chunkId] & vl_ASP_RecvFrom.msg; |
| } else { |
| vl_chunkId := f_EPTF_FBQ_getOrCreateFreeSlot(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Queue); |
| v_EPTF_DsRestAPI_Browser_chunk[vl_chunkId] := vl_ASP_RecvFrom.msg; |
| f_EPTF_FBQ_moveFromFreeHeadToBusyTail(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Queue); |
| f_EPTF_int2int_HashMap_Insert(v_EPTF_DsRestAPI_Browser_chunk_FromConnId_Hash, vl_ASP_RecvFrom.connId, vl_chunkId); |
| } |
| } |
| |
| repeat; |
| } |
| [] v_DsRestAPI_HTTPServer_IPL4_PCO_PT.receive(ASP_Event:?) -> value vl_ASP_Event { |
| repeat; |
| } |
| [] v_DsRestAPI_HTTPServer_IPL4_PCO_PT.receive { |
| repeat; |
| } |
| [] as_EPTF_DsRestAPI_HTTPServer_bufferHandler() { |
| repeat; |
| } |
| } |
| |
| private altstep as_EPTF_DsRestAPI_HTTPServer_bufferHandler () runs on EPTF_DsRestAPI_HTTPServer_CT { |
| [f_EPTF_FBQ_getLengthOfBusyChain(v_EPTF_DsRestAPI_Browser_Buffer_FBQ) != 0] t_bufferTimer.timeout { |
| var integer vl_nextItem; |
| if (f_EPTF_FBQ_getBusyHeadIdx(vl_nextItem, v_EPTF_DsRestAPI_Browser_Buffer_FBQ)) { |
| var EPTF_DsRestAPI_HTTPServer_BufferItem vl_bufferedItem := v_EPTF_DsRestAPI_Browser_Buffer_Messages[vl_nextItem]; |
| f_EPTF_FBQ_moveFromBusyToFreeHead(vl_nextItem, v_EPTF_DsRestAPI_Browser_Buffer_FBQ); |
| |
| var charstring vl_contentType := "application/json"; |
| var octetstring vl_resultContent := f_EPTF_DsRestAPI_DSServer_processJSONRequest(vl_bufferedItem.request); |
| vl_resultContent := f_EPTF_DsRestAPI_HTTPServer_encodeHTTP_oct(vl_resultContent, vl_contentType, "OK", "200"); |
| var ASP_Send vl_responseMsg := { vl_bufferedItem.connId, {tcp := {}}, vl_resultContent }; |
| v_DsRestAPI_HTTPServer_IPL4_PCO_PT.send(vl_responseMsg); |
| } |
| t_bufferTimer.start; |
| repeat; |
| } |
| } |
| |
| } // ~ group PrivateFunctions |
| |
| } // ~ module EPTF_CLL_DsRestAPI_HTTPServer_Functions |