-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
ServerResponse.cs
132 lines (114 loc) · 5.33 KB
/
ServerResponse.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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
// After the server pipe is connected, it forks off a thread to handle the connection, and creates
// a new instance of the pipe to listen for new clients. When it gets a request, it validates
// the security and elevation level of the client. If that fails, it disconnects the client. Otherwise,
// it handles the request, sends a response (described by Response class) back to the client, then
// disconnects the pipe and ends the thread.
namespace Microsoft.NET.Sdk.Razor.Tool
{
/// <summary>
/// Base class for all possible responses to a request.
/// The ResponseType enum should list all possible response types
/// and ReadResponse creates the appropriate response subclass based
/// on the response type sent by the client.
/// The format of a response is:
///
/// Field Name Field Type Size (bytes)
/// -------------------------------------------------
/// responseLength int (positive) 4
/// responseType enum ResponseType 4
/// responseBody Response subclass variable
/// </summary>
internal abstract class ServerResponse
{
public enum ResponseType
{
// The client and server are using incompatible protocol versions.
MismatchedVersion,
// The build request completed on the server and the results are contained
// in the message.
Completed,
// The shutdown request completed and the server process information is
// contained in the message.
Shutdown,
// The request was rejected by the server.
Rejected,
}
public abstract ResponseType Type { get; }
public async Task WriteAsync(Stream outStream, CancellationToken cancellationToken)
{
using (var memoryStream = new MemoryStream())
using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode))
{
// Format the response
ServerLogger.Log("Formatting Response");
writer.Write((int)Type);
AddResponseBody(writer);
writer.Flush();
cancellationToken.ThrowIfCancellationRequested();
// Send the response to the client
// Write the length of the response
var length = checked((int)memoryStream.Length);
ServerLogger.Log("Writing response length");
// There is no way to know the number of bytes written to
// the pipe stream. We just have to assume all of them are written.
await outStream
.WriteAsync(BitConverter.GetBytes(length), 0, 4, cancellationToken)
.ConfigureAwait(false);
// Write the response
ServerLogger.Log("Writing response of size {0}", length);
memoryStream.Position = 0;
await memoryStream
.CopyToAsync(outStream, bufferSize: length, cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
}
protected abstract void AddResponseBody(BinaryWriter writer);
/// <summary>
/// May throw exceptions if there are pipe problems.
/// </summary>
/// <param name="stream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<ServerResponse> ReadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
{
ServerLogger.Log("Reading response length");
// Read the response length
var lengthBuffer = new byte[4];
await ServerProtocol.ReadAllAsync(stream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false);
var length = BitConverter.ToUInt32(lengthBuffer, 0);
// Read the response
ServerLogger.Log("Reading response of length {0}", length);
var responseBuffer = new byte[length];
await ServerProtocol.ReadAllAsync(
stream,
responseBuffer,
responseBuffer.Length,
cancellationToken)
.ConfigureAwait(false);
using (var reader = new BinaryReader(new MemoryStream(responseBuffer), Encoding.Unicode))
{
var responseType = (ResponseType)reader.ReadInt32();
switch (responseType)
{
case ResponseType.Completed:
return CompletedServerResponse.Create(reader);
case ResponseType.MismatchedVersion:
return new MismatchedVersionServerResponse();
case ResponseType.Shutdown:
return ShutdownServerResponse.Create(reader);
case ResponseType.Rejected:
return new RejectedServerResponse();
default:
throw new InvalidOperationException("Received invalid response type from server.");
}
}
}
}
}