A client for the SmartREST protocol from Cumulocity.
Fork of MbedSmartRest by
Revision 13:e76920d5e1ec, committed 2014-04-11
- Comitter:
- vwochnik
- Date:
- Fri Apr 11 09:33:45 2014 +0000
- Parent:
- 12:788dd934f283
- Child:
- 14:dc3f8dd5c02b
- Commit message:
- fix
Changed in this revision
--- a/HTTPBuffer.cpp Wed Apr 02 12:23:46 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -#include "HTTPBuffer.h" -#include <stdlib.h> -#include <string.h> - -HTTPBuffer::HTTPBuffer() -{ - _rptr = _wptr = _buf = NULL; - bufferSize(HTTPBUFFER_INITIAL_SIZE); -} - -HTTPBuffer::~HTTPBuffer() -{ - if (_buf != NULL) - free(_buf); -} - -char HTTPBuffer::read() -{ - if (_rptr == _wptr) - return 0; - return *_rptr++; -} - -uint8_t HTTPBuffer::status() -{ - if (_rptr == _wptr) - return DS_STATUS_CLOSED; - return DS_STATUS_OK; -} - -void HTTPBuffer::writeReset() -{ - _rptr = _wptr = _buf; - bufferSize(HTTPBUFFER_INITIAL_SIZE); -} - -int HTTPBuffer::write(const char* buf, size_t len) -{ - if (_wptr - _buf + len > _len) { - size_t newLen = _len; - while (_wptr - _buf + len > newLen) - newLen += HTTPBUFFER_INCREMENT; - bufferSize(newLen); - } - memcpy(_wptr, buf, len); - _wptr += len; -} - -void HTTPBuffer::setDataType(const char* type) -{ -} - -void HTTPBuffer::setIsChunked(bool chunked) -{ -} - -void HTTPBuffer::setDataLen(size_t len) -{ - bufferSize(len); -} - - -void HTTPBuffer::bufferSize(size_t length) -{ - if (_len == length) - return; - - char *buf = (char*)realloc(_buf, length); - if (buf == NULL) - return; - - // set pointers - _wptr = buf + (_wptr - _buf); - _rptr = buf + (_rptr - _buf); - _buf = buf; - - _len = length; -}
--- a/HTTPBuffer.h Wed Apr 02 12:23:46 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -#ifndef HTTPBUFFER_H -#define HTTPBUFFER_H - -#include <stddef.h> -#include "IHTTPData.h" -#include "AbstractDataSource.h" - -#define HTTPBUFFER_INITIAL_SIZE 128 -#define HTTPBUFFER_INCREMENT 64 -static int x = 123; -class HTTPBuffer : public IHTTPDataIn, public AbstractDataSource -{ -public: - HTTPBuffer(); - ~HTTPBuffer(); - - char read(); - uint8_t status(); - - void writeReset(); - -protected: - int write(const char* buf, size_t len); - void setDataType(const char* type); - void setIsChunked(bool chunked); - void setDataLen(size_t len); - void bufferSize(size_t length); - -//private: -public: - // buffer, write and read pointers - char *_buf, *_wptr, *_rptr; - size_t _len; -}; - -#endif \ No newline at end of file
--- a/HTTPClient.lib Wed Apr 02 12:23:46 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/vwochnik/code/HTTPClient/#9386b15b0820
--- a/HTTPGeneratorWrapper.cpp Wed Apr 02 12:23:46 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -#include "HTTPGeneratorWrapper.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -HTTPGeneratorWrapper::HTTPGeneratorWrapper(DataGenerator& generator) : _sink(generator.writtenLength()) -{ - generator.writeTo(_sink); - _len = _sink.length(); - _pos = 0; - - char *p = (char*)_sink.buffer(); - size_t i = 0; - while (i < _len) { - putchar(*p++); - i++; - } -} - -void HTTPGeneratorWrapper::readReset() -{ - _pos = 0; -} - -int HTTPGeneratorWrapper::read(char* buf, size_t len, size_t* pReadLen) -{ - *pReadLen = len; - if (len > _len - _pos) - *pReadLen = _len - _pos; - else - *pReadLen = len; - memcpy(buf, _sink.buffer()+_pos, *pReadLen); - _pos += *pReadLen; - return 0; -} - -int HTTPGeneratorWrapper::getDataType(char* type, size_t maxTypeLen) -{ - return 1; -} - -bool HTTPGeneratorWrapper::getIsChunked() -{ - return false; -} - -size_t HTTPGeneratorWrapper::getDataLen() -{ - return _len; -}
--- a/HTTPGeneratorWrapper.h Wed Apr 02 12:23:46 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -#ifndef HTTPGENERATORWRAPPER_H -#define HTTPGENERATORWRAPPER_H - -#include "DataGenerator.h" -#include "MbedDataSink.h" -#include "IHTTPData.h" - -class HTTPGeneratorWrapper : public IHTTPDataOut -{ -public: - HTTPGeneratorWrapper(DataGenerator& generator); - -protected: - void readReset(); - int read(char* buf, size_t len, size_t* pReadLen); - int getDataType(char* type, size_t maxTypeLen); - bool getIsChunked(); - size_t getDataLen(); - -private: - MbedDataSink _sink; - size_t _len; - size_t _pos; -}; - -#endif \ No newline at end of file
--- a/MbedClient.cpp Wed Apr 02 12:23:46 2014 +0000 +++ b/MbedClient.cpp Fri Apr 11 09:33:45 2014 +0000 @@ -1,100 +1,204 @@ #include "MbedClient.h" #include <stdlib.h> - #include <stdio.h> +#include <string.h> +#include "b64.h" +#include "mbed.h" -#define STATE_INIT 0 -#define STATE_IN_REQUEST 1 -#define STATE_SENT_ID 2 -#define STATE_SENT_DATA 3 -#define STATE_REQ_COMPLETE 4 -#define STATE_RECVD_RESPONSE 5 -#define STATE_RECV_DATA 6 +const char *cExpectedStatus = "HTTP/1.* 200 OK"; -const char * const cXidHeader = "X-Id"; - -MbedClient::MbedClient(const char* url, const char* username, const char* password) - : _url(url), _username(username), _password(password) +MbedClient::MbedClient(const char* host, uint16_t port, const char* username, const char* password) + : _host(host), _port(port), _username(username), _password(password), _source(_sock), _sink(_sock), _sock() { - _state = STATE_INIT; - _headers[0] = cXidHeader; - _headers[1] = NULL; + _state = MBED_STATE_INIT; } MbedClient::~MbedClient() { - if (_generator != NULL) - delete _generator; } uint8_t MbedClient::beginRequest() { - if (_state != STATE_INIT) + if (_state != MBED_STATE_INIT) return CLIENT_INTERNAL_ERROR; - _client.basicAuth(_username, _password); - _client.customHeaders(NULL, 0); - _state = STATE_IN_REQUEST; + + if (_sock.connect(_host, _port) < 0) { + stop(); + return CLIENT_CONNECTION_ERROR; + } + + if ((!send("POST /s HTTP/1.0\r\n")) || + (!send("Host: ")) || + (!send(_host)) || + (!send("\r\n"))) + return CLIENT_CONNECTION_ERROR; + + if (!sendBasicAuth()) + return CLIENT_CONNECTION_ERROR; + + _state = MBED_STATE_IN_REQUEST; return CLIENT_OK; } uint8_t MbedClient::sendIdentifier(const char* identifier) { - if (_state != STATE_IN_REQUEST) + if (_state != MBED_STATE_IN_REQUEST) return CLIENT_INTERNAL_ERROR; - _headers[1] = identifier; - _client.customHeaders(_headers, 1); - _state = STATE_SENT_ID; + + if ((!send("X-Id: ")) || + (!send(identifier)) || + (!send("\r\n"))) + return CLIENT_CONNECTION_ERROR; + + _state = MBED_STATE_SENT_ID; return CLIENT_OK; } uint8_t MbedClient::sendData(DataGenerator& generator) { - puts("Send called."); - if ((_state != STATE_IN_REQUEST) && (_state != STATE_SENT_ID)) + char len[8]; + + if ((_state != MBED_STATE_IN_REQUEST) && (_state != MBED_STATE_SENT_ID)) return CLIENT_INTERNAL_ERROR; - puts("Setting gen."); - _generator = new HTTPGeneratorWrapper(generator); - _state = STATE_SENT_DATA; + + snprintf(len, 8, "%ld", generator.writtenLength()); + + if ((!send("Content-Length: ")) || + (!send(len)) || + (!send("\r\n\r\n"))) + return CLIENT_CONNECTION_ERROR; + + if (generator.writeTo(_sink) != generator.writtenLength()) { + stop(); + return CLIENT_CONNECTION_ERROR; + } + + _state = MBED_STATE_SENT_DATA; return CLIENT_OK; } uint8_t MbedClient::endRequest() { - if ((_state != STATE_IN_REQUEST) && (_state != STATE_SENT_ID) && (_state != STATE_SENT_DATA)) + if ((_state != MBED_STATE_IN_REQUEST) && (_state != MBED_STATE_SENT_ID) && (_state != MBED_STATE_SENT_DATA)) return CLIENT_INTERNAL_ERROR; - _state = STATE_REQ_COMPLETE; + + if (_state != MBED_STATE_SENT_DATA) { + // send end of headers + if (!send("\r\n")) + return CLIENT_CONNECTION_ERROR; + } + + if (!_sink.flush()) { + stop(); + return CLIENT_CONNECTION_ERROR; + } + + _state = MBED_STATE_REQ_COMPLETE; return CLIENT_OK; } uint8_t MbedClient::awaitResponse() { - HTTPResult result; + int8_t state = 0; char c; size_t offset = 0; - puts("Action"); - if (_state != STATE_REQ_COMPLETE) + if (_state != MBED_STATE_REQ_COMPLETE) return CLIENT_INTERNAL_ERROR; - puts("Calling"); - result = _client.post(_url, *_generator, &_buffer); - if (result != 0) + + while ((state >= 0) && (state < 20) && (((c = _source.read()) > 0) || (_source.status() == DS_STATUS_OK))) { + switch (state) { + case 0: // read expected status line + if ((cExpectedStatus[offset] != c) && (cExpectedStatus[offset] != '*')) + state = -1; + offset++; + if (offset == strlen(cExpectedStatus)) + state = 1; + break; + linebrk: + case 1: + if (c == '\n') + state = 2; + else if (c != '\r') + state = -1; + break; + case 2: + if (c == '\n') { + state = 20; + break; + } else if (c == '\r') { + break; + } else { + state = 3; + goto random; + } + random: + case 3: + if ((c == '\r') || (c == '\n')) { + state = 1; + goto linebrk; + } + } + } + + if (state != 20) { + stop(); return CLIENT_CONNECTION_ERROR; - char *p = _buffer._buf; - while (p != _buffer._wptr) - putchar(*p++); - _state = STATE_RECVD_RESPONSE; + } + + _state = MBED_STATE_RECVD_RESPONSE; return CLIENT_OK; } AbstractDataSource& MbedClient::receiveData() { - return _buffer; + return _source; } void MbedClient::stop() { - _buffer.writeReset(); - _headers[1] = NULL; - if (_generator != NULL) - delete _generator; - _generator = NULL; - _state = STATE_INIT; + _sock.close(); + _source.reset(); + _sink.reset(); + _state = MBED_STATE_INIT; +} + +bool MbedClient::send(const char *str) +{ + if (_sink.write(str) != strlen(str)) { + stop(); + return false; + } + return true; } + +bool MbedClient::sendBasicAuth() +{ + size_t ul, pl; unsigned char input[3]; unsigned char output[5]; + int inputOffset = 0; + + if (!send("Authorization: Basic ")) + return false; + + ul = strlen(_username); + pl = strlen(_password); + + for (int i = 0; i < (ul+1+pl); i++) { + if (i < ul) + input[inputOffset++] = _username[i]; + else if (i == ul) + input[inputOffset++] = ':'; + else + input[inputOffset++] = _password[i-(ul+1)]; + + if ((inputOffset == 3) || (i == ul+pl)) { + b64_encode(input, inputOffset, output, 4); + output[4] = '\0'; + if (!send((char*)output)) + return false; + inputOffset = 0; + } + } + + if (!send("\r\n")) + return false; + return true; +} \ No newline at end of file
--- a/MbedClient.h Wed Apr 02 12:23:46 2014 +0000 +++ b/MbedClient.h Fri Apr 11 09:33:45 2014 +0000 @@ -1,14 +1,23 @@ #ifndef MBEDCLIENT_H #define MBEDCLIENT_H +#include <stdint.h> #include "AbstractClient.h" -#include "HTTPBuffer.h" -#include "HTTPGeneratorWrapper.h" -#include "HTTPClient.h" +#include "TCPSocketConnection.h" +#include "MbedDataSource.h" +#include "MbedDataSink.h" + +#define MBED_STATE_INIT 0 +#define MBED_STATE_IN_REQUEST 1 +#define MBED_STATE_SENT_ID 2 +#define MBED_STATE_SENT_DATA 3 +#define MBED_STATE_REQ_COMPLETE 4 +#define MBED_STATE_RECVD_RESPONSE 5 +#define MBED_STATE_RECV_DATA 6 class MbedClient : public AbstractClient { public: - MbedClient(const char*, const char*, const char*); + MbedClient(const char*, uint16_t, const char*, const char*); ~MbedClient(); uint8_t beginRequest(); @@ -19,13 +28,16 @@ AbstractDataSource& receiveData(); void stop(); +protected: + bool send(const char *str); + bool sendBasicAuth(); + private: - const char *_url, *_username, *_password; - HTTPClient _client; - HTTPGeneratorWrapper *_generator; - HTTPBuffer _buffer; - uint8_t _state; - const char *_headers[2]; + const char *_host, *_username, *_password; + uint16_t _port, _state; + TCPSocketConnection _sock; + MbedDataSource _source; + MbedDataSink _sink; }; #endif \ No newline at end of file
--- a/MbedDataSink.cpp Wed Apr 02 12:23:46 2014 +0000 +++ b/MbedDataSink.cpp Fri Apr 11 09:33:45 2014 +0000 @@ -1,65 +1,104 @@ #include "MbedDataSink.h" +#include "MbedClient.h" #include <stdlib.h> #include <stdio.h> +#include <string.h> -MbedDataSink::MbedDataSink(size_t length) +#include "mbed.h" + +MbedDataSink::MbedDataSink(TCPSocketConnection& sock) : _len(0), _sock(sock) { - _len = length; - _ptr = _buf = (char*)malloc(length); } MbedDataSink::~MbedDataSink() { - free(_buf); } size_t MbedDataSink::write(char c) { - if (_ptr - _buf == _len) - return 0; - *_ptr++ = c; + while (MBED_SINK_BUFFER_SIZE - _len < 1) { + if (!send()) + return 0; + } + + putchar(c); + _buf[_len++] = c; return 1; } size_t MbedDataSink::write(void *buf, size_t length) { - size_t written = 0; - char *b = (char*)buf; + size_t sent = 0, len; - while ((length-- > 0) && (write(*b++))) - written++; - - return written; + while (sent < length) { + while (MBED_SINK_BUFFER_SIZE - _len < 1) { + if (!send()) + return 0; + } + + if (MBED_SINK_BUFFER_SIZE - _len >= length-sent) + len = length-sent; + else + len = MBED_SINK_BUFFER_SIZE - _len; + + memcpy(_buf+_len, (char*)buf+sent, len); + _len += len; + sent += len; + } + + return length; } size_t MbedDataSink::write(const char *str) { - size_t written = 0; - - for (char c, *p = (char*)str; (c = *p) > 0; ++p) { - if (!write(c)) - break; - written++; - } - return written; + return write((void*)str, strlen(str)); } size_t MbedDataSink::write(unsigned long number) { - size_t left, len; + char str[24]; - left = _len - (_ptr - _buf); - len = snprintf(_ptr, left, "%ld", number); - _ptr += len; - return len; + snprintf(str, 24, "%uld", number); + return write(str); +} + +bool MbedDataSink::flush() +{ + while (_len > 0) { + if (!send()) + return false; + } + + return true; } -const char * MbedDataSink::buffer() +bool MbedDataSink::send() { - return _buf; + int ret; + + if (!_sock.is_connected()) + return false; + + _sock.set_blocking(true); + ret = _sock.send(_buf, _len); + + //TODO: fix bug in u-blox so no longer wait is needed + wait(0.5); + + if (ret < 0){ + puts("Send failed."); + return false; + } + + // move rest of buffer + if ((ret > 0) && (ret < _len)) + memmove(_buf, _buf+ret, _len-ret); + + _len -= ret; + return true; } -size_t MbedDataSink::length() +void MbedDataSink::reset() { - return _ptr - _buf; -} + _len = 0; +} \ No newline at end of file
--- a/MbedDataSink.h Wed Apr 02 12:23:46 2014 +0000 +++ b/MbedDataSink.h Fri Apr 11 09:33:45 2014 +0000 @@ -3,13 +3,16 @@ #include <stddef.h> #include "AbstractDataSink.h" +#include "TCPSocketConnection.h" -class HTTPGeneratorWrapper; +#define MBED_SINK_BUFFER_SIZE 60 + +class MbedClient; class MbedDataSink : public AbstractDataSink { public: - MbedDataSink(size_t length); + MbedDataSink(TCPSocketConnection& sock); ~MbedDataSink(); size_t write(char c); @@ -17,15 +20,18 @@ size_t write(const char *str); size_t write(unsigned long number); + protected: - const char * buffer(); - size_t length(); + bool flush(); + bool send(); + void reset(); private: - char *_buf, *_ptr; + TCPSocketConnection& _sock; + char _buf[MBED_SINK_BUFFER_SIZE]; size_t _len; -friend class HTTPGeneratorWrapper; +friend class MbedClient; }; #endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MbedDataSource.cpp Fri Apr 11 09:33:45 2014 +0000 @@ -0,0 +1,60 @@ +#include "MbedDataSource.h" +#include "stdio.h" + +MbedDataSource::MbedDataSource(TCPSocketConnection& sock) : _sock(sock) +{ + _offset = _len = 0; + _timeout = false; +} + +MbedDataSource::~MbedDataSource() +{ +} + +char MbedDataSource::read() +{ + while (_offset == _len) { + if (!receive()) + return 0; + } + + return _buf[_offset++]; +} + +uint8_t MbedDataSource::status() +{ + if (!_sock.is_connected()) + return DS_STATUS_CLOSED; + + if (_timeout) + return DS_STATUS_TIMEOUT; + + return DS_STATUS_OK; +} + +bool MbedDataSource::receive() +{ + int ret; + + if (status() != DS_STATUS_OK) + return false; + + _sock.set_blocking(true, 60000); + ret = _sock.receive(_buf, MBED_SOURCE_BUFFER_SIZE); + + if (ret < 0) { + _timeout = true; + return false; + } + + _len = (size_t)ret; + _offset = 0; + + return true; +} + +void MbedDataSource::reset() +{ + _len = _offset = 0; + _timeout = false; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MbedDataSource.h Fri Apr 11 09:33:45 2014 +0000 @@ -0,0 +1,34 @@ +#ifndef MBEDDATASOURCE_H +#define MBEDDATASOURCE_H + +#include <stddef.h> +#include "AbstractDataSource.h" +#include "TCPSocketConnection.h" + +#define MBED_SOURCE_BUFFER_SIZE 60 + +class MbedClient; + +class MbedDataSource : public AbstractDataSource +{ +public: + MbedDataSource(TCPSocketConnection& sock); + ~MbedDataSource(); + + char read(); + uint8_t status(); + +protected: + bool receive(); + void reset(); + +private: + TCPSocketConnection& _sock; + char _buf[MBED_SOURCE_BUFFER_SIZE]; + size_t _len, _offset; + bool _timeout; + +friend class MbedClient; +}; + +#endif
--- a/MbedSmartRest.cpp Wed Apr 02 12:23:46 2014 +0000 +++ b/MbedSmartRest.cpp Fri Apr 11 09:33:45 2014 +0000 @@ -1,5 +1,5 @@ #include "MbedSmartRest.h" -MbedSmartRest::MbedSmartRest(const char* url, const char* username, const char* password, const char* identifier) : SmartRest(_client, identifier), _client(url, username, password) +MbedSmartRest::MbedSmartRest(const char* host, uint16_t port, const char* username, const char* password, const char* identifier) : SmartRest(_client, identifier), _client(host, port, username, password) { } \ No newline at end of file
--- a/MbedSmartRest.h Wed Apr 02 12:23:46 2014 +0000 +++ b/MbedSmartRest.h Fri Apr 11 09:33:45 2014 +0000 @@ -7,7 +7,7 @@ class MbedSmartRest : public SmartRest { public: - MbedSmartRest(const char*, const char*, const char*, const char*); + MbedSmartRest(const char*, uint16_t, const char*, const char*, const char*); private: MbedClient _client;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/b64.cpp Fri Apr 11 09:33:45 2014 +0000 @@ -0,0 +1,70 @@ +// Simple Base64 code +// (c) Copyright 2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#include "b64.h" + +/* Simple test program +#include <stdio.h> +void main() +{ + char* in = "amcewen"; + char out[22]; + + b64_encode(in, 15, out, 22); + out[21] = '\0'; + + printf(out); +} +*/ + +int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen) +{ + // Work out if we've got enough space to encode the input + // Every 6 bits of input becomes a byte of output + if (aOutputLen < (aInputLen*8)/6) + { + // FIXME Should we return an error here, or just the length + return (aInputLen*8)/6; + } + + // If we get here we've got enough space to do the encoding + + const char* b64_dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (aInputLen == 3) + { + aOutput[0] = b64_dictionary[aInput[0] >> 2]; + aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)]; + aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2|(aInput[2]>>6)]; + aOutput[3] = b64_dictionary[aInput[2]&0x3F]; + } + else if (aInputLen == 2) + { + aOutput[0] = b64_dictionary[aInput[0] >> 2]; + aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)]; + aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2]; + aOutput[3] = '='; + } + else if (aInputLen == 1) + { + aOutput[0] = b64_dictionary[aInput[0] >> 2]; + aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4]; + aOutput[2] = '='; + aOutput[3] = '='; + } + else + { + // Break the input into 3-byte chunks and process each of them + int i; + for (i = 0; i < aInputLen/3; i++) + { + b64_encode(&aInput[i*3], 3, &aOutput[i*4], 4); + } + if (aInputLen % 3 > 0) + { + // It doesn't fit neatly into a 3-byte chunk, so process what's left + b64_encode(&aInput[i*3], aInputLen % 3, &aOutput[i*4], aOutputLen - (i*4)); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/b64.h Fri Apr 11 09:33:45 2014 +0000 @@ -0,0 +1,6 @@ +#ifndef b64_h +#define b64_h + +int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen); + +#endif