A client for the SmartREST protocol from Cumulocity.

Dependencies:   SmartRest

Fork of MbedSmartRest by Vincent Wochnik

MbedClient.cpp

Committer:
vwochnik
Date:
2014-04-11
Revision:
13:e76920d5e1ec
Parent:
12:788dd934f283
Child:
14:dc3f8dd5c02b

File content as of revision 13:e76920d5e1ec:

#include "MbedClient.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "b64.h"
#include "mbed.h"

const char *cExpectedStatus = "HTTP/1.* 200 OK";

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 = MBED_STATE_INIT;
}

MbedClient::~MbedClient()
{
}

uint8_t MbedClient::beginRequest()
{
    if (_state != MBED_STATE_INIT)
        return CLIENT_INTERNAL_ERROR;

    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 != MBED_STATE_IN_REQUEST)
        return CLIENT_INTERNAL_ERROR;

    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)
{
    char len[8];
    
    if ((_state != MBED_STATE_IN_REQUEST) && (_state != MBED_STATE_SENT_ID))
        return CLIENT_INTERNAL_ERROR;
    
    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 != MBED_STATE_IN_REQUEST) && (_state != MBED_STATE_SENT_ID) && (_state != MBED_STATE_SENT_DATA))
        return CLIENT_INTERNAL_ERROR;
    
    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()
{
    int8_t state = 0; char c; size_t offset = 0;

    if (_state != MBED_STATE_REQ_COMPLETE)
        return CLIENT_INTERNAL_ERROR;
    
    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;
    }
    
    _state = MBED_STATE_RECVD_RESPONSE;
    return CLIENT_OK;
}

AbstractDataSource& MbedClient::receiveData()
{
    return _source;
}

void MbedClient::stop()
{
    _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;
}