A client for the SmartREST protocol from Cumulocity.
Fork of MbedSmartRest by
Diff: MbedClient.cpp
- Revision:
- 13:e76920d5e1ec
- Parent:
- 12:788dd934f283
- Child:
- 14:dc3f8dd5c02b
--- 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