Own fork of MbedSmartRest

Dependents:   MbedSmartRestMain MbedSmartRestMain

Fork of MbedSmartRest by Cumulocity Official

MbedClient.cpp

Committer:
xinlei
Date:
2015-04-20
Revision:
21:207549b3711e
Parent:
20:505d29d5bdfc
Child:
23:0529d6779ab1

File content as of revision 21:207549b3711e:

/*
 * MbedClient.cpp
 *
 * Created on: Feb 1, 2013
 * * Authors: Vincent Wochnik <v.wochnik@gmail.com>
 *
 * Copyright (c) 2013 Cumulocity GmbH
 *
 * 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 <stdlib.h>
#include <string.h>
#include "mbed.h"
#include "rtos.h"

#include "MbedClient.h"
#include "SmartRestConf.h"
#include "logging.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_INTERNAL_ERROR 6
#define MBCL_DBG(...) aDebug(__VA_ARGS__)
//#define MBCL_DBG(fmt, ...)
#define DNS_ENTRY_DURATION 50

MbedClient::MbedClient(MDMSerial& mdm, uint8_t tries) :
    _tries(tries),
    _state(STATE_INIT),
    _isStreamRequest(false),
    _sock(),
    _source(_sock),
    _sink(_sock),
    _filter(_source),
    _mdm(mdm),
    cachedIPValid(0)
{
}

MbedClient::~MbedClient()
{
}

uint8_t MbedClient::beginRequest()
{
    if (_state != STATE_INIT)
        return internalError();

    MBCL_DBG("\033[32mMbedClient:\033[39m Begin SmartREST request.\n");
    _source.setTimeout(60000);
    if (!connect())
        return connectionError();

    if (!sendRequestHeader("/s"))
        return connectionError();

    _state = STATE_IN_REQUEST;
    return CLIENT_OK;
}

uint8_t MbedClient::beginStream(const char *uri)
{
    if (_state != STATE_INIT)
        return internalError();

    // set stream request flag to later set the timeout right
    _isStreamRequest = true;

    MBCL_DBG("\033[32mMbedClient:\033[39m Begin SmartREST stream.\n");
    _source.setTimeout(60000);
    if (!connect())
        return connectionError();

    if (!sendRequestHeader(uri))
        return connectionError();

    _state = STATE_IN_REQUEST;
    return CLIENT_OK;
}

uint8_t MbedClient::sendIdentifier(const char* identifier)
{
    MBCL_DBG("\033[32mMbedClient:\033[39m Send identifier.\n");
    if (_state != STATE_IN_REQUEST)
        return internalError();

    if ((identifier != NULL) && (strlen(identifier) != 0)) {
        if ((!send("X-Id: ")) ||
            (!send(identifier)) ||
            (!send("\r\n")))
            return connectionError();
    }
    _state = STATE_SENT_ID;
    return CLIENT_OK;
}

uint8_t MbedClient::sendData(const DataGenerator& generator)
{
    MBCL_DBG("\033[32mMbedClient:\033[39m Send payload.\n");
    if (_state != STATE_IN_REQUEST && _state != STATE_SENT_ID)
        return internalError();
    
    size_t len = generator.writtenLength();
    if ((!send("Content-Length: ")) ||
        (_sink.write((unsigned long)len) == 0) ||
        (!send("\r\n\r\n")))
        return connectionError();

    if (generator.writeTo(_sink) != len)
        return connectionError();
    _state = STATE_SENT_DATA;
    return CLIENT_OK;
}

uint8_t MbedClient::endRequest()
{
    MBCL_DBG("\033[32mMbedClient:\033[39m End request.\n");
    if ((_state != STATE_IN_REQUEST) &&
        (_state != STATE_SENT_ID) &&
        (_state != STATE_SENT_DATA)) {
        return internalError();
    }
    
    if (_state != STATE_SENT_DATA) {
        // send end of headers
        if (!send("\r\n")) {
            return connectionError();
        }
    }
    
    if (!_sink.flush()) {
        return connectionError();
    }
    
    _state = STATE_REQ_COMPLETE;
    return CLIENT_OK;
}

uint8_t MbedClient::awaitResponse()
{
    aDebug("\033[32mThread %p:\033[39m Await response.\n", Thread::gettid());
    if (_state != STATE_REQ_COMPLETE) {
        return internalError();
    }
    // set timeout to fifteen minutes if stream request flag set
    if (_isStreamRequest) {
        _source.setTimeout(300000);
    }
    
    uint8_t status = _filter.readStatus();
    if ((status != 200) || (!_filter.skipHeaders())) {
        return connectionError();
    }    
    
    _state = STATE_RECVD_RESPONSE;
    return CLIENT_OK;
}

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

void MbedClient::stop()
{
    MBCL_DBG("\033[32mMbedClient:\033[39m Reset client, %zu bytes sent.\n", packetSize);
    _isStreamRequest = false;
    _sock.close();
    _source.reset();
    _sink.reset();
    _filter.reset();
    _state = STATE_INIT;
}

bool MbedClient::connect()
{
    uint8_t tries = _tries;
    do {
        if (cachedIPValid == 0) {
            MDMParser::IP ip = _mdm.gethostbyname(getHost());
            if (ip == NOIP)
                continue;
            const unsigned char *c = (const unsigned char*)&ip;
            snprintf(cachedIP, sizeof(cachedIP), "%u.%u.%u.%u", c[3], c[2], c[1], c[0]);
            aInfo("\033[32mThread %p:\033[39m Connect to %s:%u (IP: %s)\n", Thread::gettid(), getHost(), getPort(), cachedIP);
        } else {
            aDebug("\033[32mMThread %p:\033[39m Connect to %s:%u\n", Thread::gettid(), cachedIP, getPort());
        }
        if (_sock.connect(cachedIP, getPort()) >= 0)
            break;
        cachedIPValid = 0;
        _sock.close();
        aCritical("\033[32mThread %p:\033[39m Connect failed.\n", Thread::gettid());
    } while (--tries > 0);

    cachedIPValid = (cachedIPValid+1) % DNS_ENTRY_DURATION;
    return (tries > 0);
}

bool MbedClient::send(const char *str)
{
    return (_sink.write(str) == strlen(str));
}

bool MbedClient::sendRequestHeader(const char *uri)
{
    MBCL_DBG("\033[32mMbedClient:\033[39m Send header.\n");
    if ((!send("POST ")) ||
        (!send(uri)) ||
        (!send(" HTTP/1.0\r\n")) ||
        (!send("Host: ")) ||
        (!send(getHost())) ||
        (!send("\r\n")))
        return false;
    
    return sendBasicAuth();
}

bool MbedClient::sendBasicAuth()
{
    // no need to send authorization if not specified
    const char* _username = getUsername();
    const char* _password = getPassword();
    if ((_username == NULL) || (strlen(_username) == 0) ||
        (_password == NULL) || (strlen(_password) == 0))
        return true;

    if (!send("Authorization: Basic "))
        return false;

    if (!send(getAuthStr()))
        return false;

    if (!send("\r\n"))
        return false;
    return true;
}

uint8_t MbedClient::internalError()
{
    aError("\033[32mMbedClient:\033[39m Internal error.\n");
    _state = STATE_INTERNAL_ERROR;
    return CLIENT_INTERNAL_ERROR;
}

uint8_t MbedClient::connectionError()
{
    aError("\033[32mMbedClient:\033[39m Connect error.\n");
    _state = STATE_INTERNAL_ERROR;
    return CLIENT_CONNECTION_ERROR;
}