-
Notifications
You must be signed in to change notification settings - Fork 234
/
Copy pathProtocolServer.cs
152 lines (132 loc) · 5.18 KB
/
ProtocolServer.cs
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
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
using Newtonsoft.Json.Linq;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
{
public abstract class ProtocolServer
{
private bool isStarted;
private ChannelBase serverChannel;
private MessageProtocolType messageProtocolType;
private TaskCompletionSource<bool> serverExitedTask;
private SynchronizationContext originalSynchronizationContext;
/// <summary>
/// Initializes an instance of the protocol server using the
/// specified channel for communication.
/// </summary>
/// <param name="serverChannel">The channel to use for communication with the client.</param>
/// <param name="messageProtocolType">The type of message protocol used by the server.</param>
public ProtocolServer(
ChannelBase serverChannel,
MessageProtocolType messageProtocolType)
{
this.serverChannel = serverChannel;
this.messageProtocolType = messageProtocolType;
this.originalSynchronizationContext = SynchronizationContext.Current;
}
public void Start()
{
if (!this.isStarted)
{
// Start the provided server channel
this.serverChannel.Start(this.messageProtocolType);
// Listen for unhandled exceptions from the dispatcher
this.serverChannel.MessageDispatcher.UnhandledException += MessageDispatcher_UnhandledException;
// Notify implementation about server start
this.OnStart();
// Server is now started
this.isStarted = true;
}
}
public void WaitForExit()
{
this.serverExitedTask = new TaskCompletionSource<bool>();
this.serverExitedTask.Task.Wait();
}
public void Stop()
{
if (this.isStarted)
{
// Make sure no future calls try to stop the server during shutdown
this.isStarted = false;
// Stop the implementation first
this.OnStop();
this.serverChannel.Stop();
if (this.serverExitedTask != null)
this.serverExitedTask.SetResult(true);
}
}
public void SetRequestHandler<TParams, TResult>(
RequestType<TParams, TResult> requestType,
Func<TParams, RequestContext<TResult>, Task> requestHandler)
{
this.serverChannel.MessageDispatcher.SetRequestHandler(
requestType,
requestHandler);
}
public void SetEventHandler<TParams>(
EventType<TParams> eventType,
Func<TParams, EventContext, Task> eventHandler)
{
this.serverChannel.MessageDispatcher.SetEventHandler(
eventType,
eventHandler);
}
/// <summary>
/// Sends an event to the channel's endpoint.
/// </summary>
/// <typeparam name="TParams">The event parameter type.</typeparam>
/// <param name="eventType">The type of event being sent.</param>
/// <param name="eventParams">The event parameters being sent.</param>
/// <returns>A Task that tracks completion of the send operation.</returns>
public Task SendEvent<TParams>(
EventType<TParams> eventType,
TParams eventParams)
{
// In a server, some events could be raised from a different
// thread. To ensure that messages are written serially,
// dispatch the SendEvent call to the message loop thread.
if (!this.serverChannel.MessageDispatcher.InMessageLoopThread)
{
this.serverChannel.MessageDispatcher.SynchronizationContext.Post(
async (obj) =>
{
await this.serverChannel.MessageWriter.WriteEvent(
eventType,
eventParams);
}, null);
return Task.FromResult(true);
}
else
{
return this.serverChannel.MessageWriter.WriteEvent(
eventType,
eventParams);
}
}
protected virtual void OnStart()
{
}
protected virtual void OnStop()
{
}
private void MessageDispatcher_UnhandledException(object sender, Exception e)
{
if (this.serverExitedTask != null)
{
this.serverExitedTask.SetException(e);
}
else if (this.originalSynchronizationContext != null)
{
this.originalSynchronizationContext.Post(o => { throw e; }, null);
}
}
}
}