Squashed 'tmk_core/' changes from 7967731..b9e0ea0
b9e0ea0 Merge commit '7fa9d8bdea3773d1195b04d98fcf27cf48ddd81d' as 'tool/mbed/mbed-sdk' 7fa9d8b Squashed 'tool/mbed/mbed-sdk/' content from commit 7c21ce5 git-subtree-dir: tmk_core git-subtree-split: b9e0ea08cb940de20b3610ecdda18e9d8cd7c552
This commit is contained in:
parent
a20ef7052c
commit
1fe4406f37
4198 changed files with 2016457 additions and 0 deletions
|
@ -0,0 +1,649 @@
|
|||
/* HTTPClient.cpp */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//Debug is disabled by default
|
||||
#if 0
|
||||
//Enable debug
|
||||
#include <cstdio>
|
||||
#define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
|
||||
#define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
|
||||
#define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
|
||||
|
||||
#else
|
||||
//Disable debug
|
||||
#define DBG(x, ...)
|
||||
#define WARN(x, ...)
|
||||
#define ERR(x, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#define HTTP_PORT 80
|
||||
|
||||
#define OK 0
|
||||
|
||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||
#define MAX(x,y) (((x)>(y))?(x):(y))
|
||||
|
||||
#define CHUNK_SIZE 256
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "HTTPClient.h"
|
||||
|
||||
HTTPClient::HTTPClient() :
|
||||
m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HTTPClient::~HTTPClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
|
||||
{
|
||||
m_basicAuthUser = user;
|
||||
m_basicAuthPassword = password;
|
||||
}
|
||||
#endif
|
||||
|
||||
HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
return connect(url, HTTP_GET, NULL, pDataIn, timeout);
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
HTTPText str(result, maxResultLen);
|
||||
return get(url, &str, timeout);
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::del(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
return connect(url, HTTP_DELETE, NULL, pDataIn, timeout);
|
||||
}
|
||||
|
||||
|
||||
int HTTPClient::getHTTPResponseCode()
|
||||
{
|
||||
return m_httpResponseCode;
|
||||
}
|
||||
|
||||
#define CHECK_CONN_ERR(ret) \
|
||||
do{ \
|
||||
if(ret) { \
|
||||
m_sock.close(); \
|
||||
ERR("Connection error (%d)", ret); \
|
||||
return HTTP_CONN; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define PRTCL_ERR() \
|
||||
do{ \
|
||||
m_sock.close(); \
|
||||
ERR("Protocol error"); \
|
||||
return HTTP_PRTCL; \
|
||||
} while(0)
|
||||
|
||||
HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
|
||||
{
|
||||
m_httpResponseCode = 0; //Invalidate code
|
||||
m_timeout = timeout;
|
||||
|
||||
pDataIn->writeReset();
|
||||
if( pDataOut )
|
||||
{
|
||||
pDataOut->readReset();
|
||||
}
|
||||
|
||||
char scheme[8];
|
||||
uint16_t port;
|
||||
char host[32];
|
||||
char path[64];
|
||||
//First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
|
||||
HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
|
||||
if(res != HTTP_OK)
|
||||
{
|
||||
ERR("parseURL returned %d", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
if(port == 0) //TODO do handle HTTPS->443
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
|
||||
DBG("Scheme: %s", scheme);
|
||||
DBG("Host: %s", host);
|
||||
DBG("Port: %d", port);
|
||||
DBG("Path: %s", path);
|
||||
|
||||
//Connect
|
||||
DBG("Connecting socket to server");
|
||||
int ret = m_sock.connect(host, port);
|
||||
if (ret < 0)
|
||||
{
|
||||
m_sock.close();
|
||||
ERR("Could not connect");
|
||||
return HTTP_CONN;
|
||||
}
|
||||
|
||||
//Send request
|
||||
DBG("Sending request");
|
||||
char buf[CHUNK_SIZE];
|
||||
const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
|
||||
snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
|
||||
ret = send(buf);
|
||||
if(ret)
|
||||
{
|
||||
m_sock.close();
|
||||
ERR("Could not write request");
|
||||
return HTTP_CONN;
|
||||
}
|
||||
|
||||
//Send all headers
|
||||
|
||||
//Send default headers
|
||||
DBG("Sending headers");
|
||||
if( pDataOut != NULL )
|
||||
{
|
||||
if( pDataOut->getIsChunked() )
|
||||
{
|
||||
ret = send("Transfer-Encoding: chunked\r\n");
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
|
||||
ret = send(buf);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
char type[48];
|
||||
if( pDataOut->getDataType(type, 48) == HTTP_OK )
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
|
||||
ret = send(buf);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
}
|
||||
|
||||
//Close headers
|
||||
DBG("Headers sent");
|
||||
ret = send("\r\n");
|
||||
CHECK_CONN_ERR(ret);
|
||||
|
||||
size_t trfLen;
|
||||
|
||||
//Send data (if available)
|
||||
if( pDataOut != NULL )
|
||||
{
|
||||
DBG("Sending data");
|
||||
while(true)
|
||||
{
|
||||
size_t writtenLen = 0;
|
||||
pDataOut->read(buf, CHUNK_SIZE, &trfLen);
|
||||
if( pDataOut->getIsChunked() )
|
||||
{
|
||||
//Write chunk header
|
||||
char chunkHeader[16];
|
||||
snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
|
||||
ret = send(chunkHeader);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
else if( trfLen == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if( trfLen != 0 )
|
||||
{
|
||||
ret = send(buf, trfLen);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
|
||||
if( pDataOut->getIsChunked() )
|
||||
{
|
||||
ret = send("\r\n"); //Chunk-terminating CRLF
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
writtenLen += trfLen;
|
||||
if( writtenLen >= pDataOut->getDataLen() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( trfLen == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Receive response
|
||||
DBG("Receiving response");
|
||||
ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
|
||||
CHECK_CONN_ERR(ret);
|
||||
|
||||
buf[trfLen] = '\0';
|
||||
|
||||
char* crlfPtr = strstr(buf, "\r\n");
|
||||
if(crlfPtr == NULL)
|
||||
{
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
int crlfPos = crlfPtr - buf;
|
||||
buf[crlfPos] = '\0';
|
||||
|
||||
//Parse HTTP response
|
||||
if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
|
||||
{
|
||||
//Cannot match string, error
|
||||
ERR("Not a correct HTTP answer : %s\n", buf);
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 300) )
|
||||
{
|
||||
//Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
|
||||
WARN("Response code %d", m_httpResponseCode);
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
DBG("Reading headers");
|
||||
|
||||
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
|
||||
trfLen -= (crlfPos + 2);
|
||||
|
||||
size_t recvContentLength = 0;
|
||||
bool recvChunked = false;
|
||||
//Now get headers
|
||||
while( true )
|
||||
{
|
||||
crlfPtr = strstr(buf, "\r\n");
|
||||
if(crlfPtr == NULL)
|
||||
{
|
||||
if( trfLen < CHUNK_SIZE - 1 )
|
||||
{
|
||||
size_t newTrfLen;
|
||||
ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
|
||||
trfLen += newTrfLen;
|
||||
buf[trfLen] = '\0';
|
||||
DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
|
||||
CHECK_CONN_ERR(ret);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRTCL_ERR();
|
||||
}
|
||||
}
|
||||
|
||||
crlfPos = crlfPtr - buf;
|
||||
|
||||
if(crlfPos == 0) //End of headers
|
||||
{
|
||||
DBG("Headers read");
|
||||
memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
|
||||
trfLen -= 2;
|
||||
break;
|
||||
}
|
||||
|
||||
buf[crlfPos] = '\0';
|
||||
|
||||
char key[32];
|
||||
char value[32];
|
||||
|
||||
key[31] = '\0';
|
||||
value[31] = '\0';
|
||||
|
||||
int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
|
||||
if ( n == 2 )
|
||||
{
|
||||
DBG("Read header : %s: %s\n", key, value);
|
||||
if( !strcmp(key, "Content-Length") )
|
||||
{
|
||||
sscanf(value, "%d", &recvContentLength);
|
||||
pDataIn->setDataLen(recvContentLength);
|
||||
}
|
||||
else if( !strcmp(key, "Transfer-Encoding") )
|
||||
{
|
||||
if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
|
||||
{
|
||||
recvChunked = true;
|
||||
pDataIn->setIsChunked(true);
|
||||
}
|
||||
}
|
||||
else if( !strcmp(key, "Content-Type") )
|
||||
{
|
||||
pDataIn->setDataType(value);
|
||||
}
|
||||
|
||||
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
|
||||
trfLen -= (crlfPos + 2);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Could not parse header");
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Receive data
|
||||
DBG("Receiving data");
|
||||
while(true)
|
||||
{
|
||||
size_t readLen = 0;
|
||||
|
||||
if( recvChunked )
|
||||
{
|
||||
//Read chunk header
|
||||
bool foundCrlf;
|
||||
do
|
||||
{
|
||||
foundCrlf = false;
|
||||
crlfPos=0;
|
||||
buf[trfLen]=0;
|
||||
if(trfLen >= 2)
|
||||
{
|
||||
for(; crlfPos < trfLen - 2; crlfPos++)
|
||||
{
|
||||
if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
|
||||
{
|
||||
foundCrlf = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!foundCrlf) //Try to read more
|
||||
{
|
||||
if( trfLen < CHUNK_SIZE )
|
||||
{
|
||||
size_t newTrfLen;
|
||||
ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
|
||||
trfLen += newTrfLen;
|
||||
CHECK_CONN_ERR(ret);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRTCL_ERR();
|
||||
}
|
||||
}
|
||||
} while(!foundCrlf);
|
||||
buf[crlfPos] = '\0';
|
||||
int n = sscanf(buf, "%x", &readLen);
|
||||
if(n!=1)
|
||||
{
|
||||
ERR("Could not read chunk length");
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
|
||||
trfLen -= (crlfPos + 2);
|
||||
|
||||
if( readLen == 0 )
|
||||
{
|
||||
//Last chunk
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
readLen = recvContentLength;
|
||||
}
|
||||
|
||||
DBG("Retrieving %d bytes", readLen);
|
||||
|
||||
do
|
||||
{
|
||||
pDataIn->write(buf, MIN(trfLen, readLen));
|
||||
if( trfLen > readLen )
|
||||
{
|
||||
memmove(buf, &buf[readLen], trfLen - readLen);
|
||||
trfLen -= readLen;
|
||||
readLen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
readLen -= trfLen;
|
||||
}
|
||||
|
||||
if(readLen)
|
||||
{
|
||||
ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
} while(readLen);
|
||||
|
||||
if( recvChunked )
|
||||
{
|
||||
if(trfLen < 2)
|
||||
{
|
||||
size_t newTrfLen;
|
||||
//Read missing chars to find end of chunk
|
||||
ret = recv(buf + trfLen, 2 - trfLen, CHUNK_SIZE - trfLen - 1, &newTrfLen);
|
||||
CHECK_CONN_ERR(ret);
|
||||
trfLen += newTrfLen;
|
||||
}
|
||||
if( (buf[0] != '\r') || (buf[1] != '\n') )
|
||||
{
|
||||
ERR("Format error");
|
||||
PRTCL_ERR();
|
||||
}
|
||||
memmove(buf, &buf[2], trfLen - 2);
|
||||
trfLen -= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_sock.close();
|
||||
DBG("Completed HTTP transaction");
|
||||
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
|
||||
{
|
||||
DBG("Trying to read between %d and %d bytes", minLen, maxLen);
|
||||
size_t readLen = 0;
|
||||
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
WARN("Connection was closed by server");
|
||||
return HTTP_CLOSED; //Connection was closed by server
|
||||
}
|
||||
|
||||
int ret;
|
||||
while(readLen < maxLen)
|
||||
{
|
||||
if(readLen < minLen)
|
||||
{
|
||||
DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
|
||||
m_sock.set_blocking(false, m_timeout);
|
||||
ret = m_sock.receive_all(buf + readLen, minLen - readLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
|
||||
m_sock.set_blocking(false, 0);
|
||||
ret = m_sock.receive(buf + readLen, maxLen - readLen);
|
||||
}
|
||||
|
||||
if( ret > 0)
|
||||
{
|
||||
readLen += ret;
|
||||
}
|
||||
else if( ret == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
ERR("Connection error (recv returned %d)", ret);
|
||||
*pReadLen = readLen;
|
||||
return HTTP_CONN;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
DBG("Read %d bytes", readLen);
|
||||
*pReadLen = readLen;
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
|
||||
{
|
||||
if(len == 0)
|
||||
{
|
||||
len = strlen(buf);
|
||||
}
|
||||
DBG("Trying to write %d bytes", len);
|
||||
size_t writtenLen = 0;
|
||||
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
WARN("Connection was closed by server");
|
||||
return HTTP_CLOSED; //Connection was closed by server
|
||||
}
|
||||
|
||||
m_sock.set_blocking(false, m_timeout);
|
||||
int ret = m_sock.send_all(buf, len);
|
||||
if(ret > 0)
|
||||
{
|
||||
writtenLen += ret;
|
||||
}
|
||||
else if( ret == 0 )
|
||||
{
|
||||
WARN("Connection was closed by server");
|
||||
return HTTP_CLOSED; //Connection was closed by server
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Connection error (send returned %d)", ret);
|
||||
return HTTP_CONN;
|
||||
}
|
||||
|
||||
DBG("Written %d bytes", writtenLen);
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
char* schemePtr = (char*) url;
|
||||
char* hostPtr = (char*) strstr(url, "://");
|
||||
if(hostPtr == NULL)
|
||||
{
|
||||
WARN("Could not find host");
|
||||
return HTTP_PARSE; //URL is invalid
|
||||
}
|
||||
|
||||
if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
|
||||
{
|
||||
WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
memcpy(scheme, schemePtr, hostPtr - schemePtr);
|
||||
scheme[hostPtr - schemePtr] = '\0';
|
||||
|
||||
hostPtr+=3;
|
||||
|
||||
size_t hostLen = 0;
|
||||
|
||||
char* portPtr = strchr(hostPtr, ':');
|
||||
if( portPtr != NULL )
|
||||
{
|
||||
hostLen = portPtr - hostPtr;
|
||||
portPtr++;
|
||||
if( sscanf(portPtr, "%hu", port) != 1)
|
||||
{
|
||||
WARN("Could not find port");
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*port=0;
|
||||
}
|
||||
char* pathPtr = strchr(hostPtr, '/');
|
||||
if( hostLen == 0 )
|
||||
{
|
||||
hostLen = pathPtr - hostPtr;
|
||||
}
|
||||
|
||||
if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
|
||||
{
|
||||
WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
memcpy(host, hostPtr, hostLen);
|
||||
host[hostLen] = '\0';
|
||||
|
||||
size_t pathLen;
|
||||
char* fragmentPtr = strchr(hostPtr, '#');
|
||||
if(fragmentPtr != NULL)
|
||||
{
|
||||
pathLen = fragmentPtr - pathPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathLen = strlen(pathPtr);
|
||||
}
|
||||
|
||||
if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
|
||||
{
|
||||
WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
memcpy(path, pathPtr, pathLen);
|
||||
path[pathLen] = '\0';
|
||||
|
||||
return HTTP_OK;
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/* HTTPClient.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
HTTP Client header file
|
||||
*/
|
||||
|
||||
#ifndef HTTP_CLIENT_H
|
||||
#define HTTP_CLIENT_H
|
||||
|
||||
#include "TCPSocketConnection.h"
|
||||
|
||||
#define HTTP_CLIENT_DEFAULT_TIMEOUT 15000
|
||||
|
||||
class HTTPData;
|
||||
|
||||
#include "IHTTPData.h"
|
||||
#include "mbed.h"
|
||||
|
||||
///HTTP client results
|
||||
enum HTTPResult
|
||||
{
|
||||
HTTP_PROCESSING, ///<Processing
|
||||
HTTP_PARSE, ///<url Parse error
|
||||
HTTP_DNS, ///<Could not resolve name
|
||||
HTTP_PRTCL, ///<Protocol error
|
||||
HTTP_NOTFOUND, ///<HTTP 404 Error
|
||||
HTTP_REFUSED, ///<HTTP 403 Error
|
||||
HTTP_ERROR, ///<HTTP xxx error
|
||||
HTTP_TIMEOUT, ///<Connection timeout
|
||||
HTTP_CONN, ///<Connection error
|
||||
HTTP_CLOSED, ///<Connection was closed by remote host
|
||||
HTTP_OK = 0, ///<Success
|
||||
};
|
||||
|
||||
/**A simple HTTP Client
|
||||
The HTTPClient is composed of:
|
||||
- The actual client (HTTPClient)
|
||||
- Classes that act as a data repository, each of which deriving from the HTTPData class (HTTPText for short text content, HTTPFile for file I/O, HTTPMap for key/value pairs, and HTTPStream for streaming purposes)
|
||||
*/
|
||||
class HTTPClient
|
||||
{
|
||||
public:
|
||||
///Instantiate the HTTP client
|
||||
HTTPClient();
|
||||
~HTTPClient();
|
||||
|
||||
#if 0 //TODO add header handlers
|
||||
/**
|
||||
Provides a basic authentification feature (Base64 encoded username and password)
|
||||
Pass two NULL pointers to switch back to no authentication
|
||||
@param user username to use for authentication, must remain valid durlng the whole HTTP session
|
||||
@param user password to use for authentication, must remain valid durlng the whole HTTP session
|
||||
*/
|
||||
void basicAuth(const char* user, const char* password); //Basic Authentification
|
||||
#endif
|
||||
|
||||
//High Level setup functions
|
||||
/** Execute a GET request on the URL
|
||||
Blocks until completion
|
||||
@param url : url on which to execute the request
|
||||
@param pDataIn : pointer to an IHTTPDataIn instance that will collect the data returned by the request, can be NULL
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult get(const char* url, IHTTPDataIn* pDataIn, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Execute a GET request on the URL
|
||||
Blocks until completion
|
||||
This is a helper to directly get a piece of text from a HTTP result
|
||||
@param url : url on which to execute the request
|
||||
@param result : pointer to a char array in which the result will be stored
|
||||
@param maxResultLen : length of the char array (including space for the NULL-terminating char)
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult get(const char* url, char* result, size_t maxResultLen, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Execute a POST request on the URL
|
||||
Blocks until completion
|
||||
@param url : url on which to execute the request
|
||||
@param dataOut : a IHTTPDataOut instance that contains the data that will be posted
|
||||
@param pDataIn : pointer to an IHTTPDataIn instance that will collect the data returned by the request, can be NULL
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Execute a PUT request on the URL
|
||||
Blocks until completion
|
||||
@param url : url on which to execute the request
|
||||
@param dataOut : a IHTTPDataOut instance that contains the data that will be put
|
||||
@param pDataIn : pointer to an IHTTPDataIn instance that will collect the data returned by the request, can be NULL
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Execute a DELETE request on the URL
|
||||
Blocks until completion
|
||||
@param url : url on which to execute the request
|
||||
@param pDataIn : pointer to an IHTTPDataIn instance that will collect the data returned by the request, can be NULL
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult del(const char* url, IHTTPDataIn* pDataIn, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Get last request's HTTP response code
|
||||
@return The HTTP response code of the last request
|
||||
*/
|
||||
int getHTTPResponseCode();
|
||||
|
||||
private:
|
||||
enum HTTP_METH
|
||||
{
|
||||
HTTP_GET,
|
||||
HTTP_POST,
|
||||
HTTP_PUT,
|
||||
HTTP_DELETE,
|
||||
HTTP_HEAD
|
||||
};
|
||||
|
||||
HTTPResult connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout); //Execute request
|
||||
HTTPResult recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen); //0 on success, err code on failure
|
||||
HTTPResult send(char* buf, size_t len = 0); //0 on success, err code on failure
|
||||
HTTPResult parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen); //Parse URL
|
||||
|
||||
//Parameters
|
||||
TCPSocketConnection m_sock;
|
||||
|
||||
int m_timeout;
|
||||
|
||||
const char* m_basicAuthUser;
|
||||
const char* m_basicAuthPassword;
|
||||
int m_httpResponseCode;
|
||||
|
||||
};
|
||||
|
||||
//Including data containers here for more convenience
|
||||
#include "data/HTTPText.h"
|
||||
#include "data/HTTPMap.h"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
/* IHTTPData.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef IHTTPDATA_H
|
||||
#define IHTTPDATA_H
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using std::size_t;
|
||||
|
||||
///This is a simple interface for HTTP data storage (impl examples are Key/Value Pairs, File, etc...)
|
||||
class IHTTPDataOut
|
||||
{
|
||||
protected:
|
||||
friend class HTTPClient;
|
||||
|
||||
/** Reset stream to its beginning
|
||||
* Called by the HTTPClient on each new request
|
||||
*/
|
||||
virtual void readReset() = 0;
|
||||
|
||||
/** Read a piece of data to be transmitted
|
||||
* @param buf Pointer to the buffer on which to copy the data
|
||||
* @param len Length of the buffer
|
||||
* @param pReadLen Pointer to the variable on which the actual copied data length will be stored
|
||||
*/
|
||||
virtual int read(char* buf, size_t len, size_t* pReadLen) = 0;
|
||||
|
||||
/** Get MIME type
|
||||
* @param type Internet media type from Content-Type header
|
||||
*/
|
||||
virtual int getDataType(char* type, size_t maxTypeLen) = 0; //Internet media type for Content-Type header
|
||||
|
||||
/** Determine whether the HTTP client should chunk the data
|
||||
* Used for Transfer-Encoding header
|
||||
*/
|
||||
virtual bool getIsChunked() = 0;
|
||||
|
||||
/** If the data is not chunked, get its size
|
||||
* Used for Content-Length header
|
||||
*/
|
||||
virtual size_t getDataLen() = 0;
|
||||
|
||||
};
|
||||
|
||||
///This is a simple interface for HTTP data storage (impl examples are Key/Value Pairs, File, etc...)
|
||||
class IHTTPDataIn
|
||||
{
|
||||
protected:
|
||||
friend class HTTPClient;
|
||||
|
||||
/** Reset stream to its beginning
|
||||
* Called by the HTTPClient on each new request
|
||||
*/
|
||||
virtual void writeReset() = 0;
|
||||
|
||||
/** Write a piece of data transmitted by the server
|
||||
* @param buf Pointer to the buffer from which to copy the data
|
||||
* @param len Length of the buffer
|
||||
*/
|
||||
virtual int write(const char* buf, size_t len) = 0;
|
||||
|
||||
/** Set MIME type
|
||||
* @param type Internet media type from Content-Type header
|
||||
*/
|
||||
virtual void setDataType(const char* type) = 0;
|
||||
|
||||
/** Determine whether the data is chunked
|
||||
* Recovered from Transfer-Encoding header
|
||||
*/
|
||||
virtual void setIsChunked(bool chunked) = 0;
|
||||
|
||||
/** If the data is not chunked, set its size
|
||||
* From Content-Length header
|
||||
*/
|
||||
virtual void setDataLen(size_t len) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,200 @@
|
|||
/* HTTPMap.cpp */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HTTPMap.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#define OK 0
|
||||
|
||||
using std::strncpy;
|
||||
|
||||
HTTPMap::HTTPMap() : m_pos(0), m_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HTTPMap::put(const char* key, const char* value)
|
||||
{
|
||||
if(m_count >= HTTPMAP_TABLE_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_keys[m_count] = key;
|
||||
m_values[m_count] = value;
|
||||
m_count++;
|
||||
}
|
||||
|
||||
void HTTPMap::clear()
|
||||
{
|
||||
m_count = 0;
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
/*virtual*/ void HTTPMap::readReset()
|
||||
{
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
/*virtual*/ int HTTPMap::read(char* buf, size_t len, size_t* pReadLen)
|
||||
{
|
||||
if(m_pos >= m_count)
|
||||
{
|
||||
*pReadLen = 0;
|
||||
m_pos = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
//URL encode
|
||||
char* out = buf;
|
||||
const char* in = m_keys[m_pos];
|
||||
if( (m_pos != 0) && (out - buf < len - 1) )
|
||||
{
|
||||
*out='&';
|
||||
out++;
|
||||
}
|
||||
|
||||
while( (*in != '\0') && (out - buf < len - 3) )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
*out = *in;
|
||||
out++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
*out='+';
|
||||
out++;
|
||||
}
|
||||
else
|
||||
{
|
||||
char hex[] = "0123456789abcdef";
|
||||
*out='%';
|
||||
out++;
|
||||
*out=hex[(*in>>4)&0xf];
|
||||
out++;
|
||||
*out=hex[(*in)&0xf];
|
||||
out++;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
|
||||
if( out - buf < len - 1 )
|
||||
{
|
||||
*out='=';
|
||||
out++;
|
||||
}
|
||||
|
||||
in = m_values[m_pos];
|
||||
while( (*in != '\0') && (out - buf < len - 3) )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
*out = *in;
|
||||
out++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
*out='+';
|
||||
out++;
|
||||
}
|
||||
else
|
||||
{
|
||||
char hex[] = "0123456789abcdef";
|
||||
*out='%';
|
||||
out++;
|
||||
*out=hex[(*in>>4)&0xf];
|
||||
out++;
|
||||
*out=hex[(*in)&0xf];
|
||||
out++;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
|
||||
*pReadLen = out - buf;
|
||||
|
||||
m_pos++;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ int HTTPMap::getDataType(char* type, size_t maxTypeLen) //Internet media type for Content-Type header
|
||||
{
|
||||
strncpy(type, "application/x-www-form-urlencoded", maxTypeLen-1);
|
||||
type[maxTypeLen-1] = '\0';
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ bool HTTPMap::getIsChunked() //For Transfer-Encoding header
|
||||
{
|
||||
return false; ////Data is computed one key/value pair at a time
|
||||
}
|
||||
|
||||
/*virtual*/ size_t HTTPMap::getDataLen() //For Content-Length header
|
||||
{
|
||||
size_t count = 0;
|
||||
for(size_t i = 0; i< m_count; i++)
|
||||
{
|
||||
//URL encode
|
||||
const char* in = m_keys[i];
|
||||
if( i != 0 )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
while( (*in != '\0') )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
count+=3;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
|
||||
count ++;
|
||||
|
||||
in = m_values[i];
|
||||
while( (*in != '\0') )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
count+=3;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* HTTPMap.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HTTPMAP_H_
|
||||
#define HTTPMAP_H_
|
||||
|
||||
#include "../IHTTPData.h"
|
||||
|
||||
#define HTTPMAP_TABLE_SIZE 32
|
||||
|
||||
/** Map of key/value pairs
|
||||
* Used to transmit POST data using the application/x-www-form-urlencoded encoding
|
||||
*/
|
||||
class HTTPMap: public IHTTPDataOut
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Instantiates HTTPMap
|
||||
It supports at most 32 key/values pairs
|
||||
*/
|
||||
HTTPMap();
|
||||
|
||||
/** Put Key/Value pair
|
||||
The references to the parameters must remain valid as long as the clear() function is not called
|
||||
@param key The key to use
|
||||
@param value The corresponding value
|
||||
*/
|
||||
void put(const char* key, const char* value);
|
||||
|
||||
/** Clear table
|
||||
*/
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
//IHTTPDataIn
|
||||
virtual void readReset();
|
||||
|
||||
virtual int read(char* buf, size_t len, size_t* pReadLen);
|
||||
|
||||
virtual int getDataType(char* type, size_t maxTypeLen); //Internet media type for Content-Type header
|
||||
|
||||
virtual bool getIsChunked(); //For Transfer-Encoding header
|
||||
|
||||
virtual size_t getDataLen(); //For Content-Length header
|
||||
|
||||
private:
|
||||
const char* m_keys[HTTPMAP_TABLE_SIZE];
|
||||
const char* m_values[HTTPMAP_TABLE_SIZE];
|
||||
|
||||
size_t m_pos;
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
#endif /* HTTPMAP_H_ */
|
|
@ -0,0 +1,104 @@
|
|||
/* HTTPText.cpp */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HTTPText.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#define OK 0
|
||||
|
||||
using std::memcpy;
|
||||
using std::strncpy;
|
||||
using std::strlen;
|
||||
|
||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||
|
||||
HTTPText::HTTPText(char* str) : m_str(str), m_pos(0)
|
||||
{
|
||||
m_size = strlen(str) + 1;
|
||||
}
|
||||
|
||||
HTTPText::HTTPText(char* str, size_t size) : m_str(str), m_size(size), m_pos(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//IHTTPDataIn
|
||||
/*virtual*/ void HTTPText::readReset()
|
||||
{
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
/*virtual*/ int HTTPText::read(char* buf, size_t len, size_t* pReadLen)
|
||||
{
|
||||
*pReadLen = MIN(len, m_size - 1 - m_pos);
|
||||
memcpy(buf, m_str + m_pos, *pReadLen);
|
||||
m_pos += *pReadLen;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ int HTTPText::getDataType(char* type, size_t maxTypeLen) //Internet media type for Content-Type header
|
||||
{
|
||||
strncpy(type, "text/plain", maxTypeLen-1);
|
||||
type[maxTypeLen-1] = '\0';
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ bool HTTPText::getIsChunked() //For Transfer-Encoding header
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*virtual*/ size_t HTTPText::getDataLen() //For Content-Length header
|
||||
{
|
||||
return m_size - 1;
|
||||
}
|
||||
|
||||
//IHTTPDataOut
|
||||
/*virtual*/ void HTTPText::writeReset()
|
||||
{
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
/*virtual*/ int HTTPText::write(const char* buf, size_t len)
|
||||
{
|
||||
size_t writeLen = MIN(len, m_size - 1 - m_pos);
|
||||
memcpy(m_str + m_pos, buf, writeLen);
|
||||
m_pos += writeLen;
|
||||
m_str[m_pos] = '\0';
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ void HTTPText::setDataType(const char* type) //Internet media type from Content-Type header
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*virtual*/ void HTTPText::setIsChunked(bool chunked) //From Transfer-Encoding header
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*virtual*/ void HTTPText::setDataLen(size_t len) //From Content-Length header, or if the transfer is chunked, next chunk length
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/* HTTPText.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HTTPTEXT_H_
|
||||
#define HTTPTEXT_H_
|
||||
|
||||
#include "../IHTTPData.h"
|
||||
|
||||
/** A data endpoint to store text
|
||||
*/
|
||||
class HTTPText : public IHTTPDataIn, public IHTTPDataOut
|
||||
{
|
||||
public:
|
||||
/** Create an HTTPText instance for output
|
||||
* @param str String to be transmitted
|
||||
*/
|
||||
HTTPText(char* str);
|
||||
|
||||
/** Create an HTTPText instance for input
|
||||
* @param str Buffer to store the incoming string
|
||||
* @param size Size of the buffer
|
||||
*/
|
||||
HTTPText(char* str, size_t size);
|
||||
|
||||
protected:
|
||||
//IHTTPDataIn
|
||||
virtual void readReset();
|
||||
|
||||
virtual int read(char* buf, size_t len, size_t* pReadLen);
|
||||
|
||||
virtual int getDataType(char* type, size_t maxTypeLen); //Internet media type for Content-Type header
|
||||
|
||||
virtual bool getIsChunked(); //For Transfer-Encoding header
|
||||
|
||||
virtual size_t getDataLen(); //For Content-Length header
|
||||
|
||||
//IHTTPDataOut
|
||||
virtual void writeReset();
|
||||
|
||||
virtual int write(const char* buf, size_t len);
|
||||
|
||||
virtual void setDataType(const char* type); //Internet media type from Content-Type header
|
||||
|
||||
virtual void setIsChunked(bool chunked); //From Transfer-Encoding header
|
||||
|
||||
virtual void setDataLen(size_t len); //From Content-Length header, or if the transfer is chunked, next chunk length
|
||||
|
||||
private:
|
||||
char* m_str;
|
||||
size_t m_size;
|
||||
|
||||
size_t m_pos;
|
||||
};
|
||||
|
||||
#endif /* HTTPTEXT_H_ */
|
|
@ -0,0 +1,58 @@
|
|||
#include "mbed.h"
|
||||
#include "CellularModem.h"
|
||||
#include "HTTPClient.h"
|
||||
#include "httptest.h"
|
||||
|
||||
int httptest(CellularModem& modem, const char* apn, const char* username, const char* password)
|
||||
{
|
||||
printf("Connecting...\n");
|
||||
|
||||
HTTPClient http;
|
||||
char str[512];
|
||||
|
||||
modem.power(true);
|
||||
Thread::wait(1000);
|
||||
int ret = modem.connect(apn, username, password);
|
||||
if(ret)
|
||||
{
|
||||
printf("Could not connect\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//GET data
|
||||
printf("Trying to fetch page...\n");
|
||||
ret = http.get("http://mbed.org/media/uploads/donatien/hello.txt", str, 128);
|
||||
if (!ret)
|
||||
{
|
||||
printf("Page fetched successfully - read %d characters\n", strlen(str));
|
||||
printf("Result: %s\n", str);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error - ret = %d - HTTP return code = %d\n", ret, http.getHTTPResponseCode());
|
||||
modem.disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
//POST data
|
||||
HTTPMap map;
|
||||
HTTPText text(str, 512);
|
||||
map.put("Hello", "World");
|
||||
map.put("test", "1234");
|
||||
printf("Trying to post data...\n");
|
||||
ret = http.post("http://httpbin.org/post", map, &text);
|
||||
if (!ret)
|
||||
{
|
||||
printf("Executed POST successfully - read %d characters\n", strlen(str));
|
||||
printf("Result: %s\n", str);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error - ret = %d - HTTP return code = %d\n", ret, http.getHTTPResponseCode());
|
||||
modem.disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
modem.disconnect();
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef HTTPTEST_H_
|
||||
#define HTTPTEST_H_
|
||||
|
||||
#include "CellularModem.h"
|
||||
|
||||
int httptest(CellularModem& modem, const char* apn = NULL, const char* username = NULL, const char* password= NULL);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#include "UbloxUSBGSMModem.h"
|
||||
#include "UbloxUSBCDMAModem.h"
|
||||
#include "httptest.h"
|
||||
|
||||
#if !defined(MODEM_UBLOX_GSM) && !defined(MODEM_UBLOX_CDMA)
|
||||
#warning No modem defined, using GSM by default
|
||||
#define MODEM_UBLOX_GSM
|
||||
#endif
|
||||
|
||||
#ifndef MODEM_APN
|
||||
#warning APN not specified, using "internet"
|
||||
#define MODEM_APN "internet"
|
||||
#endif
|
||||
|
||||
#ifndef MODEM_USERNAME
|
||||
#warning username not specified
|
||||
#define MODEM_USERNAME NULL
|
||||
#endif
|
||||
|
||||
#ifndef MODEM_PASSWORD
|
||||
#warning password not specified
|
||||
#define MODEM_PASSWORD NULL
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef MODEM_UBLOX_GSM
|
||||
UbloxUSBGSMModem modem;
|
||||
#else
|
||||
UbloxUSBCDMAModem modem(p18, true, 1);
|
||||
#endif
|
||||
httptest(modem, MODEM_APN, MODEM_USERNAME, MODEM_PASSWORD);
|
||||
while (true);
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#include "CellularModem.h"
|
||||
#include "smstest.h"
|
||||
|
||||
void smstest(CellularModem& modem)
|
||||
{
|
||||
modem.power(true);
|
||||
Thread::wait(1000);
|
||||
|
||||
#ifdef DESTINATION_NUMBER
|
||||
modem.sendSM(DESINATION_NUMBER, "Hello from mbed:)");
|
||||
#endif
|
||||
|
||||
while(true)
|
||||
{
|
||||
char num[17];
|
||||
char msg[64];
|
||||
size_t count;
|
||||
int ret = modem.getSMCount(&count);
|
||||
if(ret)
|
||||
{
|
||||
printf("getSMCount returned %d\n", ret);
|
||||
Thread::wait(3000);
|
||||
continue;
|
||||
}
|
||||
if( count > 0)
|
||||
{
|
||||
printf("%d SMS to read\n", count);
|
||||
ret = modem.getSM(num, msg, 64);
|
||||
if(ret)
|
||||
{
|
||||
printf("getSM returned %d\n", ret);
|
||||
Thread::wait(3000);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s : %s\n", num, msg);
|
||||
}
|
||||
Thread::wait(3000);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef SMSTEST_H_
|
||||
#define SMSTEST_H_
|
||||
|
||||
#include "CellularModem.h"
|
||||
|
||||
void smstest(CellularModem&);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#include "UbloxUSBGSMModem.h"
|
||||
#include "UbloxUSBCDMAModem.h"
|
||||
#include "smstest.h"
|
||||
|
||||
#if !defined(MODEM_UBLOX_GSM) && !defined(MODEM_UBLOX_CDMA)
|
||||
#warning No modem defined, using GSM by default
|
||||
#define MODEM_UBLOX_GSM
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef MODEM_UBLOX_GSM
|
||||
UbloxUSBGSMModem modem;
|
||||
#else
|
||||
UbloxUSBCDMAModem modem(p18, true, 1);
|
||||
#endif
|
||||
|
||||
smstest(modem);
|
||||
while (true);
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#include "mbed.h"
|
||||
#include "test_env.h"
|
||||
#include "EthernetInterface.h"
|
||||
|
||||
struct s_ip_address {
|
||||
int ip_1;
|
||||
int ip_2;
|
||||
int ip_3;
|
||||
int ip_4;
|
||||
};
|
||||
|
||||
int main() {
|
||||
MBED_HOSTTEST_TIMEOUT(20);
|
||||
MBED_HOSTTEST_SELECT(tcpecho_client_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(TCP echo client);
|
||||
MBED_HOSTTEST_START("NET_4");
|
||||
|
||||
char buffer[256] = {0};
|
||||
char out_buffer[] = "Hello World\n";
|
||||
char out_success[] = "{{success}}\n{{end}}\n";
|
||||
char out_failure[] = "{{failure}}\n{{end}}\n";
|
||||
s_ip_address ip_addr = {0, 0, 0, 0};
|
||||
int port = 0;
|
||||
|
||||
printf("TCPCllient waiting for server IP and port..." NL);
|
||||
scanf("%d.%d.%d.%d:%d", &ip_addr.ip_1, &ip_addr.ip_2, &ip_addr.ip_3, &ip_addr.ip_4, &port);
|
||||
printf("Address received:%d.%d.%d.%d:%d" NL, ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4, port);
|
||||
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
printf("TCPClient IP Address is %s" NL, eth.getIPAddress());
|
||||
sprintf(buffer, "%d.%d.%d.%d", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4);
|
||||
|
||||
TCPSocketConnection socket;
|
||||
while (socket.connect(buffer, port) < 0) {
|
||||
printf("TCPCllient unable to connect to %s:%d" NL, buffer, port);
|
||||
wait(1);
|
||||
}
|
||||
|
||||
socket.send_all(out_buffer, sizeof(out_buffer) - 1);
|
||||
|
||||
int n = socket.receive(buffer, sizeof(buffer));
|
||||
if (n > 0)
|
||||
{
|
||||
buffer[n] = '\0';
|
||||
printf("%s", buffer);
|
||||
if (strncmp(out_buffer, buffer, sizeof(out_buffer) - 1) == 0) {
|
||||
socket.send_all(out_success, sizeof(out_success) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
socket.send_all(out_failure, sizeof(out_failure) - 1);
|
||||
|
||||
socket.close();
|
||||
eth.disconnect();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
#include "mbed.h"
|
||||
#include "test_env.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
const int BUFFER_SIZE = 64;
|
||||
const int MAX_ECHO_LOOPS = 1000;
|
||||
const char ASCII_MAX = '~' - ' ';
|
||||
|
||||
struct s_ip_address
|
||||
{
|
||||
int ip_1;
|
||||
int ip_2;
|
||||
int ip_3;
|
||||
int ip_4;
|
||||
};
|
||||
}
|
||||
|
||||
char char_rand() {
|
||||
return (rand() % ASCII_MAX) + ' ';
|
||||
}
|
||||
|
||||
int main() {
|
||||
MBED_HOSTTEST_TIMEOUT(20);
|
||||
MBED_HOSTTEST_SELECT(tcpecho_client_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(TCP client echo loop);
|
||||
MBED_HOSTTEST_START("NET_13");
|
||||
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
char out_buffer[BUFFER_SIZE] = {0};
|
||||
s_ip_address ip_addr = {0, 0, 0, 0};
|
||||
int port = 0;
|
||||
|
||||
printf("MBED: TCPCllient waiting for server IP and port...\r\n");
|
||||
scanf("%d.%d.%d.%d:%d", &ip_addr.ip_1, &ip_addr.ip_2, &ip_addr.ip_3, &ip_addr.ip_4, &port);
|
||||
printf("MBED: Address received: %d.%d.%d.%d:%d\r\n", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4, port);
|
||||
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
printf("MBED: TCPClient IP Address is %s\r\n", eth.getIPAddress());
|
||||
sprintf(buffer, "%d.%d.%d.%d", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4);
|
||||
|
||||
TCPSocketConnection socket;
|
||||
while (socket.connect(buffer, port) < 0) {
|
||||
printf("MBED: TCPCllient unable to connect to %s:%d\r\n", buffer, port);
|
||||
wait(1);
|
||||
}
|
||||
|
||||
// Test loop for multiple client connections
|
||||
bool result = true;
|
||||
int count_error = 0;
|
||||
for (int i = 0; i < MAX_ECHO_LOOPS; i++) {
|
||||
std::generate(out_buffer, out_buffer + BUFFER_SIZE, char_rand);
|
||||
socket.send_all(out_buffer, BUFFER_SIZE);
|
||||
|
||||
int n = socket.receive(buffer, BUFFER_SIZE);
|
||||
if (n > 0)
|
||||
{
|
||||
bool echoed = memcmp(out_buffer, buffer, BUFFER_SIZE) == 0;
|
||||
result = result && echoed;
|
||||
if (echoed == false) {
|
||||
count_error++; // Count error messages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("MBED: Loop messages passed: %d / %d\r\n", MAX_ECHO_LOOPS - count_error, MAX_ECHO_LOOPS);
|
||||
|
||||
if (notify_completion_str(result, buffer)) {
|
||||
socket.send_all(buffer, strlen(buffer));
|
||||
}
|
||||
socket.close();
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(result);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#include "mbed.h"
|
||||
#include "test_env.h"
|
||||
#include "EthernetInterface.h"
|
||||
|
||||
namespace {
|
||||
const int ECHO_SERVER_PORT = 7;
|
||||
const int BUFFER_SIZE = 64;
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
MBED_HOSTTEST_TIMEOUT(20);
|
||||
MBED_HOSTTEST_SELECT(tcpecho_server_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(TCP echo server);
|
||||
MBED_HOSTTEST_START("NET_3");
|
||||
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
printf("MBED: Server IP Address is %s:%d" NL, eth.getIPAddress(), ECHO_SERVER_PORT);
|
||||
|
||||
TCPSocketServer server;
|
||||
server.bind(ECHO_SERVER_PORT);
|
||||
server.listen();
|
||||
|
||||
while (true) {
|
||||
printf("MBED: Wait for new connection..." NL);
|
||||
TCPSocketConnection client;
|
||||
server.accept(client);
|
||||
client.set_blocking(false, 1500); // Timeout after (1.5)s
|
||||
printf("MBED: Connection from: %s" NL, client.get_address());
|
||||
|
||||
while (true) {
|
||||
const int n = client.receive(buffer, sizeof(buffer));
|
||||
if (n <= 0) {
|
||||
break;
|
||||
}
|
||||
const int buffer_string_end_index = n >= BUFFER_SIZE ? BUFFER_SIZE-1 : n;
|
||||
buffer[buffer_string_end_index] = '\0';
|
||||
client.send_all(buffer, n);
|
||||
if (n <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
client.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
#include "mbed.h"
|
||||
#include "rtos.h"
|
||||
#include "test_env.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include <algorithm>
|
||||
|
||||
#define CHECK(RC, STEP) if (RC < 0) error(STEP": %d\r\n", RC)
|
||||
|
||||
namespace {
|
||||
const int BUFFER_SIZE = 64;
|
||||
const int MAX_ECHO_LOOPS = 100;
|
||||
const char ASCII_MAX = '~' - ' ';
|
||||
|
||||
struct s_ip_address {
|
||||
int ip_1;
|
||||
int ip_2;
|
||||
int ip_3;
|
||||
int ip_4;
|
||||
};
|
||||
}
|
||||
|
||||
char char_rand() {
|
||||
return (rand() % ASCII_MAX) + ' ';
|
||||
}
|
||||
|
||||
int main() {
|
||||
MBED_HOSTTEST_TIMEOUT(20);
|
||||
MBED_HOSTTEST_SELECT(udpecho_client_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(UDP echo client);
|
||||
MBED_HOSTTEST_START("NET_6");
|
||||
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
char out_buffer[BUFFER_SIZE] = {0};
|
||||
s_ip_address ip_addr = {0, 0, 0, 0};
|
||||
int port = 0;
|
||||
bool result = true;
|
||||
|
||||
printf("MBED: UDPCllient waiting for server IP and port...\r\n");
|
||||
scanf("%d.%d.%d.%d:%d", &ip_addr.ip_1, &ip_addr.ip_2, &ip_addr.ip_3, &ip_addr.ip_4, &port);
|
||||
printf("MBED: Address received: %d.%d.%d.%d:%d\r\n", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4, port);
|
||||
|
||||
EthernetInterface eth;
|
||||
int rc = eth.init(); //Use DHCP
|
||||
CHECK(rc, "eth init");
|
||||
|
||||
rc = eth.connect();
|
||||
CHECK(rc, "connect");
|
||||
printf("IP: %s\n", eth.getIPAddress());
|
||||
|
||||
UDPSocket socket;
|
||||
rc = socket.init();
|
||||
CHECK(rc, "socket init");
|
||||
|
||||
printf("MBED: UDPClient IP Address is %s\r\n", eth.getIPAddress());
|
||||
sprintf(buffer, "%d.%d.%d.%d", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4);
|
||||
|
||||
Endpoint echo_server;
|
||||
rc = echo_server.set_address(buffer, port);
|
||||
CHECK(rc, "set_address");
|
||||
|
||||
for (int i =0; i < MAX_ECHO_LOOPS; i++) {
|
||||
std::generate(out_buffer, out_buffer + BUFFER_SIZE, char_rand);
|
||||
rc = socket.sendTo(echo_server, out_buffer, sizeof(out_buffer));
|
||||
CHECK(rc, "sendTo");
|
||||
|
||||
const int n = socket.receiveFrom(echo_server, buffer, sizeof(buffer));
|
||||
CHECK(n, "receiveFrom");
|
||||
|
||||
if (memcmp(buffer, out_buffer, BUFFER_SIZE) != 0) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (notify_completion_str(result, buffer)) {
|
||||
socket.sendTo(echo_server, buffer, strlen(buffer));
|
||||
}
|
||||
|
||||
socket.close();
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(result);
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
#include "mbed.h"
|
||||
#include "rtos.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* How to use:
|
||||
* make.py -m LPC1768 -t ARM -d E:\ -n NET_14
|
||||
* udp_link_layer_auto.py -p COM20 -d E:\ -t 10
|
||||
*/
|
||||
|
||||
// Evil globals
|
||||
namespace {
|
||||
// IP and port used to store HOST address info
|
||||
char host_address[32] = {0};
|
||||
volatile int host_port = 0;
|
||||
|
||||
const int ECHO_SERVER_PORT = 7;
|
||||
const int BUFFER_SIZE = 64;
|
||||
|
||||
// Forwarding packet queue
|
||||
std::list<std::string> datagram_queue;
|
||||
|
||||
// Statistics (mbed)
|
||||
volatile int received_packets = 0;
|
||||
volatile int forwarded_packets = 0;
|
||||
volatile int max_queue_len = 0;
|
||||
|
||||
Mutex cli_serv_mutex;
|
||||
// cli_serv_mutex.lock(); // LOCK
|
||||
// cli_serv_mutex.unlock(); // LOCK
|
||||
}
|
||||
|
||||
void udp_server_task(void const *argument)
|
||||
{
|
||||
DigitalOut indicator(LED1);
|
||||
UDPSocket server;
|
||||
|
||||
server.bind(ECHO_SERVER_PORT);
|
||||
// printf("[udp_server_task] Start\r\n");
|
||||
|
||||
Endpoint client;
|
||||
char buffer[BUFFER_SIZE] = { 0 };
|
||||
while (true) {
|
||||
//printf("[udp_server_task] Wait for packet...\r\n");
|
||||
int n = server.receiveFrom(client, buffer, sizeof(buffer));
|
||||
if (n > 0) {
|
||||
//printf("[udp_server_task] Received packet from: %s\r\n", client.get_address());
|
||||
const int buffer_string_end_index = n >= BUFFER_SIZE ? BUFFER_SIZE - 1 : n;
|
||||
buffer[buffer_string_end_index] = '\0';
|
||||
//printf("[udp_server_task] Server received: %s\r\n", buffer);
|
||||
if (host_port == 0) {
|
||||
strcpy(host_address, client.get_address());
|
||||
host_port = ECHO_SERVER_PORT + 1;
|
||||
//printf("[udp_server_task] Set host address and port: %s:%d\r\n", host_address, host_port);
|
||||
}
|
||||
// Dispatch data to client for sending to test HOST
|
||||
cli_serv_mutex.lock(); // LOCK
|
||||
// Push to datagram queue
|
||||
datagram_queue.push_front(std::string(buffer));
|
||||
max_queue_len = datagram_queue.size() > max_queue_len ? datagram_queue.size() : max_queue_len;
|
||||
received_packets++;
|
||||
cli_serv_mutex.unlock(); // LOCK
|
||||
indicator = !indicator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void udp_client_task(void const *argument)
|
||||
{
|
||||
while (host_port == 0) {
|
||||
// Waiting for HOST port notification
|
||||
}
|
||||
|
||||
DigitalOut indicator(LED2);
|
||||
UDPSocket socket;
|
||||
socket.init();
|
||||
|
||||
Endpoint echo_server;
|
||||
echo_server.set_address(host_address, host_port);
|
||||
//printf("[udp_client_task] Start: %s:%d\r\n", host_address, host_port);
|
||||
|
||||
while (1) {
|
||||
std::string datagram;
|
||||
bool sent_datagram = false;
|
||||
cli_serv_mutex.lock(); // LOCK
|
||||
if (datagram_queue.size() > 0) {
|
||||
// POP from datagram queue
|
||||
datagram = datagram_queue.back();
|
||||
datagram_queue.pop_back();
|
||||
sent_datagram = true;
|
||||
}
|
||||
cli_serv_mutex.unlock(); // LOCK
|
||||
if (sent_datagram) {
|
||||
//printf("[udp_client_task] Forwarded datagram: %s\r\n", datagram.c_str());
|
||||
socket.sendTo(echo_server, (char *)datagram.c_str(), datagram.length());
|
||||
forwarded_packets++;
|
||||
indicator = !indicator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
EthernetInterface eth;
|
||||
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
printf("MBED: Server IP Address is %s:%d\r\n", eth.getIPAddress(), ECHO_SERVER_PORT);
|
||||
|
||||
Thread UdpServerTask(udp_server_task, NULL, osPriorityNormal, DEFAULT_STACK_SIZE * 2.25);
|
||||
Thread UdpClientTask(udp_client_task, NULL, osPriorityNormal, DEFAULT_STACK_SIZE * 2.25);
|
||||
|
||||
// Control TCP server to get MBED statistics
|
||||
{
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
const int TELNET_SERVER_PORT = 23;
|
||||
const int BUFFER_SIZE = 256;
|
||||
TCPSocketServer server;
|
||||
server.bind(TELNET_SERVER_PORT);
|
||||
server.listen();
|
||||
|
||||
while (true) {
|
||||
printf("MBED: Wait for new connection...\r\n");
|
||||
TCPSocketConnection client;
|
||||
server.accept(client);
|
||||
client.set_blocking(false, 1500); // Timeout after (1.5)s
|
||||
printf("MBED: Connection from: %s\r\n", client.get_address());
|
||||
|
||||
while (true) {
|
||||
int n = client.receive(buffer, sizeof(buffer));
|
||||
//if (n <= 0) break;
|
||||
if (n > 0) {
|
||||
// printf("Recv %d chars\r\n", n);
|
||||
const int buffer_string_end_index = n >= BUFFER_SIZE ? BUFFER_SIZE - 1 : n;
|
||||
buffer[buffer_string_end_index] = '\0';
|
||||
// client.send_all(buffer, strlen(buffer));
|
||||
if (strncmp(buffer, "stat", 4) == 0) {
|
||||
sprintf(buffer, "received_packets %d\nforwarded_packets %d\nmax_queue_len %d",
|
||||
received_packets, forwarded_packets, max_queue_len);
|
||||
client.send_all(buffer, strlen(buffer));
|
||||
// printf("%s", buffer);
|
||||
}
|
||||
}
|
||||
//if (n <= 0) break;
|
||||
}
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include "mbed.h"
|
||||
#include "test_env.h"
|
||||
#include "EthernetInterface.h"
|
||||
|
||||
namespace {
|
||||
const int ECHO_SERVER_PORT = 7;
|
||||
const int BUFFER_SIZE = 64;
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
MBED_HOSTTEST_TIMEOUT(20);
|
||||
MBED_HOSTTEST_SELECT(udpecho_server_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(UDP echo server);
|
||||
MBED_HOSTTEST_START("NET_5");
|
||||
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
printf("MBED: Server IP Address is %s:%d\r\n", eth.getIPAddress(), ECHO_SERVER_PORT);
|
||||
|
||||
UDPSocket server;
|
||||
server.bind(ECHO_SERVER_PORT);
|
||||
|
||||
Endpoint client;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
printf("MBED: Waiting for packet...\r\n");
|
||||
while (true) {
|
||||
int n = server.receiveFrom(client, buffer, sizeof(buffer));
|
||||
if (n > 0) {
|
||||
//printf("Received packet from: %s\n", client.get_address());
|
||||
const int buffer_string_end_index = n >= BUFFER_SIZE ? BUFFER_SIZE-1 : n;
|
||||
buffer[buffer_string_end_index] = '\0';
|
||||
//printf("Server received: %s\n", buffer);
|
||||
server.sendTo(client, buffer, n);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#include "mbed.h"
|
||||
#include "EthernetInterface.h"
|
||||
|
||||
const int BROADCAST_PORT = 58083;
|
||||
|
||||
int main() {
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
UDPSocket socket;
|
||||
socket.bind(BROADCAST_PORT);
|
||||
socket.set_broadcasting();
|
||||
|
||||
Endpoint broadcaster;
|
||||
char buffer[256];
|
||||
while (true) {
|
||||
printf("\nWait for packet...\n");
|
||||
int n = socket.receiveFrom(broadcaster, buffer, sizeof(buffer));
|
||||
buffer[n] = '\0';
|
||||
printf("Packet from \"%s\": %s\n", broadcaster.get_address(), buffer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#include "mbed.h"
|
||||
#include "EthernetInterface.h"
|
||||
|
||||
const int BROADCAST_PORT = 58083;
|
||||
|
||||
int main() {
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
UDPSocket sock;
|
||||
sock.init();
|
||||
sock.set_broadcasting();
|
||||
|
||||
Endpoint broadcast;
|
||||
broadcast.set_address("255.255.255.255", BROADCAST_PORT);
|
||||
|
||||
char out_buffer[] = "very important data";
|
||||
|
||||
while (true) {
|
||||
printf("Broadcasting...\n");
|
||||
sock.sendTo(broadcast, out_buffer, sizeof(out_buffer));
|
||||
Thread::wait(1000);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#include "mbed.h"
|
||||
#include "EthernetInterface.h"
|
||||
|
||||
const char* MCAST_GRP = "224.1.1.1";
|
||||
const int MCAST_PORT = 5007;
|
||||
|
||||
int main() {
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
UDPSocket server;
|
||||
server.bind(MCAST_PORT);
|
||||
if (server.join_multicast_group(MCAST_GRP) != 0) {
|
||||
printf("Error joining the multicast group\n");
|
||||
while (true) {}
|
||||
}
|
||||
|
||||
Endpoint client;
|
||||
char buffer[256];
|
||||
while (true) {
|
||||
printf("\nWait for packet...\n");
|
||||
int n = server.receiveFrom(client, buffer, sizeof(buffer));
|
||||
|
||||
printf("Packet from \"%s\": %s\n", client.get_address(), buffer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#include "mbed.h"
|
||||
#include "EthernetInterface.h"
|
||||
|
||||
const char* MCAST_GRP = "224.1.1.1";
|
||||
const int MCAST_PORT = 5007;
|
||||
|
||||
int main() {
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
UDPSocket sock;
|
||||
sock.init();
|
||||
|
||||
Endpoint multicast_group;
|
||||
multicast_group.set_address(MCAST_GRP, MCAST_PORT);
|
||||
|
||||
char out_buffer[] = "very important data";
|
||||
while (true) {
|
||||
printf("Multicast to group: %s\n", MCAST_GRP);
|
||||
sock.sendTo(multicast_group, out_buffer, sizeof(out_buffer));
|
||||
Thread::wait(1000);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
#include <algorithm>
|
||||
#include "mbed.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include "test_env.h"
|
||||
|
||||
namespace {
|
||||
// Test connection information
|
||||
const char *HTTP_SERVER_NAME = "developer.mbed.org";
|
||||
const char *HTTP_SERVER_FILE_PATH = "/media/uploads/mbed_official/hello.txt";
|
||||
const int HTTP_SERVER_PORT = 80;
|
||||
const int RECV_BUFFER_SIZE = 512;
|
||||
|
||||
// Test related data
|
||||
const char *HTTP_OK_STR = "200 OK";
|
||||
const char *HTTP_HELLO_STR = "Hello world!";
|
||||
|
||||
// Test buffers
|
||||
char buffer[RECV_BUFFER_SIZE] = {0};
|
||||
}
|
||||
|
||||
bool find_substring(const char *first, const char *last, const char *s_first, const char *s_last) {
|
||||
const char *f = std::search(first, last, s_first, s_last);
|
||||
return (f != last);
|
||||
}
|
||||
|
||||
int main() {
|
||||
MBED_HOSTTEST_TIMEOUT(20);
|
||||
MBED_HOSTTEST_SELECT(default_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(TCP client hello world);
|
||||
MBED_HOSTTEST_START("NET_1");
|
||||
|
||||
bool result = false;
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
printf("TCP client IP Address is %s\r\n", eth.getIPAddress());
|
||||
|
||||
TCPSocketConnection sock;
|
||||
if (sock.connect(HTTP_SERVER_NAME, HTTP_SERVER_PORT) == 0) {
|
||||
printf("HTTP: Connected to %s:%d\r\n", HTTP_SERVER_NAME, HTTP_SERVER_PORT);
|
||||
|
||||
// We are constructing GET command like this:
|
||||
// GET http://developer.mbed.org/media/uploads/mbed_official/hello.txt HTTP/1.0\n\n
|
||||
strcpy(buffer, "GET http://");
|
||||
strcat(buffer, HTTP_SERVER_NAME);
|
||||
strcat(buffer, HTTP_SERVER_FILE_PATH);
|
||||
strcat(buffer, " HTTP/1.0\n\n");
|
||||
// Send GET command
|
||||
sock.send_all(buffer, strlen(buffer));
|
||||
|
||||
// Server will respond with HTTP GET's success code
|
||||
bool found_200_ok = false;
|
||||
{
|
||||
const int ret = sock.receive(buffer, sizeof(buffer) - 1);
|
||||
buffer[ret] = '\0';
|
||||
// Find 200 OK HTTP status in reply
|
||||
found_200_ok = find_substring(buffer, buffer + ret, HTTP_OK_STR, HTTP_OK_STR + strlen(HTTP_OK_STR));
|
||||
printf("HTTP: Received %d chars from server\r\n", ret);
|
||||
printf("HTTP: Received 200 OK status ... %s\r\n", found_200_ok ? "[OK]" : "[FAIL]");
|
||||
printf("HTTP: Received massage:\r\n\r\n");
|
||||
printf("%s", buffer);
|
||||
}
|
||||
|
||||
// Server will respond with requested file content
|
||||
bool found_hello = false;
|
||||
{
|
||||
const int ret = sock.receive(buffer, sizeof(buffer) - 1);
|
||||
buffer[ret] = '\0';
|
||||
// Find Hello World! in reply
|
||||
found_hello = find_substring(buffer, buffer + ret, HTTP_HELLO_STR, HTTP_HELLO_STR + strlen(HTTP_HELLO_STR));
|
||||
printf("HTTP: Received %d chars from server\r\n", ret);
|
||||
printf("HTTP: Received '%s' status ... %s\r\n", HTTP_HELLO_STR, found_hello ? "[OK]" : "[FAIL]");
|
||||
printf("HTTP: Received massage:\r\n\r\n");
|
||||
printf("%s", buffer);
|
||||
}
|
||||
|
||||
if (found_200_ok && found_hello) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
sock.close();
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(result);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#include "mbed.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include "test_env.h"
|
||||
|
||||
namespace {
|
||||
const char *HTTP_SERVER_NAME = "utcnist.colorado.edu";
|
||||
const int HTTP_SERVER_PORT = 37;
|
||||
const float YEARS_TO_PASS = 114.0;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
MBED_HOSTTEST_TIMEOUT(20);
|
||||
MBED_HOSTTEST_SELECT(default_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(NIST Internet Time Service);
|
||||
MBED_HOSTTEST_START("NET_2");
|
||||
|
||||
bool result = false;
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
printf("UDP client IP Address is %s\n", eth.getIPAddress());
|
||||
|
||||
UDPSocket sock;
|
||||
sock.init();
|
||||
|
||||
Endpoint nist;
|
||||
nist.set_address(HTTP_SERVER_NAME, HTTP_SERVER_PORT);
|
||||
|
||||
char out_buffer[] = "plop"; // Does not matter
|
||||
sock.sendTo(nist, out_buffer, sizeof(out_buffer));
|
||||
|
||||
union {
|
||||
char in_buffer_tab[4];
|
||||
unsigned int in_buffer_uint;
|
||||
};
|
||||
|
||||
const int n = sock.receiveFrom(nist, in_buffer_tab, sizeof(in_buffer_tab));
|
||||
if (n > 0) {
|
||||
result = true;
|
||||
const unsigned int timeRes = ntohl(in_buffer_uint);
|
||||
const float years = timeRes / 60.0 / 60.0 / 24.0 / 365.0;
|
||||
const float days = timeRes / 24.0 / 60.0 / 60.0;
|
||||
printf("UDP: Received %d bytes from server %s on port %d\r\n", n, nist.get_address(), nist.get_port());
|
||||
printf("UDP: %u seconds since 01/01/1900 00:00 GMT ... %s\r\n", timeRes, timeRes > 0 ? "[OK]" : "[FAIL]");
|
||||
printf("UDP: %.2f days since 01/01/1900 00:00 GMT ... %s\r\n", days, timeRes > 0 ? "[OK]" : "[FAIL]");
|
||||
printf("UDP: %.2f years since 01/01/1900 00:00 GMT ... %s\r\n", years, timeRes > YEARS_TO_PASS ? "[OK]" : "[FAIL]");
|
||||
|
||||
if (years < YEARS_TO_PASS) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
sock.close();
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(result);
|
||||
}
|
|
@ -0,0 +1,621 @@
|
|||
/* HTTPClient.cpp */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//Debug is disabled by default
|
||||
#if 1
|
||||
//Enable debug
|
||||
#include <cstdio>
|
||||
//#define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
|
||||
#define DBG(x, ...)
|
||||
#define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
|
||||
#define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
|
||||
|
||||
#else
|
||||
//Disable debug
|
||||
#define DBG(x, ...)
|
||||
#define WARN(x, ...)
|
||||
#define ERR(x, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#define HTTP_PORT 80
|
||||
|
||||
#define OK 0
|
||||
|
||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||
#define MAX(x,y) (((x)>(y))?(x):(y))
|
||||
|
||||
#define CHUNK_SIZE 256
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "HTTPClient.h"
|
||||
|
||||
HTTPClient::HTTPClient() :
|
||||
m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HTTPClient::~HTTPClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
|
||||
{
|
||||
m_basicAuthUser = user;
|
||||
m_basicAuthPassword = password;
|
||||
}
|
||||
#endif
|
||||
|
||||
HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
return connect(url, HTTP_GET, NULL, pDataIn, timeout);
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
HTTPText str(result, maxResultLen);
|
||||
return get(url, &str, timeout);
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
|
||||
{
|
||||
return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
|
||||
}
|
||||
|
||||
int HTTPClient::getHTTPResponseCode()
|
||||
{
|
||||
return m_httpResponseCode;
|
||||
}
|
||||
|
||||
#define CHECK_CONN_ERR(ret) \
|
||||
do{ \
|
||||
if(ret) { \
|
||||
m_sock.close(); \
|
||||
ERR("Connection error (%d)", ret); \
|
||||
return HTTP_CONN; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define PRTCL_ERR() \
|
||||
do{ \
|
||||
m_sock.close(); \
|
||||
ERR("Protocol error"); \
|
||||
return HTTP_PRTCL; \
|
||||
} while(0)
|
||||
|
||||
HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
|
||||
{
|
||||
m_httpResponseCode = 0; //Invalidate code
|
||||
m_timeout = timeout;
|
||||
|
||||
char scheme[8];
|
||||
uint16_t port;
|
||||
char host[32];
|
||||
char path[64];
|
||||
//First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
|
||||
HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
|
||||
if(res != HTTP_OK)
|
||||
{
|
||||
ERR("parseURL returned %d", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
if(port == 0) //TODO do handle HTTPS->443
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
|
||||
DBG("Scheme: %s", scheme);
|
||||
DBG("Host: %s", host);
|
||||
DBG("Port: %d", port);
|
||||
DBG("Path: %s", path);
|
||||
|
||||
//Connect
|
||||
DBG("Connecting socket to server");
|
||||
int ret = m_sock.connect(host, port);
|
||||
if (ret < 0)
|
||||
{
|
||||
m_sock.close();
|
||||
ERR("Could not connect");
|
||||
return HTTP_CONN;
|
||||
}
|
||||
|
||||
//Send request
|
||||
DBG("Sending request");
|
||||
char buf[CHUNK_SIZE];
|
||||
const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":"";
|
||||
snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
|
||||
ret = send(buf);
|
||||
if(ret)
|
||||
{
|
||||
m_sock.close();
|
||||
ERR("Could not write request");
|
||||
return HTTP_CONN;
|
||||
}
|
||||
|
||||
//Send all headers
|
||||
|
||||
//Send default headers
|
||||
DBG("Sending headers");
|
||||
if( (method == HTTP_POST) && (pDataOut != NULL) )
|
||||
{
|
||||
if( pDataOut->getIsChunked() )
|
||||
{
|
||||
ret = send("Transfer-Encoding: chunked\r\n");
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
|
||||
ret = send(buf);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
char type[48];
|
||||
if( pDataOut->getDataType(type, 48) == HTTP_OK )
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
|
||||
ret = send(buf);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
}
|
||||
|
||||
//Close headers
|
||||
DBG("Headers sent");
|
||||
ret = send("\r\n");
|
||||
CHECK_CONN_ERR(ret);
|
||||
|
||||
size_t trfLen;
|
||||
|
||||
//Send data (if POST)
|
||||
if( (method == HTTP_POST) && (pDataOut != NULL) )
|
||||
{
|
||||
DBG("Sending data");
|
||||
while(true)
|
||||
{
|
||||
size_t writtenLen = 0;
|
||||
pDataOut->read(buf, CHUNK_SIZE, &trfLen);
|
||||
if( pDataOut->getIsChunked() )
|
||||
{
|
||||
//Write chunk header
|
||||
char chunkHeader[16];
|
||||
snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
|
||||
ret = send(chunkHeader);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
else if( trfLen == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if( trfLen != 0 )
|
||||
{
|
||||
ret = send(buf, trfLen);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
|
||||
if( pDataOut->getIsChunked() )
|
||||
{
|
||||
ret = send("\r\n"); //Chunk-terminating CRLF
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
writtenLen += trfLen;
|
||||
if( writtenLen >= pDataOut->getDataLen() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( trfLen == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Receive response
|
||||
DBG("Receiving response");
|
||||
ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
|
||||
CHECK_CONN_ERR(ret);
|
||||
|
||||
buf[trfLen] = '\0';
|
||||
|
||||
char* crlfPtr = strstr(buf, "\r\n");
|
||||
if(crlfPtr == NULL)
|
||||
{
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
int crlfPos = crlfPtr - buf;
|
||||
buf[crlfPos] = '\0';
|
||||
|
||||
//Parse HTTP response
|
||||
if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
|
||||
{
|
||||
//Cannot match string, error
|
||||
ERR("Not a correct HTTP answer : %s\n", buf);
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
if(m_httpResponseCode != 200)
|
||||
{
|
||||
//Cannot match string, error
|
||||
WARN("Response code %d", m_httpResponseCode);
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
DBG("Reading headers");
|
||||
|
||||
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
|
||||
trfLen -= (crlfPos + 2);
|
||||
|
||||
size_t recvContentLength = 0;
|
||||
bool recvChunked = false;
|
||||
//Now get headers
|
||||
while( true )
|
||||
{
|
||||
crlfPtr = strstr(buf, "\r\n");
|
||||
if(crlfPtr == NULL)
|
||||
{
|
||||
if( trfLen < CHUNK_SIZE - 1 )
|
||||
{
|
||||
size_t newTrfLen;
|
||||
ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
|
||||
trfLen += newTrfLen;
|
||||
buf[trfLen] = '\0';
|
||||
DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
|
||||
CHECK_CONN_ERR(ret);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRTCL_ERR();
|
||||
}
|
||||
}
|
||||
|
||||
crlfPos = crlfPtr - buf;
|
||||
|
||||
if(crlfPos == 0) //End of headers
|
||||
{
|
||||
DBG("Headers read");
|
||||
memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
|
||||
trfLen -= 2;
|
||||
break;
|
||||
}
|
||||
|
||||
buf[crlfPos] = '\0';
|
||||
|
||||
char key[64] = {0};
|
||||
char value[32] = {0};
|
||||
|
||||
int n = sscanf(buf, "%63[^:]: %31[^\r\n]", key, value);
|
||||
|
||||
if ( n == 2 )
|
||||
{
|
||||
DBG("Read header : %s: %s\n", key, value);
|
||||
if( !strcmp(key, "Content-Length") )
|
||||
{
|
||||
sscanf(value, "%d", &recvContentLength);
|
||||
pDataIn->setDataLen(recvContentLength);
|
||||
}
|
||||
else if( !strcmp(key, "Transfer-Encoding") )
|
||||
{
|
||||
if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
|
||||
{
|
||||
recvChunked = true;
|
||||
pDataIn->setIsChunked(true);
|
||||
}
|
||||
}
|
||||
else if( !strcmp(key, "Content-Type") )
|
||||
{
|
||||
pDataIn->setDataType(value);
|
||||
}
|
||||
|
||||
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
|
||||
trfLen -= (crlfPos + 2);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Could not parse header");
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Receive data
|
||||
DBG("Receiving data");
|
||||
while(true)
|
||||
{
|
||||
size_t readLen = 0;
|
||||
|
||||
if( recvChunked )
|
||||
{
|
||||
//Read chunk header
|
||||
crlfPos=0;
|
||||
for(crlfPos++; crlfPos < trfLen - 2; crlfPos++)
|
||||
{
|
||||
if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(crlfPos >= trfLen - 2) //Try to read more
|
||||
{
|
||||
if( trfLen < CHUNK_SIZE )
|
||||
{
|
||||
size_t newTrfLen;
|
||||
ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
|
||||
trfLen += newTrfLen;
|
||||
CHECK_CONN_ERR(ret);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRTCL_ERR();
|
||||
}
|
||||
}
|
||||
buf[crlfPos] = '\0';
|
||||
int n = sscanf(buf, "%x", &readLen);
|
||||
if(n!=1)
|
||||
{
|
||||
ERR("Could not read chunk length");
|
||||
PRTCL_ERR();
|
||||
}
|
||||
|
||||
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
|
||||
trfLen -= (crlfPos + 2);
|
||||
|
||||
if( readLen == 0 )
|
||||
{
|
||||
//Last chunk
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
readLen = recvContentLength;
|
||||
}
|
||||
|
||||
DBG("Retrieving %d bytes", readLen);
|
||||
|
||||
do
|
||||
{
|
||||
pDataIn->write(buf, MIN(trfLen, readLen));
|
||||
if( trfLen > readLen )
|
||||
{
|
||||
memmove(buf, &buf[readLen], trfLen - readLen);
|
||||
trfLen -= readLen;
|
||||
readLen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
readLen -= trfLen;
|
||||
}
|
||||
|
||||
if(readLen)
|
||||
{
|
||||
ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
|
||||
CHECK_CONN_ERR(ret);
|
||||
}
|
||||
} while(readLen);
|
||||
|
||||
if( recvChunked )
|
||||
{
|
||||
if(trfLen < 2)
|
||||
{
|
||||
size_t newTrfLen;
|
||||
//Read missing chars to find end of chunk
|
||||
ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen);
|
||||
CHECK_CONN_ERR(ret);
|
||||
trfLen += newTrfLen;
|
||||
}
|
||||
if( (buf[0] != '\r') || (buf[1] != '\n') )
|
||||
{
|
||||
ERR("Format error");
|
||||
PRTCL_ERR();
|
||||
}
|
||||
memmove(buf, &buf[2], trfLen - 2);
|
||||
trfLen -= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_sock.close();
|
||||
DBG("Completed HTTP transaction");
|
||||
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
|
||||
{
|
||||
DBG("Trying to read between %d and %d bytes", minLen, maxLen);
|
||||
size_t readLen = 0;
|
||||
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
WARN("Connection was closed by server");
|
||||
return HTTP_CLOSED; //Connection was closed by server
|
||||
}
|
||||
|
||||
int ret;
|
||||
while(readLen < maxLen)
|
||||
{
|
||||
if(readLen < minLen)
|
||||
{
|
||||
DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
|
||||
m_sock.set_blocking(false, m_timeout);
|
||||
ret = m_sock.receive_all(buf + readLen, minLen - readLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
|
||||
m_sock.set_blocking(false, 0);
|
||||
ret = m_sock.receive(buf + readLen, maxLen - readLen);
|
||||
}
|
||||
|
||||
if( ret > 0)
|
||||
{
|
||||
readLen += ret;
|
||||
}
|
||||
else if( ret == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
ERR("Connection error (recv returned %d)", ret);
|
||||
*pReadLen = readLen;
|
||||
return HTTP_CONN;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
DBG("Read %d bytes", readLen);
|
||||
*pReadLen = readLen;
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
|
||||
{
|
||||
if(len == 0)
|
||||
{
|
||||
len = strlen(buf);
|
||||
}
|
||||
DBG("Trying to write %d bytes", len);
|
||||
size_t writtenLen = 0;
|
||||
|
||||
if(!m_sock.is_connected())
|
||||
{
|
||||
WARN("Connection was closed by server");
|
||||
return HTTP_CLOSED; //Connection was closed by server
|
||||
}
|
||||
|
||||
m_sock.set_blocking(false, m_timeout);
|
||||
int ret = m_sock.send_all(buf, len);
|
||||
if(ret > 0)
|
||||
{
|
||||
writtenLen += ret;
|
||||
}
|
||||
else if( ret == 0 )
|
||||
{
|
||||
WARN("Connection was closed by server");
|
||||
return HTTP_CLOSED; //Connection was closed by server
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Connection error (send returned %d)", ret);
|
||||
return HTTP_CONN;
|
||||
}
|
||||
|
||||
DBG("Written %d bytes", writtenLen);
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
char* schemePtr = (char*) url;
|
||||
char* hostPtr = (char*) strstr(url, "://");
|
||||
if(hostPtr == NULL)
|
||||
{
|
||||
WARN("Could not find host");
|
||||
return HTTP_PARSE; //URL is invalid
|
||||
}
|
||||
|
||||
if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
|
||||
{
|
||||
WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
memcpy(scheme, schemePtr, hostPtr - schemePtr);
|
||||
scheme[hostPtr - schemePtr] = '\0';
|
||||
|
||||
hostPtr+=3;
|
||||
|
||||
size_t hostLen = 0;
|
||||
|
||||
char* portPtr = strchr(hostPtr, ':');
|
||||
if( portPtr != NULL )
|
||||
{
|
||||
hostLen = portPtr - hostPtr;
|
||||
portPtr++;
|
||||
if( sscanf(portPtr, "%hu", port) != 1)
|
||||
{
|
||||
WARN("Could not find port");
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*port=0;
|
||||
}
|
||||
char* pathPtr = strchr(hostPtr, '/');
|
||||
if( hostLen == 0 )
|
||||
{
|
||||
hostLen = pathPtr - hostPtr;
|
||||
}
|
||||
|
||||
if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
|
||||
{
|
||||
WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
memcpy(host, hostPtr, hostLen);
|
||||
host[hostLen] = '\0';
|
||||
|
||||
size_t pathLen;
|
||||
char* fragmentPtr = strchr(hostPtr, '#');
|
||||
if(fragmentPtr != NULL)
|
||||
{
|
||||
pathLen = fragmentPtr - pathPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathLen = strlen(pathPtr);
|
||||
}
|
||||
|
||||
if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
|
||||
{
|
||||
WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
|
||||
return HTTP_PARSE;
|
||||
}
|
||||
memcpy(path, pathPtr, pathLen);
|
||||
path[pathLen] = '\0';
|
||||
|
||||
return HTTP_OK;
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/* HTTPClient.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
HTTP Client header file
|
||||
*/
|
||||
|
||||
#ifndef HTTP_CLIENT_H
|
||||
#define HTTP_CLIENT_H
|
||||
|
||||
#include "TCPSocketConnection.h"
|
||||
|
||||
#define HTTP_CLIENT_DEFAULT_TIMEOUT 15000
|
||||
|
||||
class HTTPData;
|
||||
|
||||
#include "IHTTPData.h"
|
||||
#include "mbed.h"
|
||||
|
||||
///HTTP client results
|
||||
enum HTTPResult
|
||||
{
|
||||
HTTP_PROCESSING, ///<Processing
|
||||
HTTP_PARSE, ///<url Parse error
|
||||
HTTP_DNS, ///<Could not resolve name
|
||||
HTTP_PRTCL, ///<Protocol error
|
||||
HTTP_NOTFOUND, ///<HTTP 404 Error
|
||||
HTTP_REFUSED, ///<HTTP 403 Error
|
||||
HTTP_ERROR, ///<HTTP xxx error
|
||||
HTTP_TIMEOUT, ///<Connection timeout
|
||||
HTTP_CONN, ///<Connection error
|
||||
HTTP_CLOSED, ///<Connection was closed by remote host
|
||||
HTTP_OK = 0, ///<Success
|
||||
};
|
||||
|
||||
/**A simple HTTP Client
|
||||
The HTTPClient is composed of:
|
||||
- The actual client (HTTPClient)
|
||||
- Classes that act as a data repository, each of which deriving from the HTTPData class (HTTPText for short text content, HTTPFile for file I/O, HTTPMap for key/value pairs, and HTTPStream for streaming purposes)
|
||||
*/
|
||||
class HTTPClient
|
||||
{
|
||||
public:
|
||||
///Instantiate the HTTP client
|
||||
HTTPClient();
|
||||
~HTTPClient();
|
||||
|
||||
#if 0 //TODO add header handlers
|
||||
/**
|
||||
Provides a basic authentification feature (Base64 encoded username and password)
|
||||
Pass two NULL pointers to switch back to no authentication
|
||||
@param user username to use for authentication, must remain valid durlng the whole HTTP session
|
||||
@param user password to use for authentication, must remain valid durlng the whole HTTP session
|
||||
*/
|
||||
void basicAuth(const char* user, const char* password); //Basic Authentification
|
||||
#endif
|
||||
|
||||
//High Level setup functions
|
||||
/** Execute a GET request on the url
|
||||
Blocks until completion
|
||||
@param url : url on which to execute the request
|
||||
@param pDataIn : pointer to an IHTTPDataIn instance that will collect the data returned by the request, can be NULL
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult get(const char* url, IHTTPDataIn* pDataIn, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Execute a GET request on the url
|
||||
Blocks until completion
|
||||
This is a helper to directly get a piece of text from a HTTP result
|
||||
@param url : url on which to execute the request
|
||||
@param result : pointer to a char array in which the result will be stored
|
||||
@param maxResultLen : length of the char array (including space for the NULL-terminating char)
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult get(const char* url, char* result, size_t maxResultLen, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Execute a POST request on the url
|
||||
Blocks until completion
|
||||
@param url : url on which to execute the request
|
||||
@param dataOut : a IHTTPDataOut instance that contains the data that will be posted
|
||||
@param pDataIn : pointer to an IHTTPDataIn instance that will collect the data returned by the request, can be NULL
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, HTTP error (<0) on failure
|
||||
*/
|
||||
HTTPResult post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout = HTTP_CLIENT_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
/** Get last request's HTTP response code
|
||||
@return The HTTP response code of the last request
|
||||
*/
|
||||
int getHTTPResponseCode();
|
||||
|
||||
private:
|
||||
enum HTTP_METH
|
||||
{
|
||||
HTTP_GET,
|
||||
HTTP_POST,
|
||||
HTTP_HEAD
|
||||
};
|
||||
|
||||
HTTPResult connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout); //Execute request
|
||||
HTTPResult recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen); //0 on success, err code on failure
|
||||
HTTPResult send(char* buf, size_t len = 0); //0 on success, err code on failure
|
||||
HTTPResult parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen); //Parse URL
|
||||
|
||||
//Parameters
|
||||
TCPSocketConnection m_sock;
|
||||
|
||||
int m_timeout;
|
||||
|
||||
const char* m_basicAuthUser;
|
||||
const char* m_basicAuthPassword;
|
||||
int m_httpResponseCode;
|
||||
|
||||
};
|
||||
|
||||
//Including data containers here for more convenience
|
||||
#include "data/HTTPText.h"
|
||||
#include "data/HTTPMap.h"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
/* IHTTPData.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef IHTTPDATA_H
|
||||
#define IHTTPDATA_H
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using std::size_t;
|
||||
|
||||
///This is a simple interface for HTTP data storage (impl examples are Key/Value Pairs, File, etc...)
|
||||
class IHTTPDataOut
|
||||
{
|
||||
protected:
|
||||
friend class HTTPClient;
|
||||
|
||||
/** Read a piece of data to be transmitted
|
||||
* @param buf Pointer to the buffer on which to copy the data
|
||||
* @param len Length of the buffer
|
||||
* @param pReadLen Pointer to the variable on which the actual copied data length will be stored
|
||||
*/
|
||||
virtual int read(char* buf, size_t len, size_t* pReadLen) = 0;
|
||||
|
||||
/** Get MIME type
|
||||
* @param type Internet media type from Content-Type header
|
||||
*/
|
||||
virtual int getDataType(char* type, size_t maxTypeLen) = 0; //Internet media type for Content-Type header
|
||||
|
||||
/** Determine whether the HTTP client should chunk the data
|
||||
* Used for Transfer-Encoding header
|
||||
*/
|
||||
virtual bool getIsChunked() = 0;
|
||||
|
||||
/** If the data is not chunked, get its size
|
||||
* Used for Content-Length header
|
||||
*/
|
||||
virtual size_t getDataLen() = 0;
|
||||
|
||||
};
|
||||
|
||||
///This is a simple interface for HTTP data storage (impl examples are Key/Value Pairs, File, etc...)
|
||||
class IHTTPDataIn
|
||||
{
|
||||
protected:
|
||||
friend class HTTPClient;
|
||||
|
||||
/** Write a piece of data transmitted by the server
|
||||
* @param buf Pointer to the buffer from which to copy the data
|
||||
* @param len Length of the buffer
|
||||
*/
|
||||
virtual int write(const char* buf, size_t len) = 0;
|
||||
|
||||
/** Set MIME type
|
||||
* @param type Internet media type from Content-Type header
|
||||
*/
|
||||
virtual void setDataType(const char* type) = 0;
|
||||
|
||||
/** Determine whether the data is chunked
|
||||
* Recovered from Transfer-Encoding header
|
||||
*/
|
||||
virtual void setIsChunked(bool chunked) = 0;
|
||||
|
||||
/** If the data is not chunked, set its size
|
||||
* From Content-Length header
|
||||
*/
|
||||
virtual void setDataLen(size_t len) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,196 @@
|
|||
/* HTTPMap.cpp */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HTTPMap.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#define OK 0
|
||||
|
||||
using std::strncpy;
|
||||
|
||||
HTTPMap::HTTPMap() : m_pos(0), m_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HTTPMap::put(const char* key, const char* value)
|
||||
{
|
||||
if(m_count >= HTTPMAP_TABLE_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_keys[m_count] = key;
|
||||
m_values[m_count] = value;
|
||||
m_count++;
|
||||
}
|
||||
|
||||
void HTTPMap::clear()
|
||||
{
|
||||
m_count = 0;
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
|
||||
/*virtual*/ int HTTPMap::read(char* buf, size_t len, size_t* pReadLen)
|
||||
{
|
||||
if(m_pos >= m_count)
|
||||
{
|
||||
*pReadLen = 0;
|
||||
m_pos = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
//URL encode
|
||||
char* out = buf;
|
||||
const char* in = m_keys[m_pos];
|
||||
if( (m_pos != 0) && (out - buf < len - 1) )
|
||||
{
|
||||
*out='&';
|
||||
out++;
|
||||
}
|
||||
|
||||
while( (*in != '\0') && (out - buf < len - 3) )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
*out = *in;
|
||||
out++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
*out='+';
|
||||
out++;
|
||||
}
|
||||
else
|
||||
{
|
||||
char hex[] = "0123456789abcdef";
|
||||
*out='%';
|
||||
out++;
|
||||
*out=hex[(*in>>4)&0xf];
|
||||
out++;
|
||||
*out=hex[(*in)&0xf];
|
||||
out++;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
|
||||
if( out - buf < len - 1 )
|
||||
{
|
||||
*out='=';
|
||||
out++;
|
||||
}
|
||||
|
||||
in = m_values[m_pos];
|
||||
while( (*in != '\0') && (out - buf < len - 3) )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
*out = *in;
|
||||
out++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
*out='+';
|
||||
out++;
|
||||
}
|
||||
else
|
||||
{
|
||||
char hex[] = "0123456789abcdef";
|
||||
*out='%';
|
||||
out++;
|
||||
*out=hex[(*in>>4)&0xf];
|
||||
out++;
|
||||
*out=hex[(*in)&0xf];
|
||||
out++;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
|
||||
*pReadLen = out - buf;
|
||||
|
||||
m_pos++;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ int HTTPMap::getDataType(char* type, size_t maxTypeLen) //Internet media type for Content-Type header
|
||||
{
|
||||
strncpy(type, "application/x-www-form-urlencoded", maxTypeLen-1);
|
||||
type[maxTypeLen-1] = '\0';
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ bool HTTPMap::getIsChunked() //For Transfer-Encoding header
|
||||
{
|
||||
return false; ////Data is computed one key/value pair at a time
|
||||
}
|
||||
|
||||
/*virtual*/ size_t HTTPMap::getDataLen() //For Content-Length header
|
||||
{
|
||||
size_t count = 0;
|
||||
for(size_t i = 0; i< m_count; i++)
|
||||
{
|
||||
//URL encode
|
||||
const char* in = m_keys[i];
|
||||
if( i != 0 )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
while( (*in != '\0') )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
count+=3;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
|
||||
count ++;
|
||||
|
||||
in = m_values[i];
|
||||
while( (*in != '\0') )
|
||||
{
|
||||
if (std::isalnum(*in) || *in == '-' || *in == '_' || *in == '.' || *in == '~')
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else if( *in == ' ' )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
count+=3;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* HTTPMap.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HTTPMAP_H_
|
||||
#define HTTPMAP_H_
|
||||
|
||||
#include "../IHTTPData.h"
|
||||
|
||||
#define HTTPMAP_TABLE_SIZE 32
|
||||
|
||||
/** Map of key/value pairs
|
||||
* Used to transmit POST data using the application/x-www-form-urlencoded encoding
|
||||
*/
|
||||
class HTTPMap: public IHTTPDataOut
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Instantiates HTTPMap
|
||||
It supports at most 32 key/values pairs
|
||||
*/
|
||||
HTTPMap();
|
||||
|
||||
/** Put Key/Value pair
|
||||
The references to the parameters must remain valid as long as the clear() function is not called
|
||||
@param key The key to use
|
||||
@param value The corresponding value
|
||||
*/
|
||||
void put(const char* key, const char* value);
|
||||
|
||||
/** Clear table
|
||||
*/
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
//IHTTPDataIn
|
||||
virtual int read(char* buf, size_t len, size_t* pReadLen);
|
||||
|
||||
virtual int getDataType(char* type, size_t maxTypeLen); //Internet media type for Content-Type header
|
||||
|
||||
virtual bool getIsChunked(); //For Transfer-Encoding header
|
||||
|
||||
virtual size_t getDataLen(); //For Content-Length header
|
||||
|
||||
private:
|
||||
const char* m_keys[HTTPMAP_TABLE_SIZE];
|
||||
const char* m_values[HTTPMAP_TABLE_SIZE];
|
||||
|
||||
size_t m_pos;
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
#endif /* HTTPMAP_H_ */
|
|
@ -0,0 +1,94 @@
|
|||
/* HTTPText.cpp */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "HTTPText.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#define OK 0
|
||||
|
||||
using std::memcpy;
|
||||
using std::strncpy;
|
||||
using std::strlen;
|
||||
|
||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||
|
||||
HTTPText::HTTPText(char* str) : m_str(str), m_pos(0)
|
||||
{
|
||||
m_size = strlen(str) + 1;
|
||||
}
|
||||
|
||||
HTTPText::HTTPText(char* str, size_t size) : m_str(str), m_size(size), m_pos(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//IHTTPDataIn
|
||||
/*virtual*/ int HTTPText::read(char* buf, size_t len, size_t* pReadLen)
|
||||
{
|
||||
*pReadLen = MIN(len, m_size - 1 - m_pos);
|
||||
memcpy(buf, m_str + m_pos, *pReadLen);
|
||||
m_pos += *pReadLen;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ int HTTPText::getDataType(char* type, size_t maxTypeLen) //Internet media type for Content-Type header
|
||||
{
|
||||
strncpy(type, "text/plain", maxTypeLen-1);
|
||||
type[maxTypeLen-1] = '\0';
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ bool HTTPText::getIsChunked() //For Transfer-Encoding header
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*virtual*/ size_t HTTPText::getDataLen() //For Content-Length header
|
||||
{
|
||||
return m_size - 1;
|
||||
}
|
||||
|
||||
//IHTTPDataOut
|
||||
/*virtual*/ int HTTPText::write(const char* buf, size_t len)
|
||||
{
|
||||
size_t writeLen = MIN(len, m_size - 1 - m_pos);
|
||||
memcpy(m_str + m_pos, buf, writeLen);
|
||||
m_pos += writeLen;
|
||||
m_str[m_pos] = '\0';
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*virtual*/ void HTTPText::setDataType(const char* type) //Internet media type from Content-Type header
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*virtual*/ void HTTPText::setIsChunked(bool chunked) //From Transfer-Encoding header
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*virtual*/ void HTTPText::setDataLen(size_t len) //From Content-Length header, or if the transfer is chunked, next chunk length
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/* HTTPText.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HTTPTEXT_H_
|
||||
#define HTTPTEXT_H_
|
||||
|
||||
#include "../IHTTPData.h"
|
||||
|
||||
/** A data endpoint to store text
|
||||
*/
|
||||
class HTTPText : public IHTTPDataIn, public IHTTPDataOut
|
||||
{
|
||||
public:
|
||||
/** Create an HTTPText instance for output
|
||||
* @param str String to be transmitted
|
||||
*/
|
||||
HTTPText(char* str);
|
||||
|
||||
/** Create an HTTPText instance for input
|
||||
* @param str Buffer to store the incoming string
|
||||
* @param size Size of the buffer
|
||||
*/
|
||||
HTTPText(char* str, size_t size);
|
||||
|
||||
protected:
|
||||
//IHTTPDataIn
|
||||
virtual int read(char* buf, size_t len, size_t* pReadLen);
|
||||
|
||||
virtual int getDataType(char* type, size_t maxTypeLen); //Internet media type for Content-Type header
|
||||
|
||||
virtual bool getIsChunked(); //For Transfer-Encoding header
|
||||
|
||||
virtual size_t getDataLen(); //For Content-Length header
|
||||
|
||||
//IHTTPDataOut
|
||||
virtual int write(const char* buf, size_t len);
|
||||
|
||||
virtual void setDataType(const char* type); //Internet media type from Content-Type header
|
||||
|
||||
virtual void setIsChunked(bool chunked); //From Transfer-Encoding header
|
||||
|
||||
virtual void setDataLen(size_t len); //From Content-Length header, or if the transfer is chunked, next chunk length
|
||||
|
||||
private:
|
||||
char* m_str;
|
||||
size_t m_size;
|
||||
|
||||
size_t m_pos;
|
||||
};
|
||||
|
||||
#endif /* HTTPTEXT_H_ */
|
|
@ -0,0 +1,67 @@
|
|||
#include "mbed.h"
|
||||
#include "test_env.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include "HTTPClient.h"
|
||||
|
||||
|
||||
namespace {
|
||||
const int BUFFER_SIZE = 512;
|
||||
}
|
||||
|
||||
int main() {
|
||||
MBED_HOSTTEST_TIMEOUT(15);
|
||||
MBED_HOSTTEST_SELECT(default_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(HTTP client hello world);
|
||||
MBED_HOSTTEST_START("NET_7");
|
||||
|
||||
char http_request_buffer[BUFFER_SIZE + 1] = {0};
|
||||
HTTPClient http;
|
||||
EthernetInterface eth;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
//GET data
|
||||
{
|
||||
bool result = true;
|
||||
const char *url_hello_txt = "http://developer.mbed.org/media/uploads/donatien/hello.txt";
|
||||
printf("HTTP_GET: Trying to fetch page '%s'...\r\n", url_hello_txt);
|
||||
HTTPResult ret = http.get(url_hello_txt, http_request_buffer, BUFFER_SIZE);
|
||||
if (ret == HTTP_OK) {
|
||||
printf("HTTP_GET: Read %d chars: '%s' ... [OK]\r\n", strlen(http_request_buffer), http_request_buffer);
|
||||
} else {
|
||||
printf("HTTP_GET: Error(%d). HTTP error(%d) ... [FAIL]\r\n", ret, http.getHTTPResponseCode());
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (result == false) {
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(false);
|
||||
}
|
||||
}
|
||||
|
||||
//POST data
|
||||
{
|
||||
bool result = true;
|
||||
const char *url_httpbin_post = "http://httpbin.org/post";
|
||||
HTTPText text(http_request_buffer, BUFFER_SIZE);
|
||||
HTTPMap map;
|
||||
map.put("Hello", "World");
|
||||
map.put("test", "1234");
|
||||
printf("HTTP_POST: Trying to post data to '%s' ...\r\n", url_httpbin_post);
|
||||
HTTPResult ret = http.post(url_httpbin_post, map, &text);
|
||||
if (ret == HTTP_OK) {
|
||||
printf("HTTP_POST: Read %d chars ... [OK]\r\n", strlen(http_request_buffer));
|
||||
printf("HTTP_POST: %s\r\n", http_request_buffer);
|
||||
} else {
|
||||
printf("HTTP_GET: Error(%d). HTTP error(%d) ... [FAIL]\r\n", ret, http.getHTTPResponseCode());
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (result == false) {
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(false);
|
||||
}
|
||||
}
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(true);
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/* NTPClient.cpp */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//Debug is disabled by default
|
||||
#if 0
|
||||
//Enable debug
|
||||
#define __DEBUG__
|
||||
#include <cstdio>
|
||||
#define DBG(x, ...) std::printf("[NTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
|
||||
#define WARN(x, ...) std::printf("[NTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
|
||||
#define ERR(x, ...) std::printf("[NTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
|
||||
|
||||
#else
|
||||
//Disable debug
|
||||
#define DBG(x, ...)
|
||||
#define WARN(x, ...)
|
||||
#define ERR(x, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#include "NTPClient.h"
|
||||
|
||||
#include "UDPSocket.h"
|
||||
|
||||
#include "mbed.h" //time() and set_time()
|
||||
|
||||
#define NTP_PORT 123
|
||||
#define NTP_CLIENT_PORT 0 //Random port
|
||||
#define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
|
||||
|
||||
NTPClient::NTPClient() : m_sock()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
NTPResult NTPClient::setTime(const char* host, uint16_t port, uint32_t timeout)
|
||||
{
|
||||
#ifdef __DEBUG__
|
||||
time_t ctTime;
|
||||
ctTime = time(NULL);
|
||||
DBG("Time is set to (UTC): %s", ctime(&ctTime));
|
||||
#endif
|
||||
|
||||
//Create & bind socket
|
||||
DBG("Binding socket");
|
||||
m_sock.bind(0); //Bind to a random port
|
||||
|
||||
m_sock.set_blocking(false, timeout); //Set not blocking
|
||||
|
||||
struct NTPPacket pkt;
|
||||
|
||||
//Now ping the server and wait for response
|
||||
DBG("Ping");
|
||||
//Prepare NTP Packet:
|
||||
pkt.li = 0; //Leap Indicator : No warning
|
||||
pkt.vn = 4; //Version Number : 4
|
||||
pkt.mode = 3; //Client mode
|
||||
pkt.stratum = 0; //Not relevant here
|
||||
pkt.poll = 0; //Not significant as well
|
||||
pkt.precision = 0; //Neither this one is
|
||||
|
||||
pkt.rootDelay = 0; //Or this one
|
||||
pkt.rootDispersion = 0; //Or that one
|
||||
pkt.refId = 0; //...
|
||||
|
||||
pkt.refTm_s = 0;
|
||||
pkt.origTm_s = 0;
|
||||
pkt.rxTm_s = 0;
|
||||
pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
|
||||
|
||||
pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
|
||||
|
||||
Endpoint outEndpoint;
|
||||
|
||||
if( outEndpoint.set_address(host, port) < 0)
|
||||
{
|
||||
m_sock.close();
|
||||
return NTP_DNS;
|
||||
}
|
||||
|
||||
//Set timeout, non-blocking and wait using select
|
||||
int ret = m_sock.sendTo( outEndpoint, (char*)&pkt, sizeof(NTPPacket) );
|
||||
if (ret < 0 )
|
||||
{
|
||||
ERR("Could not send packet");
|
||||
m_sock.close();
|
||||
return NTP_CONN;
|
||||
}
|
||||
|
||||
//Read response
|
||||
Endpoint inEndpoint;
|
||||
|
||||
DBG("Pong");
|
||||
do
|
||||
{
|
||||
ret = m_sock.receiveFrom( inEndpoint, (char*)&pkt, sizeof(NTPPacket) ); //FIXME need a DNS Resolver to actually compare the incoming address with the DNS name
|
||||
if(ret < 0)
|
||||
{
|
||||
ERR("Could not receive packet");
|
||||
m_sock.close();
|
||||
return NTP_CONN;
|
||||
}
|
||||
} while( strcmp(outEndpoint.get_address(), inEndpoint.get_address()) != 0 );
|
||||
|
||||
if(ret < sizeof(NTPPacket)) //TODO: Accept chunks
|
||||
{
|
||||
ERR("Receive packet size does not match");
|
||||
m_sock.close();
|
||||
return NTP_PRTCL;
|
||||
}
|
||||
|
||||
if( pkt.stratum == 0) //Kiss of death message : Not good !
|
||||
{
|
||||
ERR("Kissed to death!");
|
||||
m_sock.close();
|
||||
return NTP_PRTCL;
|
||||
}
|
||||
|
||||
//Correct Endianness
|
||||
pkt.refTm_s = ntohl( pkt.refTm_s );
|
||||
pkt.refTm_f = ntohl( pkt.refTm_f );
|
||||
pkt.origTm_s = ntohl( pkt.origTm_s );
|
||||
pkt.origTm_f = ntohl( pkt.origTm_f );
|
||||
pkt.rxTm_s = ntohl( pkt.rxTm_s );
|
||||
pkt.rxTm_f = ntohl( pkt.rxTm_f );
|
||||
pkt.txTm_s = ntohl( pkt.txTm_s );
|
||||
pkt.txTm_f = ntohl( pkt.txTm_f );
|
||||
|
||||
//Compute offset, see RFC 4330 p.13
|
||||
uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
|
||||
int64_t offset = ( (int64_t)( pkt.rxTm_s - pkt.origTm_s ) + (int64_t) ( pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
|
||||
DBG("Sent @%ul", pkt.txTm_s);
|
||||
DBG("Offset: %lld", offset);
|
||||
//Set time accordingly
|
||||
set_time( time(NULL) + offset );
|
||||
|
||||
#ifdef __DEBUG__
|
||||
ctTime = time(NULL);
|
||||
DBG("Time is now (UTC): %s", ctime(&ctTime));
|
||||
#endif
|
||||
|
||||
m_sock.close();
|
||||
|
||||
return NTP_OK;
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/* NTPClient.h */
|
||||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
NTP Client header file
|
||||
*/
|
||||
|
||||
#ifndef NTPCLIENT_H_
|
||||
#define NTPCLIENT_H_
|
||||
|
||||
#include "UDPSocket.h"
|
||||
|
||||
#define NTP_DEFAULT_PORT 123
|
||||
#define NTP_DEFAULT_TIMEOUT 4000
|
||||
|
||||
///NTP client results
|
||||
enum NTPResult
|
||||
{
|
||||
NTP_DNS, ///<Could not resolve name
|
||||
NTP_PRTCL, ///<Protocol error
|
||||
NTP_TIMEOUT, ///<Connection timeout
|
||||
NTP_CONN, ///<Connection error
|
||||
NTP_OK = 0, ///<Success
|
||||
};
|
||||
|
||||
/** NTP Client to update the mbed's RTC using a remote time server
|
||||
*
|
||||
*/
|
||||
class NTPClient
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Instantiate the NTP client
|
||||
*/
|
||||
NTPClient();
|
||||
|
||||
/**Get current time (blocking)
|
||||
Update the time using the server host
|
||||
Blocks until completion
|
||||
@param host NTP server IPv4 address or hostname (will be resolved via DNS)
|
||||
@param port port to use; defaults to 123
|
||||
@param timeout waiting timeout in ms (osWaitForever for blocking function, not recommended)
|
||||
@return 0 on success, NTP error code (<0) on failure
|
||||
*/
|
||||
NTPResult setTime(const char* host, uint16_t port = NTP_DEFAULT_PORT, uint32_t timeout = NTP_DEFAULT_TIMEOUT); //Blocking
|
||||
|
||||
private:
|
||||
#if defined (__ICCARM__)
|
||||
#pragma pack()
|
||||
#endif
|
||||
struct NTPPacket //See RFC 4330 for Simple NTP
|
||||
{
|
||||
//WARN: We are in LE! Network is BE!
|
||||
//LSb first
|
||||
unsigned mode : 3;
|
||||
unsigned vn : 3;
|
||||
unsigned li : 2;
|
||||
|
||||
uint8_t stratum;
|
||||
uint8_t poll;
|
||||
uint8_t precision;
|
||||
//32 bits header
|
||||
|
||||
uint32_t rootDelay;
|
||||
uint32_t rootDispersion;
|
||||
uint32_t refId;
|
||||
|
||||
uint32_t refTm_s;
|
||||
uint32_t refTm_f;
|
||||
uint32_t origTm_s;
|
||||
uint32_t origTm_f;
|
||||
uint32_t rxTm_s;
|
||||
uint32_t rxTm_f;
|
||||
uint32_t txTm_s;
|
||||
uint32_t txTm_f;
|
||||
#if defined (__ICCARM__)
|
||||
};
|
||||
#else
|
||||
} __attribute__ ((packed));
|
||||
#endif
|
||||
|
||||
UDPSocket m_sock;
|
||||
};
|
||||
|
||||
#endif /* NTPCLIENT_H_ */
|
|
@ -0,0 +1,39 @@
|
|||
#include "mbed.h"
|
||||
#include "test_env.h"
|
||||
#include "EthernetInterface.h"
|
||||
#include "NTPClient.h"
|
||||
|
||||
int main() {
|
||||
MBED_HOSTTEST_TIMEOUT(15);
|
||||
MBED_HOSTTEST_SELECT(default_auto);
|
||||
MBED_HOSTTEST_DESCRIPTION(NTP client);
|
||||
MBED_HOSTTEST_START("NET_8");
|
||||
|
||||
EthernetInterface eth;
|
||||
NTPClient ntp;
|
||||
eth.init(); //Use DHCP
|
||||
eth.connect();
|
||||
|
||||
// NTP set time
|
||||
{
|
||||
bool result = true;
|
||||
const char *url_ntp_server = "0.pool.ntp.org";
|
||||
printf("NTP_SETTIME: Trying to update time... \r\n");
|
||||
const int ret = ntp.setTime(url_ntp_server);
|
||||
if (ret == 0) {
|
||||
time_t ctTime = time(NULL);
|
||||
printf("NTP_SETTIME: UTC Time read successfully ... [OK]\r\n");
|
||||
printf("NTP_SETTIME: %s\r\n", ctime(&ctTime));
|
||||
}
|
||||
else {
|
||||
printf("NTP_SETTIME: Error(%d) ... [FAIL]\r\n", ret);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (result == false) {
|
||||
MBED_HOSTTEST_RESULT(false);
|
||||
}
|
||||
}
|
||||
eth.disconnect();
|
||||
MBED_HOSTTEST_RESULT(true);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue