mirror of
https://github.com/deavmi/doap
synced 2024-07-04 14:50:54 +02:00
CoapClient
- Added client class
This commit is contained in:
parent
ce3f6240a6
commit
456d8918ee
259
source/doap/client/client.d
Normal file
259
source/doap/client/client.d
Normal file
|
@ -0,0 +1,259 @@
|
|||
module doap.client.client;
|
||||
|
||||
import std.socket : Socket, Address, SocketType, ProtocolType, getAddress, parseAddress, InternetAddress, SocketShutdown;
|
||||
import doap.client.messaging : CoapMessagingLayer;
|
||||
import doap.protocol;
|
||||
import doap.client.request : CoapRequestBuilder, CoapRequest, CoapRequestFuture;
|
||||
import core.sync.mutex : Mutex;
|
||||
import std.container.slist : SList;
|
||||
|
||||
/**
|
||||
* A CoAP client
|
||||
*/
|
||||
public class CoapClient
|
||||
{
|
||||
/**
|
||||
* CoAP server endpoint
|
||||
*/
|
||||
private Address address;
|
||||
|
||||
/**
|
||||
* Running status
|
||||
*/
|
||||
package bool running;
|
||||
|
||||
/**
|
||||
* The datagram socket
|
||||
*/
|
||||
package Socket socket;
|
||||
|
||||
/**
|
||||
* The messaging layer which provides
|
||||
* request-response message match-ups
|
||||
*/
|
||||
private CoapMessagingLayer messaging;
|
||||
|
||||
/**
|
||||
* The request-response match list
|
||||
*/
|
||||
private SList!(CoapRequest) outgoingRequests;
|
||||
|
||||
/**
|
||||
* The lock for the request-response match list
|
||||
*/
|
||||
private Mutex requestsLock;
|
||||
|
||||
/**
|
||||
* Creates a new CoAP client to the
|
||||
* provided endpoint address
|
||||
*
|
||||
* Params:
|
||||
* address = the CoAP server endpoint
|
||||
*/
|
||||
this(Address address)
|
||||
{
|
||||
this.address = address;
|
||||
this.messaging = new CoapMessagingLayer(this);
|
||||
|
||||
this.requestsLock = new Mutex();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a new datagram socket,
|
||||
* sets the running status to `true`
|
||||
* and then starts the messaging
|
||||
* layer
|
||||
*/
|
||||
private void init()
|
||||
{
|
||||
// TODO: IF connect fails then don't start messaging
|
||||
this.socket = new Socket(this.address.addressFamily(), SocketType.DGRAM, ProtocolType.UDP);
|
||||
// this.socket.blocking(true);
|
||||
this.socket.connect(address);
|
||||
|
||||
// Set status to running
|
||||
this.running = true;
|
||||
|
||||
// Start the messaging layer
|
||||
this.messaging.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops this client
|
||||
*
|
||||
* This results in closing down the
|
||||
* messaging layer and ensuring that
|
||||
* no new datagrams may arrive on
|
||||
* our source port.
|
||||
*/
|
||||
public void close()
|
||||
{
|
||||
// Set status to not running
|
||||
this.running = false;
|
||||
|
||||
// Shutdown the socket (stopping the messaging layer)
|
||||
this.socket.shutdown(SocketShutdown.BOTH);
|
||||
|
||||
// Unbind (disallow incoming datagrams to source port (from device))
|
||||
this.socket.close();
|
||||
|
||||
// TODO: We must wake up other sleeprs with an error
|
||||
// (somehow, pass it in, flag set)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new CoAP request builder
|
||||
*
|
||||
* Returns: a new `CoapRequestBuilder`
|
||||
*/
|
||||
public CoapRequestBuilder newRequestBuilder()
|
||||
{
|
||||
return new CoapRequestBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the builder this will extract the details required
|
||||
* to encode the CoAP packet into its byte form, register
|
||||
* a coap request internally and return a future for this
|
||||
* request.
|
||||
*
|
||||
* Params:
|
||||
* requestBuilder = the request builder
|
||||
* Returns: the future
|
||||
*/
|
||||
package CoapRequestFuture doRequest(CoapRequestBuilder requestBuilder)
|
||||
{
|
||||
// Encode the packet
|
||||
CoapPacket requestPacket = new CoapPacket();
|
||||
requestPacket.setCode(requestBuilder.requestCode);
|
||||
requestPacket.setPayload(requestBuilder.pyld);
|
||||
requestPacket.setToken(requestBuilder.tkn);
|
||||
|
||||
// Create the future
|
||||
CoapRequestFuture future = new CoapRequestFuture();
|
||||
|
||||
// Link the CoapRequest to the future so it can be signalled
|
||||
CoapRequest request = new CoapRequest(requestPacket.getToken(), future);
|
||||
|
||||
// Store the request
|
||||
storeRequest(request);
|
||||
|
||||
// Send
|
||||
this.socket.send(requestPacket.getBytes());
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the request
|
||||
*
|
||||
* Params:
|
||||
* request = the `CoapRequest` to store in the
|
||||
* tracking list
|
||||
*/
|
||||
private void storeRequest(CoapRequest request)
|
||||
{
|
||||
// Store the request
|
||||
requestsLock.lock();
|
||||
outgoingRequests.insertAfter(outgoingRequests[], request);
|
||||
requestsLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a token this will try and find an active
|
||||
* request with a matching token and return it
|
||||
*
|
||||
* Params:
|
||||
* token = the token
|
||||
* Returns: the original `CoapRequest` if a match
|
||||
* is found, otherwise `null`
|
||||
*/
|
||||
package CoapRequest yankRequest(ubyte[] token)
|
||||
{
|
||||
CoapRequest foundRequest = null;
|
||||
|
||||
requestsLock.lock();
|
||||
|
||||
foreach(CoapRequest request; outgoingRequests)
|
||||
{
|
||||
if(request.token == token)
|
||||
{
|
||||
foundRequest = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requestsLock.unlock();
|
||||
|
||||
return foundRequest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the client
|
||||
*
|
||||
* In the future dogfooding should be
|
||||
* used and we should test against our
|
||||
* own server too.
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
// Address[] resolved = getAddress("coap.me");
|
||||
// resolved[0].po
|
||||
Address addr = new InternetAddress("coap.me", 5683);
|
||||
// CoapClient client = new CoapClient(addr);
|
||||
|
||||
// client.resource("/hello");
|
||||
|
||||
// client.connect();
|
||||
|
||||
// Test sending something
|
||||
CoapPacket packet = new CoapPacket();
|
||||
packet.setCode(Code.POST);
|
||||
packet.setToken([69]);
|
||||
packet.setPayload(cast(ubyte[])"My custom payload");
|
||||
packet.setType(MessageType.CONFIRMABLE);
|
||||
packet.setMessageId(257);
|
||||
|
||||
// client.socket.send(packet.getBytes());
|
||||
|
||||
}
|
||||
|
||||
version(unittest)
|
||||
{
|
||||
import std.stdio : writeln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client testing
|
||||
*
|
||||
* This tests building of a request using the builder,
|
||||
* finalizing through the client and then waiting on
|
||||
* the returned future for a result.
|
||||
*
|
||||
* We test the blocking example here therefore, i.e.
|
||||
* a blocking `get()`.
|
||||
*
|
||||
* This therefore tests the entire `messaging` module
|
||||
* and `client` module.
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
Address addr = new InternetAddress("coap.me", 5683);
|
||||
CoapClient client = new CoapClient(addr);
|
||||
|
||||
|
||||
CoapRequestFuture future = client.newRequestBuilder().payload(cast(ubyte[])"Hello this is Tristan!")
|
||||
.token([69])
|
||||
.post();
|
||||
|
||||
|
||||
writeln("Future start");
|
||||
CoapPacket response = future.get();
|
||||
writeln("Future done");
|
||||
writeln("Got response: ", response);
|
||||
|
||||
client.close();
|
||||
}
|
Loading…
Reference in New Issue
Block a user