Added HTTP API to C027_Support library.

Fork of C027_Support by u-blox

Files at this revision

API Documentation at this revision

Comitter:
fdilenarda
Date:
Thu Jan 21 14:59:31 2016 +0000
Parent:
135:2fbd5723e063
Parent:
134:307c15ce18e8
Commit message:
Merge between HTTP API and features for connection manager.

Changed in this revision

MDM.cpp Show annotated file Show diff for this revision Revisions of this file
MDM.h Show annotated file Show diff for this revision Revisions of this file
diff -r 2fbd5723e063 -r 95ae93a46ae5 MDM.cpp
--- a/MDM.cpp	Tue Jan 12 08:37:29 2016 +0000
+++ b/MDM.cpp	Thu Jan 21 14:59:31 2016 +0000
@@ -13,6 +13,10 @@
 #define ISSOCKET(s)     (((s) >= 0) && ((s) < NUMSOCKETS) && (_sockets[s].handle != SOCKET_ERROR))
 //! check for timeout
 #define TIMEOUT(t, ms)  ((ms != TIMEOUT_BLOCKING) && (ms < t.read_ms())) 
+// num HTTP profiles
+#define NUMPROFILES      (sizeof(_httpProfiles)/sizeof(*_httpProfiles))
+//! test if it is an HTTP profile is ok to use
+#define ISPROFILE(p)     (((p) >= 0) && ((p) < NUMPROFILES) && (_httpProfiles[p].handle != HTTP_PROF_ERROR))
 //! registration ok check helper
 #define REG_OK(r)       ((r == REG_HOME) || (r == REG_ROAMING)) 
 //! registration done check helper (no need to poll further)
@@ -103,6 +107,9 @@
     memset(_sockets, 0, sizeof(_sockets));
     for (int socket = 0; socket < NUMSOCKETS; socket ++)
         _sockets[socket].handle = SOCKET_ERROR;
+    memset(_httpProfiles, 0, sizeof(_httpProfiles));
+    for (int profile = 0; profile < NUMPROFILES; profile ++)
+        _httpProfiles[profile].handle = HTTP_PROF_ERROR;
 #ifdef MDM_DEBUG
     _debugLevel = 1;
     _debugTime.start();
@@ -196,8 +203,13 @@
                     _loc.time.tm_yday=0;
                     _loc.validData = true;
                     TRACE("Parsed UULOC position\r\n");
-               }
-               if (_dev.dev == DEV_LISA_C2) {
+                // +UHTTPCR: <profile_id>,<op_code>,<param_val>
+                } else if ((sscanf(cmd, "UUHTTPCR: %d,%d,%d", &a, &b, &c) == 3)) {
+                    _httpProfiles[a].cmd = b;          //command
+                    _httpProfiles[a].result = c;       //result
+                    TRACE("%s for profile %d: result code is %d\r\n", getHTTPcmd(b), a, c);
+                }
+                if (_dev.dev == DEV_LISA_C2) {
                     // CDMA Specific -------------------------------------------
                     // +CREG: <n><SID>,<NID>,<stat>
                     if (sscanf(cmd, "CREG: %*d,%d,%d,%d",&a,&b,&c) == 3) {
@@ -1315,6 +1327,301 @@
 }
 
 // ----------------------------------------------------------------
+// HTTP
+
+int MDMParser::httpFindProfile()
+{
+    int profile = HTTP_PROF_ERROR;  //default value
+    LOCK();
+    // find a free HTTP profile 
+    profile = _findProfile();
+    TRACE("httpFindProfile: profile is %d\r\n", profile);
+    if (profile != HTTP_PROF_ERROR) {
+        _httpProfiles[profile].handle     = 1;
+        _httpProfiles[profile].timeout_ms = TIMEOUT_BLOCKING;
+        _httpProfiles[profile].pending    = false;
+        _httpProfiles[profile].cmd        = -1;
+        _httpProfiles[profile].result     = -1;
+    }
+    UNLOCK();
+    return profile;
+}
+
+int MDMParser::_findProfile(int handle) {
+    for (int profile = 0; profile < NUMPROFILES; profile++) {
+        if (_httpProfiles[profile].handle == handle)
+            return profile;
+    }
+    return HTTP_PROF_ERROR;
+}
+
+bool MDMParser::httpSetBlocking(int profile, int timeout_ms)
+{
+    bool ok = false;
+    LOCK();
+    TRACE("httpSetBlocking(%d,%d)\r\n", profile, timeout_ms);
+    if (ISPROFILE(profile)) {
+        _httpProfiles[profile].timeout_ms = timeout_ms;
+        ok = true;
+    }
+    UNLOCK();
+    return ok;
+}
+
+bool MDMParser::httpSetProfileForCmdMng(int profile)
+{
+    bool ok = false;
+    LOCK();
+    TRACE("httpSetProfileForCmdMng(%d)\r\n", profile);
+    if (ISPROFILE(profile)) {
+        _httpProfiles[profile].pending = true;
+        _httpProfiles[profile].result = -1;
+        ok = true;
+    }
+    UNLOCK();
+    return ok;
+}
+
+bool MDMParser::httpFreeProfile(int profile)
+{
+    bool ok = true;
+    LOCK();
+    if (ISPROFILE(profile)) {
+        TRACE("httpFreeProfile(%d)\r\n", profile);
+        _httpProfiles[profile].handle     = HTTP_PROF_ERROR;
+        _httpProfiles[profile].timeout_ms = TIMEOUT_BLOCKING;
+        _httpProfiles[profile].pending    = false;
+        _httpProfiles[profile].cmd        = -1;
+        _httpProfiles[profile].result     = -1;
+        ok = true;
+    }
+    UNLOCK();
+    return ok;
+}
+
+bool MDMParser::httpResetProfile(int httpProfile)
+{
+    bool ok = false;
+    
+    LOCK();
+    TRACE("httpResetProfile(%d)\r\n", httpProfile);
+    sendFormated("AT+UHTTP=%d\r\n", httpProfile);
+    if (RESP_OK == waitFinalResp())
+        ok = true;
+    UNLOCK();
+    
+    return ok;
+}
+
+bool MDMParser::httpSetPar(int httpProfile, HttpOpCode httpOpCode, const char * httpInPar)
+{
+    bool ok = false;
+    IP ip = NOIP;
+    int httpInParNum = 0;
+    
+    LOCK();
+    TRACE("httpSetPar(%d,%d,\"%s\")\r\n", httpProfile, httpOpCode, httpInPar);
+    switch(httpOpCode){
+        case HTTP_IP_ADDRESS:   //0
+            ip = gethostbyname(httpInPar);
+            if (ip == NOIP)
+                return false;
+            
+            sendFormated("AT+UHTTP=%d,%d,\"" IPSTR "\"\r\n", httpProfile, httpOpCode, IPNUM(ip));
+            if (RESP_OK == waitFinalResp())
+                ok = true;
+            break;
+            
+        case HTTP_SERVER_NAME:  //1
+        case HTTP_USER_NAME:    //2
+        case HTTP_PASSWORD:     //3
+            sendFormated("AT+UHTTP=%d,%d,\"%s\"\r\n", httpProfile, httpOpCode, httpInPar);
+            if (RESP_OK == waitFinalResp())
+                ok = true;
+            break;
+        
+        case HTTP_AUTH_TYPE:    //4    
+        case HTTP_PORT:         //5
+            httpInParNum = atoi(httpInPar);
+            sendFormated("AT+UHTTP=%d,%d,%d\r\n", httpProfile, httpOpCode, httpInParNum); 
+            if (RESP_OK == waitFinalResp())
+                ok = true;
+            break;
+            
+        case HTTP_SECURE:       //6
+            if(_dev.dev != DEV_LISA_C2)
+            {
+                httpInParNum = atoi(httpInPar);
+                sendFormated("AT+UHTTP=%d,%d,%d\r\n", httpProfile, httpOpCode, httpInParNum); 
+                if (RESP_OK == waitFinalResp())
+                    ok = true;
+            } else {
+                TRACE("httpSetPar: HTTP secure option not supported by module\r\n");
+                ok = false;
+            }
+            break;
+            
+        default:
+            TRACE("httpSetPar: unknown httpOpCode %s\r\n", httpOpCode);
+            ok = false; 
+            break;   
+    }
+    UNLOCK();
+    return ok;
+}
+
+bool MDMParser::httpCommand(int httpProfile, HttpCmd httpCmdCode, const char* httpPath, const char* httpOut, \
+                            const char* httpIn, int httpContentType, const char* httpCustomPar, char* buf, int len)
+{   
+    bool ok = false;
+#ifdef MDM_DEBUG
+    memset(buf, '\0', len);
+#endif
+    LOCK();
+    TRACE("%s\r\n", getHTTPcmd(httpCmdCode));
+    switch (httpCmdCode) 
+    {   
+        case HTTP_HEAD:
+            sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\"\r\n", httpProfile, HTTP_HEAD, httpPath, httpOut);
+            break;
+            
+        case HTTP_GET:
+            sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\"\r\n", httpProfile, HTTP_GET, httpPath, httpOut);
+            break;
+            
+        case HTTP_DELETE:
+            sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\"\r\n", httpProfile, HTTP_DELETE, httpPath, httpOut);
+            break;
+            
+        case HTTP_PUT:
+            //in this case the parameter httpIn is a filename
+            sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\"\r\n", httpProfile, HTTP_PUT, httpPath, httpOut, httpIn);
+            break;
+            
+        case HTTP_POST_FILE:
+            //in this case the parameter httpIn is a filename
+            if(_dev.dev != DEV_LISA_C2)
+            {
+                if(httpContentType != 6)
+                    sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
+                                  httpProfile, HTTP_POST_FILE, httpPath, httpOut, httpIn, httpContentType);
+                else
+                    sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d,%d\r\n", \
+                                  httpProfile, HTTP_POST_FILE, httpPath, httpOut, httpIn, httpContentType, httpCustomPar);
+            }
+            else{
+                if((httpContentType != 5) && (httpContentType != 6) && (httpCustomPar == NULL))
+                {
+                    //parameters values consistent with the AT commands specs of LISA-C200
+                    //(in particular httpCustomPar has to be not defined)
+                    sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
+                                  httpProfile, HTTP_POST_FILE, httpPath, httpOut, httpIn, httpContentType);
+                } else {
+                    TRACE("httpCommand: command not supported by module");
+                    return ok;  //error
+                }
+            }
+            break;
+            
+        case HTTP_POST_DATA:
+            //in this case the parameter httpIn is a string containing data
+            if(_dev.dev != DEV_LISA_C2)
+            {
+                if(httpContentType != 6)
+                    sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
+                                  httpProfile, HTTP_POST_DATA, httpPath, httpOut, httpIn, httpContentType);
+                else
+                    sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d,%d\r\n", \
+                                  httpProfile, HTTP_POST_DATA, httpPath, httpOut, httpIn, httpContentType, httpCustomPar);
+            } else {
+                if((httpContentType != 5) && (httpContentType != 6) && (httpCustomPar == NULL))
+                {
+                    //parameters values consistent with the AT commands specs of LISA-C200
+                    //(in particular httpCustomPar has to be not defined)
+                    sendFormated("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d\r\n", \
+                                  httpProfile, HTTP_POST_DATA, httpPath, httpOut, httpIn, httpContentType);
+                } else {
+                    TRACE("httpCommand: command not supported by module");
+                    return ok;  //error
+                }
+            }    
+            break;
+            
+        default:
+            TRACE("HTTP command not recognized\r\n");
+            return ok;  //error
+    }
+    
+    if (RESP_OK == waitFinalResp())
+    {
+        Timer timer;
+        timer.start();
+        httpSetProfileForCmdMng(httpProfile);
+        while (_httpProfiles[httpProfile].pending)  //waiting for unsolicited
+        {     
+            ok = false;  //reset variable  
+            if(_httpProfiles[httpProfile].result != -1)
+            {    
+                //received unsolicited: starting its analysis 
+                _httpProfiles[httpProfile].pending = false;   
+                if(_httpProfiles[httpProfile].result == 1)
+                {
+                    //HTTP command successfully executed
+                    if(_dev.dev != DEV_LISA_C2)
+                    {
+                        TRACE("httpCommand: reading files with a dimension " \
+                              "also greater than MAX_SIZE bytes\r\n");
+                        if(readFileNew(httpOut,buf,len) >=0 )
+                            ok = true;
+                    } else {
+                        TRACE("httpCommand: reading files with a dimension " \
+                              "less than MAX_SIZE bytes, otherwise error\r\n");
+                        if(readFile(httpOut,buf,len) >=0 )
+                            ok = true;
+                    }
+                } else {
+                    //HTTP command not successfully executed
+                    ok = false;
+                }
+            } else if (!TIMEOUT(timer, _httpProfiles[httpProfile].timeout_ms)) {
+                ok = (WAIT == waitFinalResp(NULL,NULL,0)); // wait for URCs
+            } else  {
+                //not received unsolicited and expired timer
+                TRACE("httpCommand: not received unsolicited and expired timer\r\n");
+                ok = false;
+            }
+            if (!ok) {
+                TRACE("%s: ERROR\r\n", getHTTPcmd(httpCmdCode));
+                _httpProfiles[httpProfile].pending = false;  //no more while loops
+            }
+        }
+    }
+    UNLOCK();
+    return ok;
+}
+
+const char* MDMParser::getHTTPcmd(int httpCmdCode) 
+{
+    switch (httpCmdCode) 
+    {
+        case HTTP_HEAD:
+            return "HTTP HEAD command";
+        case HTTP_GET:
+            return "HTTP GET command";
+        case HTTP_DELETE:
+            return "HTTP DELETE command";
+        case HTTP_PUT:
+            return "HTTP PUT command";
+        case HTTP_POST_FILE:
+            return "HTTP POST file command";
+        case HTTP_POST_DATA:
+            return "HTTP POST data command";
+        default:
+            return "HTTP command not recognized";
+   }
+}
+
+// ----------------------------------------------------------------
 
 int MDMParser::_cbCMGL(int type, const char* buf, int len, CMGLparam* param)
 { 
@@ -1482,7 +1789,112 @@
     }
     return WAIT;
 }
