A client for the SmartREST protocol from Cumulocity.

Dependencies:   SmartRest

Fork of MbedSmartRest by Vincent Wochnik

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