forked from ServiceStack/ServiceStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCancellableRequestsFeature.cs
More file actions
151 lines (124 loc) · 4.64 KB
/
Copy pathCancellableRequestsFeature.cs
File metadata and controls
151 lines (124 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
using System;
using System.Collections.Concurrent;
using System.Configuration;
using System.Diagnostics;
using System.Threading;
using ServiceStack.Web;
namespace ServiceStack
{
public class CancellableRequestsFeature : IPlugin
{
public string AtPath { get; set; }
internal ConcurrentDictionary<string, ICancellableRequest> RequestsMap = new ConcurrentDictionary<string, ICancellableRequest>();
public CancellableRequestsFeature()
{
this.AtPath = "/current-requests/{Tag}/cancel";
}
internal void UnregisterCancellableRequest(string requestTag)
{
ICancellableRequest existing;
if (RequestsMap.TryRemove(requestTag, out existing))
existing.Dispose();
}
public void Register(IAppHost appHost)
{
appHost.RegisterService(typeof(CancellableRequestService), AtPath);
}
}
[DefaultRequest(typeof(CancelRequest))]
public class CancellableRequestService : Service
{
public object Any(CancelRequest request)
{
if (request.Tag.IsNullOrEmpty())
throw new ArgumentNullException("Tag");
using (var cancallableReq = base.Request.GetCancellableRequest(request.Tag))
{
if (cancallableReq == null)
throw HttpError.NotFound("Request with Tag '{0}' does not exist".Fmt(request.Tag));
cancallableReq.TokenSource.Cancel();
return new CancelRequestResponse
{
Tag = request.Tag,
Elapsed = cancallableReq.Elapsed,
};
}
}
}
public interface ICancellableRequest : IDisposable
{
CancellationToken Token { get; }
CancellationTokenSource TokenSource { get; }
TimeSpan Elapsed { get; }
}
class CancellableRequest : ICancellableRequest
{
private readonly CancellableRequestsFeature feature;
private readonly string requestTag;
private readonly Stopwatch stopwatch;
public CancellationToken Token { get; private set; }
public CancellationTokenSource TokenSource { get; private set; }
public CancellableRequest(CancellableRequestsFeature feature, IRequest req, string tag)
{
this.TokenSource = new CancellationTokenSource();
this.Token = this.TokenSource.Token;
this.feature = feature;
this.requestTag = tag;
this.stopwatch = Stopwatch.StartNew();
this.feature.UnregisterCancellableRequest(this.requestTag);
req.Items[typeof(CancellableRequest).Name] = feature.RequestsMap[tag] = this;
}
public TimeSpan Elapsed
{
get { return stopwatch.Elapsed; }
}
public void Dispose()
{
stopwatch.Stop();
this.feature.UnregisterCancellableRequest(this.requestTag);
}
}
class EmptyCancellableRequest : ICancellableRequest
{
private readonly Stopwatch stopwatch;
public CancellationToken Token { get; private set; }
public CancellationTokenSource TokenSource { get; private set; }
public EmptyCancellableRequest()
{
this.TokenSource = new CancellationTokenSource();
this.Token = this.TokenSource.Token;
this.stopwatch = Stopwatch.StartNew();
}
public TimeSpan Elapsed
{
get { return stopwatch.Elapsed; }
}
public void Dispose()
{
stopwatch.Stop();
}
}
public static class CancellableRequestsExtensions
{
public static ICancellableRequest CreateCancellableRequest(this IRequest req)
{
var feature = HostContext.GetPlugin<CancellableRequestsFeature>();
if (feature == null)
throw new Exception("Requires CancellableRequestsFeature plugin");
var xTag = req.GetHeader(HttpHeaders.XTag);
if (xTag != null)
return new CancellableRequest(feature, req, xTag);
return new EmptyCancellableRequest();
}
public static ICancellableRequest GetCancellableRequest(this IRequest req, string tag)
{
var feature = HostContext.GetPlugin<CancellableRequestsFeature>();
if (feature == null)
throw new Exception("Requires CancellableRequestsFeature plugin");
ICancellableRequest cancellableReq;
if (feature.RequestsMap.TryGetValue(tag, out cancellableReq))
return cancellableReq;
return null;
}
}
}