+
+//The following function is useful for reading files with a dimension greater than MAX_SIZE bytes
+int MDMParser::readFileNew(const char* filename, char* buf, int len)
+{   
+    int countBytes = -1;  //counter for file reading (default value)
+    
+    if(_dev.dev != DEV_LISA_C2)
+    {
+        //retrieve information about the file, in particular its size
+        int filesize = infoFile(filename);
+        TRACE("readFileNew: filename is %s; filesize is %d\r\n", filename, filesize);
+        
+        if (len < filesize)
+            TRACE("readFileNew: WARNING. Buffer dimension is %d bytes," \
+                  "while file size is %d bytes\r\n", len, filesize);
+        
+        if (filesize > 0)
+        {
+#ifdef MDM_DEBUG
+            memset(buf, '\0', len);
+#endif
+            int offset = 0;              //start reading from 0
+            int blockSize = MAX_SIZE;    //still need space for headers and unsolicited commands
+            int bytesToRead = filesize;  //bytes to read 
+            
+            while (bytesToRead)
+            {    
+                bool ok = false;
+                
+                if (bytesToRead < blockSize)
+                    blockSize = bytesToRead;
+                
+                LOCK();
+                if (blockSize > 0) {
+                            
+                    sendFormated("AT+URDBLOCK=\"%s\",%d,%d\r\n", filename, offset, blockSize);
+                    
+                    if (RESP_OK == waitFinalResp(_cbURDBLOCK, buf)) {
+                        bytesToRead -= blockSize;
+                        offset += blockSize;
+                        buf += blockSize;
+                        ok = true;
+                    } else {
+                        //error condition
+                        countBytes = -1;
+                        ok = false;
+                    }
+                }
+                UNLOCK();
+                
+                if (!ok) {
+                    TRACE("readFileNew: ERROR\r\n");
+                    return countBytes;  //in this case countBytes is -1
+                }
+            }
+            
+            countBytes = offset;  //total read bytes
+            return countBytes;
+        }
+    } else {
+        TRACE("httpCommand: command not supported by module"); 
+    }
+    return countBytes;  //it could be 0 or -1 (possible error)    
+}
+
+int MDMParser::_cbURDBLOCK(int type, const char* buf, int len, char* out)
+{   
+    char fileNameRes[48]; 
+    int sizeRes;
+    
+    if ((type == TYPE_PLUS) && out) {
+        if ((sscanf(buf, "\r\n+URDBLOCK: \"%[^\"]\",%d,", fileNameRes, &sizeRes) == 2) &&
+            (buf[len-sizeRes-2] == '\"') && (buf[len-1] == '\"')) {
+            memcpy(out, &buf[len-1-sizeRes], sizeRes);
+        }
+    }
+    
+    return WAIT;
+}
+
+int MDMParser::infoFile(const char* filename)
+{
+    int infoFile = 0;  //default value
+    
+    LOCK();
+    sendFormated("AT+ULSTFILE=2,\"%s\"\r\n", filename);
+    if (RESP_OK != waitFinalResp(_cbULSTFILE, &infoFile))
+        infoFile = -1;  //error condition    
+    UNLOCK();
+    
+    return infoFile;
+}
+
+int MDMParser::_cbULSTFILE(int type, const char* buf, int len, int* infoFile)
+{ 
+    if (infoFile) {
+        if (type == TYPE_PLUS) {
+            if (sscanf(buf, "\r\n+ULSTFILE: %d\r\n", infoFile) == 1) {
+            }
+        }
+    }
+    return WAIT;
+}
+
 // ----------------------------------------------------------------
