Wei Chang Shen / HTTPClient

Fork of HTTPClient by Donatien Garnier

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPClient.cpp Source File

HTTPClient.cpp

00001 /* HTTPClient.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 //Debug is disabled by default
00021 #if 0
00022 //Enable debug
00023 #include <cstdio>
00024 #define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
00025 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
00026 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
00027 
00028 #else
00029 //Disable debug
00030 #define DBG(x, ...)
00031 #define WARN(x, ...)
00032 #define ERR(x, ...)
00033 
00034 #endif
00035 
00036 #define HTTP_PORT 80
00037 
00038 #define OK 0
00039 
00040 #define MIN(x,y) (((x)<(y))?(x):(y))
00041 #define MAX(x,y) (((x)>(y))?(x):(y))
00042 
00043 #define CHUNK_SIZE 256
00044 
00045 #include <cstring>
00046 
00047 #include "HTTPClient.h"
00048 
00049 HTTPClient::HTTPClient() :
00050     m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
00051 {
00052 
00053 }
00054 
00055 HTTPClient::~HTTPClient()
00056 {
00057 
00058 }
00059 
00060 #if 0
00061 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
00062 {
00063     m_basicAuthUser = user;
00064     m_basicAuthPassword = password;
00065 }
00066 #endif
00067 
00068 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00069 {
00070     return connect(url, HTTP_GET, NULL, pDataIn, timeout);
00071 }
00072 
00073 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00074 {
00075     HTTPText str(result, maxResultLen);
00076     return get(url, &str, timeout);
00077 }
00078 
00079 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00080 {
00081     return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00082 }
00083 
00084 HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00085 {
00086     return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00087 }
00088 
00089 HTTPResult HTTPClient::del(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00090 {
00091     return connect(url, HTTP_DELETE, NULL, pDataIn, timeout);
00092 }
00093 
00094 
00095 int HTTPClient::getHTTPResponseCode()
00096 {
00097     return m_httpResponseCode;
00098 }
00099 
00100 #define CHECK_CONN_ERR(ret) \
00101   do{ \
00102     if(ret) { \
00103       m_sock.close(); \
00104       ERR("Connection error (%d)", ret); \
00105       return HTTP_CONN; \
00106     } \
00107   } while(0)
00108 
00109 #define PRTCL_ERR() \
00110   do{ \
00111     m_sock.close(); \
00112     ERR("Protocol error"); \
00113     return HTTP_PRTCL; \
00114   } while(0)
00115 
00116 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
00117 {
00118     m_httpResponseCode = 0; //Invalidate code
00119     m_timeout = timeout;
00120 
00121     pDataIn->writeReset();
00122     if( pDataOut ) {
00123         pDataOut->readReset();
00124     }
00125 
00126     char scheme[8];
00127     uint16_t port;
00128     char host[32];
00129     char path[64];
00130     //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
00131     HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
00132     if(res != HTTP_OK) {
00133         ERR("parseURL returned %d", res);
00134         return res;
00135     }
00136 
00137     if(port == 0) { //TODO do handle HTTPS->443
00138         port = 80;
00139     }
00140 
00141     DBG("Scheme: %s", scheme);
00142     DBG("Host: %s", host);
00143     DBG("Port: %d", port);
00144     DBG("Path: %s", path);
00145 
00146     //Connect
00147     DBG("Connecting socket to server");
00148     int ret = m_sock.connect(host, port);
00149     if (ret < 0) {
00150         m_sock.close();
00151         ERR("Could not connect");
00152         return HTTP_CONN;
00153     }
00154 
00155     //Send request
00156     DBG("Sending request");
00157     char buf[CHUNK_SIZE];
00158     const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
00159     snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
00160     ret = send(buf);
00161     if(ret) {
00162         m_sock.close();
00163         ERR("Could not write request");
00164         return HTTP_CONN;
00165     }
00166 
00167     //Send all headers
00168 
00169     //Send default headers
00170     DBG("Sending headers");
00171     if( pDataOut != NULL ) {
00172         if( pDataOut->getIsChunked() ) {
00173             ret = send("Transfer-Encoding: chunked\r\n");
00174             CHECK_CONN_ERR(ret);
00175         } else {
00176             snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
00177             ret = send(buf);
00178             CHECK_CONN_ERR(ret);
00179         }
00180         char type[48];
00181         if( pDataOut->getDataType(type, 48) == HTTP_OK ) {
00182             snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
00183             ret = send(buf);
00184             CHECK_CONN_ERR(ret);
00185         }
00186 
00187         //Send specific headers
00188         while( pDataOut->getHeader(buf, sizeof(buf) - 3) ) { //must have space left for CRLF + 0 terminating char
00189             size_t headerlen = strlen(buf);
00190             snprintf(buf + headerlen, sizeof(buf) - headerlen, "\r\n");
00191             ret = send(buf);
00192             CHECK_CONN_ERR(ret);
00193         }
00194     }
00195 
00196     //Send specific headers
00197     while( pDataIn->getHeader(buf, sizeof(buf) - 3) ) {
00198         size_t headerlen = strlen(buf);
00199         snprintf(buf + headerlen, sizeof(buf) - headerlen, "\r\n");
00200         ret = send(buf);
00201         CHECK_CONN_ERR(ret);
00202     }
00203 
00204     //Close headers
00205     DBG("Headers sent");
00206     ret = send("\r\n");
00207     CHECK_CONN_ERR(ret);
00208 
00209     size_t trfLen;
00210 
00211     //Send data (if available)
00212     if( pDataOut != NULL ) {
00213         DBG("Sending data");
00214         while(true) {
00215             size_t writtenLen = 0;
00216             pDataOut->read(buf, CHUNK_SIZE, &trfLen);
00217             if( pDataOut->getIsChunked() ) {
00218                 //Write chunk header
00219                 char chunkHeader[16];
00220                 snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
00221                 ret = send(chunkHeader);
00222                 CHECK_CONN_ERR(ret);
00223             } else if( trfLen == 0 ) {
00224                 break;
00225             }
00226             if( trfLen != 0 ) {
00227                 ret = send(buf, trfLen);
00228                 CHECK_CONN_ERR(ret);
00229             }
00230 
00231             if( pDataOut->getIsChunked()  ) {
00232                 ret = send("\r\n"); //Chunk-terminating CRLF
00233                 CHECK_CONN_ERR(ret);
00234             } else {
00235                 writtenLen += trfLen;
00236                 if( writtenLen >= pDataOut->getDataLen() ) {
00237                     break;
00238                 }
00239             }
00240 
00241             if( trfLen == 0 ) {
00242                 break;
00243             }
00244         }
00245     }
00246 
00247     //Receive response
00248     DBG("Receiving response");
00249     ret = recv(buf, 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
00250     CHECK_CONN_ERR(ret);
00251 
00252     buf[trfLen] = '\0';
00253 
00254     //Make sure we got the first response line
00255     char* crlfPtr = NULL;
00256     while( true ) {
00257         crlfPtr = strstr(buf, "\r\n");
00258         if(crlfPtr == NULL) {
00259             if( trfLen < CHUNK_SIZE - 1 ) {
00260                 size_t newTrfLen;
00261                 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00262                 trfLen += newTrfLen;
00263                 buf[trfLen] = '\0';
00264                 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
00265                 CHECK_CONN_ERR(ret);
00266                 continue;
00267             } else {
00268                 PRTCL_ERR();
00269             }
00270         }
00271         break;
00272     }
00273 
00274     int crlfPos = crlfPtr - buf;
00275     buf[crlfPos] = '\0';
00276 
00277     //Parse HTTP response
00278     //if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
00279     if(crlfPos > 13) {
00280         buf[13] = '\0';
00281     }
00282     if( sscanf(buf, "HTTP/%*d.%*d %d", &m_httpResponseCode) != 1 ) { //Kludge for newlib nano
00283         //Cannot match string, error
00284         ERR("Not a correct HTTP answer : %s\n", buf);
00285         PRTCL_ERR();
00286     }
00287 
00288     if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 300) ) {
00289         //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
00290         WARN("Response code %d", m_httpResponseCode);
00291         PRTCL_ERR();
00292     }
00293 
00294     DBG("Reading headers");
00295 
00296     memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00297     trfLen -= (crlfPos + 2);
00298 
00299     size_t recvContentLength = 0;
00300     bool recvChunked = false;
00301     bool recvLengthUnknown = true;
00302     //Now get headers
00303     while( true ) {
00304         crlfPtr = strstr(buf, "\r\n");
00305         if(crlfPtr == NULL) {
00306             if( trfLen < CHUNK_SIZE - 1 ) {
00307                 size_t newTrfLen;
00308                 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00309                 trfLen += newTrfLen;
00310                 buf[trfLen] = '\0';
00311                 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
00312                 CHECK_CONN_ERR(ret);
00313                 continue;
00314             } else {
00315                 PRTCL_ERR();
00316             }
00317         }
00318 
00319         crlfPos = crlfPtr - buf;
00320 
00321         if(crlfPos == 0) { //End of headers
00322             DBG("Headers read");
00323             memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
00324             trfLen -= 2;
00325             break;
00326         }
00327 
00328         buf[crlfPos] = '\0';
00329 
00330         char key[32];
00331         char value[32];
00332 
00333         //key[31] = '\0';
00334         //value[31] = '\0';
00335 
00336         memset(key, 0, 32);
00337         memset(value, 0, 32);
00338 
00339         //int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
00340 
00341         int n = 0;
00342 
00343         char* keyEnd = strchr(buf, ':');
00344         if(keyEnd != NULL) {
00345             *keyEnd = '\0';
00346             if(strlen(buf) < 32) {
00347                 strcpy(key, buf);
00348                 n++;
00349                 char* valueStart = keyEnd + 2;
00350                 if( (valueStart - buf) < crlfPos ) {
00351                     if (strlen(valueStart) > 31) {
00352                         char* ee = valueStart + 31;
00353                         *ee = '\0';
00354                     }
00355                     strcpy(value, valueStart);
00356                     n++;
00357 
00358                 }
00359             }
00360         }
00361         if ( n == 2 ) {
00362             DBG("Read header : %s: %s\n", key, value);
00363             if( !strcmp(key, "Content-Length") ) {
00364                 sscanf(value, "%d", &recvContentLength);
00365                 recvLengthUnknown = false;
00366                 pDataIn->setDataLen(recvContentLength);
00367             } else if( !strcmp(key, "Transfer-Encoding") ) {
00368                 if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") ) {
00369                     recvChunked = true;
00370                     recvLengthUnknown = false;
00371                     pDataIn->setIsChunked(true);
00372                 }
00373             } else if( !strcmp(key, "Content-Type") ) {
00374                 pDataIn->setDataType(value);
00375             }
00376 
00377             memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00378             trfLen -= (crlfPos + 2);
00379 
00380         } else {
00381             ERR("Could not parse header");
00382             PRTCL_ERR();
00383         }
00384 
00385     }
00386 
00387 //Receive data
00388     DBG("Receiving data");
00389     while(true) {
00390         size_t readLen = 0;
00391 
00392         if( recvChunked ) {
00393             //Read chunk header
00394             bool foundCrlf;
00395             do {
00396                 foundCrlf = false;
00397                 crlfPos=0;
00398                 buf[trfLen]=0;
00399                 if(trfLen >= 2) {
00400                     for(; crlfPos < trfLen - 2; crlfPos++) {
00401                         if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' ) {
00402                             foundCrlf = true;
00403                             break;
00404                         }
00405                     }
00406                 }
00407                 if(!foundCrlf) { //Try to read more
00408                     if( trfLen < CHUNK_SIZE ) {
00409                         size_t newTrfLen;
00410                         ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00411                         trfLen += newTrfLen;
00412                         CHECK_CONN_ERR(ret);
00413                         continue;
00414                     } else {
00415                         PRTCL_ERR();
00416                     }
00417                 }
00418             } while(!foundCrlf);
00419             buf[crlfPos] = '\0';
00420             int n = sscanf(buf, "%x", &readLen);
00421             if(n!=1) {
00422                 ERR("Could not read chunk length");
00423                 PRTCL_ERR();
00424             }
00425 
00426             memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
00427             trfLen -= (crlfPos + 2);
00428 
00429             if( readLen == 0 ) {
00430                 //Last chunk
00431                 break;
00432             }
00433         } else {
00434             readLen = recvContentLength;
00435         }
00436 
00437         DBG("Retrieving %d bytes (%d bytes in buffer)", readLen, trfLen);
00438 
00439         do {
00440             if(recvLengthUnknown ) {
00441                 readLen = trfLen;
00442             }
00443             pDataIn->write(buf, MIN(trfLen, readLen));
00444             if(!recvLengthUnknown) {
00445                 if( trfLen > readLen ) {
00446                     memmove(buf, &buf[readLen], trfLen - readLen);
00447                     trfLen -= readLen;
00448                     readLen = 0;
00449                 } else {
00450                     readLen -= trfLen;
00451                 }
00452             } else {
00453                 trfLen = 0;
00454             }
00455 
00456             if(readLen || recvLengthUnknown) {
00457                 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
00458                 if(recvLengthUnknown && (ret == HTTP_CLOSED)) {
00459                     //Write and exit
00460                     pDataIn->write(buf, trfLen);
00461                     break;
00462                 }
00463                 CHECK_CONN_ERR(ret);
00464                 if(recvLengthUnknown && (trfLen == 0)) {
00465                     break;
00466                 }
00467             }
00468         } while(readLen || recvLengthUnknown);
00469 
00470         if( recvChunked ) {
00471             if(trfLen < 2) {
00472                 size_t newTrfLen;
00473                 //Read missing chars to find end of chunk
00474                 ret = recv(buf + trfLen, 2 - trfLen, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00475                 CHECK_CONN_ERR(ret);
00476                 trfLen += newTrfLen;
00477             }
00478             if( (buf[0] != '\r') || (buf[1] != '\n') ) {
00479                 ERR("Format error");
00480                 PRTCL_ERR();
00481             }
00482             memmove(buf, &buf[2], trfLen - 2);
00483             trfLen -= 2;
00484         } else {
00485             break;
00486         }
00487 
00488     }
00489 
00490     m_sock.close();
00491     DBG("Completed HTTP transaction");
00492 
00493     return HTTP_OK;
00494 }
00495 
00496 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
00497 {
00498     DBG("Trying to read between %d and %d bytes", minLen, maxLen);
00499     size_t readLen = 0;
00500 
00501     if(!m_sock.is_connected()) {
00502         WARN("Connection was closed by server");
00503         return HTTP_CLOSED; //Connection was closed by server
00504     }
00505 
00506     int ret;
00507     while(readLen < maxLen) {
00508         if(readLen < minLen) {
00509             DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
00510             m_sock.set_blocking(false, m_timeout);
00511             ret = m_sock.receive_all(buf + readLen, minLen - readLen);
00512         } else {
00513             DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
00514             m_sock.set_blocking(false, 0);
00515             ret = m_sock.receive(buf + readLen, maxLen - readLen);
00516         }
00517 
00518         if( ret > 0) {
00519             readLen += ret;
00520         } else if( ret == 0 ) {
00521             break;
00522         } else {
00523             if(!m_sock.is_connected()) {
00524                 ERR("Connection error (recv returned %d)", ret);
00525                 *pReadLen = readLen;
00526                 return HTTP_CONN;
00527             } else {
00528                 break;
00529             }
00530         }
00531 
00532         if(!m_sock.is_connected()) {
00533             break;
00534         }
00535     }
00536     DBG("Read %d bytes", readLen);
00537     *pReadLen = readLen;
00538     return HTTP_OK;
00539 }
00540 
00541 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
00542 {
00543     if(len == 0) {
00544         len = strlen(buf);
00545     }
00546     DBG("Trying to write %d bytes", len);
00547     size_t writtenLen = 0;
00548 
00549     if(!m_sock.is_connected()) {
00550         WARN("Connection was closed by server");
00551         return HTTP_CLOSED; //Connection was closed by server
00552     }
00553 
00554     m_sock.set_blocking(false, m_timeout);
00555     int ret = m_sock.send_all(buf, len);
00556     if(ret > 0) {
00557         writtenLen += ret;
00558     } else if( ret == 0 ) {
00559         WARN("Connection was closed by server");
00560         return HTTP_CLOSED; //Connection was closed by server
00561     } else {
00562         ERR("Connection error (send returned %d)", ret);
00563         return HTTP_CONN;
00564     }
00565 
00566     DBG("Written %d bytes", writtenLen);
00567     return HTTP_OK;
00568 }
00569 
00570 HTTPResult HTTPClient::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL
00571 {
00572     char* schemePtr = (char*) url;
00573     char* hostPtr = (char*) strstr(url, "://");
00574     if(hostPtr == NULL) {
00575         WARN("Could not find host");
00576         return HTTP_PARSE; //URL is invalid
00577     }
00578 
00579     if( maxSchemeLen < hostPtr - schemePtr + 1 ) { //including NULL-terminating char
00580         WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
00581         return HTTP_PARSE;
00582     }
00583     memcpy(scheme, schemePtr, hostPtr - schemePtr);
00584     scheme[hostPtr - schemePtr] = '\0';
00585 
00586     hostPtr+=3;
00587 
00588     size_t hostLen = 0;
00589 
00590     char* portPtr = strchr(hostPtr, ':');
00591     if( portPtr != NULL ) {
00592         hostLen = portPtr - hostPtr;
00593         portPtr++;
00594         if( sscanf(portPtr, "%hu", port) != 1) {
00595             WARN("Could not find port");
00596             return HTTP_PARSE;
00597         }
00598     } else {
00599         *port=0;
00600     }
00601     char* pathPtr = strchr(hostPtr, '/');
00602     if( hostLen == 0 ) {
00603         hostLen = pathPtr - hostPtr;
00604     }
00605 
00606     if( maxHostLen < hostLen + 1 ) { //including NULL-terminating char
00607         WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
00608         return HTTP_PARSE;
00609     }
00610     memcpy(host, hostPtr, hostLen);
00611     host[hostLen] = '\0';
00612 
00613     size_t pathLen;
00614     char* fragmentPtr = strchr(hostPtr, '#');
00615     if(fragmentPtr != NULL) {
00616         pathLen = fragmentPtr - pathPtr;
00617     } else {
00618         pathLen = strlen(pathPtr);
00619     }
00620 
00621     if( maxPathLen < pathLen + 1 ) { //including NULL-terminating char
00622         WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
00623         return HTTP_PARSE;
00624     }
00625     memcpy(path, pathPtr, pathLen);
00626     path[pathLen] = '\0';
00627 
00628     return HTTP_OK;
00629 }