sandbox / mbed-client-classic

Fork of mbed-client-classic by Christopher Haster

source/m2mconnectionhandlerpimpl.cpp

Committer:
geky
Date:
2016-03-03
Revision:
4:0c58f5786538
Parent:
2:c3434146c3d2
Child:
5:babbe7026a0c

File content as of revision 4:0c58f5786538:

/*
 * Copyright (c) 2015 ARM Limited. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mbed-client-classic/m2mconnectionhandlerpimpl.h"
#include "mbed-client/m2mconnectionobserver.h"
#include "mbed-client/m2mconstants.h"
#include "mbed-client/m2msecurity.h"
#include "mbed-client/m2mconnectionhandler.h"

#include "m2mnetwork.h"
#include "threadwrapper.h"
#include "mbed_error.h"

#include "NetworkInterface.h"
#include "UDPSocket.h"



M2MConnectionHandlerPimpl::M2MConnectionHandlerPimpl(M2MConnectionHandler* base, M2MConnectionObserver &observer,
                                                     M2MConnectionSecurity* sec,
                                                     M2MInterface::BindingMode mode,
                                                     M2MInterface::NetworkStack stack)
:_base(base),
 _observer(observer),
 _security_impl(sec),
 _use_secure_connection(false),
 _binding_mode(mode),
 _network_stack(stack),
 _resolved(true),
 _is_handshaking(false),
 _socket(0),
 _listening(false),
 _listen_thread(0),
 _recv_thread(0),
 _send_thread(0)
{
    memset(&_address_buffer, 0, sizeof _address_buffer);
    memset(&_address, 0, sizeof _address);
    _address._address = _address_buffer;

    if (_network_stack != M2MInterface::LwIP_IPv4) {
        error("ConnectionHandler: Unsupported network stack, only IPv4 is currently supported");
    }
    
    if (_binding_mode == M2MInterface::TCP ||
        _binding_mode == M2MInterface::TCP_QUEUE) {
        error("ConnectionHandler: Unsupported binding mode, only UDP based modes are currently supported");
    }
    
    _running = true;
    _recv_thread = rtos::create_thread<
        M2MConnectionHandlerPimpl,
        &M2MConnectionHandlerPimpl::recv_handler>(this, osPriorityAboveNormal);
    _send_thread = rtos::create_thread<
        M2MConnectionHandlerPimpl,
        &M2MConnectionHandlerPimpl::send_handler>(this, osPriorityAboveNormal);
    _listen_thread = rtos::create_thread<
        M2MConnectionHandlerPimpl,
        &M2MConnectionHandlerPimpl::listen_handler>(this, osPriorityAboveNormal);    
}

M2MConnectionHandlerPimpl::~M2MConnectionHandlerPimpl()
{
    _listening = false;
    _running = false;
    
    if (_listen_thread) {
        delete _listen_thread;
        _listen_thread = 0;
    }
    
    if (_recv_thread) {
        delete _recv_thread;
        _recv_thread = 0;
    }
    
    if (_send_thread) {
        delete _send_thread;
        _send_thread = 0;
    }
    
    if (_socket) {
        delete _socket;
        _socket = 0;
    }
    
    delete _security_impl;
}

bool M2MConnectionHandlerPimpl::bind_connection(const uint16_t listen_port)
{
    return true;
}

bool M2MConnectionHandlerPimpl::resolve_server_address(const String& server_address,
                                                       const uint16_t server_port,
                                                       M2MConnectionObserver::ServerType server_type,
                                                       const M2MSecurity* security)
{
    NetworkInterface *iface = M2MNetwork::getInterface();
    if (!iface) {
        return false;
    }
    
    if (_socket) {
        delete _socket;
    }
    
    _socket = new UDPSocket(iface);
    if (_socket->open(server_address.c_str(), server_port) < 0) {
        return false;
    }

    if (security) {
        if (security->resource_value_int(M2MSecurity::SecurityMode) == M2MSecurity::Certificate ||
            security->resource_value_int(M2MSecurity::SecurityMode) == M2MSecurity::Psk) {
            if (_security_impl != NULL) {
                _security_impl->reset();
                _security_impl->init(security);
                
                if (_security_impl->connect(_base) < 0) {
                    return false;
                }
                
                _use_secure_connection = true;
            }
        }
    }
    
    _address._address = (void*)_socket->getIPAddress();
    _address._length = strlen((char*)_address._address);
    _address._port = _socket->getPort();
    _address._stack = _network_stack;
    
    _observer.address_ready(_address,
                            server_type,
                            _address._port);
    
    return true;
}


void M2MConnectionHandlerPimpl::recv_handler()
{
    while (_running) {
        if (!_socket) {
            rtos::Thread::wait(1000);
            continue;
        }
        
        int size = _socket->recv((char*)_recv_buffer, sizeof _recv_buffer, false);
        if (size <= 0) {
            rtos::Thread::wait(200);
            continue;
        }

        _recv_queue.put(new std::string(_recv_buffer, _recv_buffer+size));
    }
}

void M2MConnectionHandlerPimpl::send_handler()
{    
    while (_running) {
        osEvent e = _send_queue.get();
        if (e.status != osEventMessage) {
            rtos::Thread::wait(200);
            continue;
        }
            
        std::string *packet = (std::string*)e.value.p;
        int size = _socket->send((char*)packet->data(), packet->size());
        delete packet;
    }
}

bool M2MConnectionHandlerPimpl::send_data(uint8_t *data,
                                          uint16_t data_len,
                                          sn_nsdl_addr_s *address)
{
    if (address == NULL || data == NULL) {
        return false;
    }
    
    if (_use_secure_connection) {
        if (_security_impl->send_message(data, data_len) < 0) {
            return false;
        }
    } else {
        if (send_to_socket(data, data_len) < 0) {
            return false;
        }
    }
    
    _observer.data_sent();
    return true;
}

bool M2MConnectionHandlerPimpl::start_listening_for_data()
{
    _listening = true;
    return true;
}

void M2MConnectionHandlerPimpl::stop_listening()
{
    _listening = false;
}

void M2MConnectionHandlerPimpl::listen_handler()
{
    while (_running) {
        if (!_listening) {
            rtos::Thread::wait(1000);
            continue;
        }
            
        memset(_listen_buffer, 0, sizeof _listen_buffer);
        int size;
            
        if (_use_secure_connection) {
            size = _security_impl->read(_listen_buffer, sizeof _listen_buffer);
        } else {
            size = receive_from_socket(_listen_buffer, sizeof _listen_buffer);
        }
            
        if (size > 0) {
            _observer.data_available((uint8_t*)_listen_buffer, size, _address);
        } else if (size != 0) {
            _listening = false;
            _observer.socket_error(2);
        }
    }
}


int M2MConnectionHandlerPimpl::send_to_socket(const unsigned char *buf, size_t len)
{
    if (_send_queue.put(new std::string(buf, buf+len)) != osOK) {
        return M2MConnectionHandler::CONNECTION_ERROR_WANTS_WRITE;
    } else {
        return len;
    }
}

int M2MConnectionHandlerPimpl::receive_from_socket(unsigned char *buf, size_t len)
{
    osEvent e = _recv_queue.get();
    if (e.status == osEventMessage) {
        std::string *packet = (std::string*)e.value.p;
        int size = packet->size();
        
        if (size <= len) {
            memcpy(buf, packet->data(), size);
            delete packet;
            return size;
        }
    }
    
    return M2MConnectionHandler::CONNECTION_ERROR_WANTS_READ;
}

void M2MConnectionHandlerPimpl::handle_connection_error(int /*error*/)
{
    _observer.socket_error(4);
}