+
 int MDMParser::cellLocSrvHttp (const char* token, const char* server_1, const char* server_2, int days/* = 14*/, \
         int period/* = 4*/, int resolution/* = 1*/)
 {
@@ -1728,6 +2140,7 @@
             { "\r\n+USORD: %d,%d,\"%c\"",                   TYPE_PLUS       },
             { "\r\n+USORF: %d,\"" IPSTR "\",%d,%d,\"%c\"",  TYPE_PLUS       },
             { "\r\n+URDFILE: %s,%d,\"%c\"",                 TYPE_PLUS       },
+            { "\r\n+URDBLOCK: %s,%d,\"%c\"",                TYPE_PLUS       },
         };
         static struct { 
               const char* sta;          const char* end;    int type; 
diff -r 2fbd5723e063 -r 95ae93a46ae5 MDM.h
--- a/MDM.h	Tue Jan 12 08:37:29 2016 +0000
+++ b/MDM.h	Thu Jan 21 14:59:31 2016 +0000
@@ -274,7 +274,83 @@
         \return true if successfully, false otherwise
     */    
     bool socketFree(int socket);
-        
+
+    // ----------------------------------------------------------------
+    // HTTP
+    // ----------------------------------------------------------------
+    
+    //! Type of HTTP Operational Codes (reference to HTTP control +UHTTP) 
+    typedef enum { HTTP_IP_ADDRESS, HTTP_SERVER_NAME, HTTP_USER_NAME, HTTP_PASSWORD, \
+                   HTTP_AUTH_TYPE, HTTP_PORT, HTTP_SECURE } HttpOpCode;
+    
+    //! Type of HTTP Commands (reference to HTTP command +UHTTPC)
+    typedef enum { HTTP_HEAD, HTTP_GET, HTTP_DELETE, HTTP_PUT, \
+                   HTTP_POST_FILE, HTTP_POST_DATA } HttpCmd;
+    
+    //! HTTP Profile error return codes
+    #define HTTP_PROF_ERROR -1
+    
+    /** find HTTP profile
+        \return true if successfully, false otherwise
+    */
+    int httpFindProfile();
+    
+    /** get the number of bytes pending for reading for this HTTP profile
+        \param profile the HTTP profile handle
+        \param timeout_ms -1 blocking, else non blocking timeout in ms
+        \return 0 if successful or SOCKET_ERROR on failure 
+    */
+    bool httpSetBlocking(int profile, int timeout_ms);
+    
+    /** set the HTTP profile for commands management
+        \param profile the HTTP profile handle
+        \return true if successfully, false otherwise
+    */
+    bool httpSetProfileForCmdMng(int profile);
+    
+    /** free the HTTP profile
+        \param profile the HTTP profile handle
+        \return true if successfully, false otherwise
+    */
+    bool httpFreeProfile(int profile);
+    
+    /** reset HTTP profile
+        \param httpProfile the HTTP profile to be reset
+        \return true if successfully, false otherwise
+    */
+    bool httpResetProfile(int httpProfile);
+    
+    /** set HTTP parameters
+        \param httpProfile the HTTP profile identifier
+        \param httpOpCode the HTTP operation code
+        \param httpInPar the HTTP input parameter
+        \return true if successfully, false otherwise
+    */
+    bool httpSetPar(int httpProfile, HttpOpCode httpOpCode, const char * httpInPar);
+    
+    /** HTTP commands management
+        \param httpProfile the HTTP profile identifier
+        \param httpCmdCode the HTTP command code
+        \param httpPath the path of HTTP server resource
+        \param httpOut the filename where the HTTP server response will be stored
+        \param httpIn the input data (filename or string) to be sent 
+                      to the HTTP server with the command request
+        \param httpContentType the HTTP Content-Type identifier
+        \param httpCustomPar the parameter for an user defined HTTP Content-Type
+        \param buf the buffer to read into
+        \param len the size of the buffer to read into
+        \return true if successfully, false otherwise
+    */
+    bool httpCommand(int httpProfile, HttpCmd httpCmdCode, const char* httpPath, \
+                     const char* httpOut, const char* httpIn, int httpContentType, \
+                     const char* httpCustomPar, char* buf, int len);
+    
+    /** get HTTP commands
+        \param httpCmdCode the HTTP command code (reference also the enum format)
+        \return HTTP command in string format
+    */
+    const char* getHTTPcmd(int httpCmdCode);
+    
     // ----------------------------------------------------------------
     // SMS Short Message Service
     // ----------------------------------------------------------------
@@ -340,13 +416,29 @@
     */
     int writeFile(const char* filename, const char* buf, int len);
     
-    /** REad a file from the local file system
+    /** Read a file from the local file system
         \param filename the name of the file 
         \param buf a buffer to hold the data 
         \param len the size to read
         \return the number of bytes read
     */
     int readFile(const char* filename, char* buf, int len);
+    
+    /** Read a file from the local file system
+        (the file size is greater than MAX_SIZE bytes)
+        \param filename the name of the file 
+        \param buf a buffer to hold the data 
+        \param len the size to read
+        \return the number of bytes read
+    */
+    int readFileNew(const char* filename, char* buf, int len);
+    
+    /** Retrieve information about the dimension of a file from the local FFS
+        \param filename the name of the file
+        \return the file dimension in number of bytes 
+    */
+    int infoFile(const char* filename);
+    
     // ----------------------------------------------------------------
     // Cell Locate
     // ----------------------------------------------------------------
@@ -626,6 +718,8 @@
     typedef struct { const char* filename; char* buf; int sz; int len; } URDFILEparam;
     static int _cbUDELFILE(int type, const char* buf, int len, void*);
     static int _cbURDFILE(int type, const char* buf, int len, URDFILEparam* param);
+    static int _cbURDBLOCK(int type, const char* buf, int len, char* out);
+    static int _cbULSTFILE(int type, const char* buf, int len, int* infoFile);
     // variables
     DevStatus   _dev; //!< collected device information
     NetStatus   _net; //!< collected network information 
@@ -637,6 +731,16 @@
     // LISA-U and SARA-G have 7 sockets
     SockCtrl _sockets[12];
     int _findSocket(int handle = SOCKET_ERROR/* = CREATE*/);
+    // management structure for HTTP profiles
+    // it's possible to have up to 4 different HTTP profiles (LISA-C200, LISA-U200 and SARA-G350) having:
+    // param handle the current HTTP profile is in handling state or not (default value is HTTP_ERROR)
+    // param timeout_ms the timeout for the current HTTP command
+    // param pending the status for the current HTTP command (in processing state or not)
+    // param cmd the code for the current HTTP command
+    // param result the result for the current HTTP command once processed
+    typedef struct {  int handle; int timeout_ms; bool pending; int cmd; int result; } HttpProfCtrl;
+    HttpProfCtrl _httpProfiles[4];
+    int _findProfile(int handle = HTTP_PROF_ERROR/* = CREATE*/);
     static MDMParser* inst;
     bool _init;
 #ifdef TARGET_UBLOX_C027