mirror of https://github.com/deavmi/doap
Merge pull request #4 from deavmi/feature/timeoutable_gets
Timeout-able get()'s
This commit is contained in:
commit
4af4c00dc1
|
@ -316,5 +316,89 @@ unittest
|
|||
writeln("Future done");
|
||||
writeln("Got response: ", response);
|
||||
|
||||
client.close();
|
||||
}
|
||||
|
||||
version(unittest)
|
||||
{
|
||||
import core.time : dur;
|
||||
import doap.client.exceptions : RequestTimeoutException;
|
||||
import doap.client.request : CoapRequestFuture, RequestState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client testing
|
||||
*
|
||||
* See above except we test a timeout-based
|
||||
* request future here.
|
||||
*
|
||||
* This test DOES time out
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
CoapClient client = new CoapClient("coap.me", 5683);
|
||||
|
||||
|
||||
CoapRequestFuture future = client.newRequestBuilder()
|
||||
.payload(cast(ubyte[])"Hello this is Tristan!")
|
||||
.token([69])
|
||||
.post();
|
||||
|
||||
try
|
||||
{
|
||||
writeln("Future start");
|
||||
CoapPacket response = future.get(dur!("msecs")(10));
|
||||
|
||||
// We should timeout and NOT get here
|
||||
assert(false);
|
||||
}
|
||||
catch(RequestTimeoutException e)
|
||||
{
|
||||
// Ensure that we have the correct state
|
||||
assert(future.getState() == RequestState.TIMEDOUT);
|
||||
|
||||
// We SHOULD time out
|
||||
assert(true);
|
||||
}
|
||||
|
||||
client.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Client testing
|
||||
*
|
||||
* See above except we test a timeout-based
|
||||
* request future here.
|
||||
*
|
||||
* This test DOES NOT time out (it tests
|
||||
* with a high-enough threshold)
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
CoapClient client = new CoapClient("coap.me", 5683);
|
||||
|
||||
|
||||
CoapRequestFuture future = client.newRequestBuilder()
|
||||
.payload(cast(ubyte[])"Hello this is Tristan!")
|
||||
.token([69])
|
||||
.post();
|
||||
|
||||
try
|
||||
{
|
||||
writeln("Future start");
|
||||
CoapPacket response = future.get(dur!("msecs")(400));
|
||||
|
||||
// Ensure that we have the correct state
|
||||
assert(future.getState() == RequestState.COMPLETED);
|
||||
|
||||
// We SHOULD get here
|
||||
assert(true);
|
||||
}
|
||||
catch(RequestTimeoutException e)
|
||||
{
|
||||
// We should NOT time out
|
||||
assert(false);
|
||||
}
|
||||
|
||||
client.close();
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
module doap.client.exceptions;
|
||||
|
||||
import doap.exceptions : CoapException;
|
||||
import core.time : Duration;
|
||||
|
||||
public class CoapClientException : CoapException
|
||||
{
|
||||
this(string msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
import doap.client.request : CoapRequestFuture;
|
||||
import std.conv : to;
|
||||
|
||||
/**
|
||||
* Thrown when a `CoapRequestFuture` has
|
||||
* a `get(Duration)` call timeout
|
||||
*/
|
||||
public final class RequestTimeoutException : CoapClientException
|
||||
{
|
||||
/**
|
||||
* The future we timed out on
|
||||
*/
|
||||
private CoapRequestFuture future;
|
||||
|
||||
/**
|
||||
* Timeout time
|
||||
*/
|
||||
private Duration timeout;
|
||||
|
||||
/**
|
||||
* Constructs a new timeout exception for
|
||||
* the given future which timed out
|
||||
*
|
||||
* Params:
|
||||
* future = the future we timed out on
|
||||
* timeout = the time duration timed out
|
||||
* on
|
||||
*/
|
||||
package this(CoapRequestFuture future, Duration timeout)
|
||||
{
|
||||
super("Timed out whilst waiting for "~to!(string)(future)~" after "~to!(string)(timeout));
|
||||
this.future = future;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the future request which timed
|
||||
* out and cause dthis exception to throw
|
||||
* in the first place
|
||||
*
|
||||
* Returns: the `CoapRequestFuture`
|
||||
*/
|
||||
public CoapRequestFuture getFuture()
|
||||
{
|
||||
return this.future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timeout period which
|
||||
* was exceeded
|
||||
*
|
||||
* Returns: the `Duration`
|
||||
*/
|
||||
public Duration getTimeout()
|
||||
{
|
||||
return this.timeout;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
module doap.client;
|
||||
|
||||
public import doap.client.client : CoapClient;
|
||||
public import doap.client.request : CoapRequestBuilder, CoapRequestFuture, RequestState;
|
||||
public import doap.client.request : CoapRequestBuilder, CoapRequestFuture, RequestState;
|
||||
public import doap.client.exceptions : CoapClientException, RequestTimeoutException;
|
|
@ -2,7 +2,7 @@ module doap.client.request;
|
|||
|
||||
import doap.client.client : CoapClient;
|
||||
import doap.protocol;
|
||||
import doap.exceptions;
|
||||
import doap.client.exceptions;
|
||||
import core.time : Duration;
|
||||
import std.datetime.stopwatch : StopWatch, AutoStart;
|
||||
|
||||
|
@ -181,12 +181,15 @@ package class CoapRequestBuilder
|
|||
* Params:
|
||||
* tkn = the token
|
||||
* Returns: this builder
|
||||
* Throws:
|
||||
* CoapClientException = invalid token
|
||||
* length
|
||||
*/
|
||||
public CoapRequestBuilder token(ubyte[] tkn)
|
||||
{
|
||||
if(tkn.length > 8)
|
||||
{
|
||||
throw new CoapException("The token cannot be more than 8 bytes");
|
||||
throw new CoapClientException("The token cannot be more than 8 bytes");
|
||||
}
|
||||
|
||||
this.tkn = tkn;
|
||||
|
@ -277,7 +280,12 @@ public enum RequestState
|
|||
/**
|
||||
* The future was cancelled
|
||||
*/
|
||||
CANCELLED
|
||||
CANCELLED,
|
||||
|
||||
/**
|
||||
* The future timed out
|
||||
*/
|
||||
TIMEDOUT
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -371,7 +379,7 @@ public class CoapRequestFuture
|
|||
*
|
||||
* Returns: the response as a `CoapPacket`
|
||||
* Throws:
|
||||
* CoapException on cancelled request
|
||||
* CoapClientException on cancelled request
|
||||
*/
|
||||
public CoapPacket get()
|
||||
{
|
||||
|
@ -393,10 +401,56 @@ public class CoapRequestFuture
|
|||
// On error
|
||||
else
|
||||
{
|
||||
throw new CoapException("Request future cancelled");
|
||||
throw new CoapClientException("Request future cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until the response is received
|
||||
* but will unbllock if the timeout given
|
||||
* is exceeded
|
||||
*
|
||||
* Returns: the response as a `CoapPacket`
|
||||
* Throws:
|
||||
* RequestTimeoutException on the
|
||||
* future request timing out
|
||||
* CoapClientException on cancellation
|
||||
* of the request
|
||||
*/
|
||||
public CoapPacket get(Duration timeout)
|
||||
{
|
||||
// We can only wait on a condition if we
|
||||
// ... first have a-hold of the lock
|
||||
this.mutex.lock();
|
||||
|
||||
scope(exit)
|
||||
{
|
||||
// Unlock the lock (either from successfully
|
||||
// ... waiting or timing out)
|
||||
this.mutex.unlock();
|
||||
}
|
||||
|
||||
// Await a response
|
||||
if(this.condition.wait(timeout))
|
||||
{
|
||||
// If successfully completed
|
||||
if(this.state == RequestState.COMPLETED)
|
||||
{
|
||||
return this.response;
|
||||
}
|
||||
// On error
|
||||
else
|
||||
{
|
||||
throw new CoapClientException("Request future cancelled");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.state = RequestState.TIMEDOUT;
|
||||
throw new RequestTimeoutException(this, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of this future
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue