Move IO related classes to their own file & namespace (#40534)
* Move IO related classes to their own file & namespace * Add file * Format
Blue committed
May 14, 2026 at 12:46 UTC
d4b49348fbc5f424fadb1609777c9dc1f22145a7
30 files changed
+1548
-1446
src/shared/inc/SocketChannel.h
+6
-6
@@ -184,7 +184,7 @@ public:
184
#ifdef WIN32
185
186
auto io = CreateIO();
187
- io.AddHandle(std::make_unique<windows::common::relay::WriteHandle>(m_socket.get(), span));
187
+ io.AddHandle(std::make_unique<windows::common::io::WriteHandle>(m_socket.get(), span));
188
189
io.Run(TimeoutToMilliseconds(timeout));
190
@@ -605,17 +605,17 @@ public:
605
606
private:
607
#ifdef WIN32
608
- windows::common::relay::MultiHandleWait CreateIO() const
608
+ windows::common::io::MultiHandleWait CreateIO() const
609
{
610
- wsl::windows::common::relay::MultiHandleWait io;
610
+ wsl::windows::common::io::MultiHandleWait io;
611
612
for (const auto event : m_exitEvents)
613
{
614
io.AddHandle(
615
- std::make_unique<windows::common::relay::EventHandle>(
615
+ std::make_unique<windows::common::io::EventHandle>(
616
event,
617
[this, event]() { THROW_HR_MSG(E_ABORT, "Exit event 0x%p signaled on channel: %hs", event, m_name.c_str()); }),
618
- windows::common::relay::MultiHandleWait::CancelOnCompleted | windows::common::relay::MultiHandleWait::NeedNotComplete);
618
+ windows::common::io::MultiHandleWait::CancelOnCompleted | windows::common::io::MultiHandleWait::NeedNotComplete);
619
}
620
621
return io;
@@ -637,7 +637,7 @@ private:
637
638
gsl::span<gsl::byte> message;
639
640
- io.AddHandle(std::make_unique<windows::common::relay::ReadSocketMessageHandle>(
640
+ io.AddHandle(std::make_unique<windows::common::io::ReadSocketMessageHandle>(
641
m_socket.get(), m_buffer, [&message](auto& received) { message = received; }));
642
643
io.Run(TimeoutToMilliseconds(timeout));
src/windows/WslcSDK/IOCallback.cpp
+1
-1
@@ -17,7 +17,7 @@ Abstract:
17
IOCallback::IOCallback(IWSLCProcess* process, const WslcContainerProcessIOCallbackOptions& options) :
18
m_process(process), m_callbackOptions(std::make_unique<WslcContainerProcessIOCallbackOptions>(options))
19
{
20
- using namespace wsl::windows::common::relay;
20
+ using namespace wsl::windows::common::io;
21
22
auto addIOCallback = [&](WslcProcessIOHandle ioHandle, WslcStdIOCallback callback, PVOID context) {
23
std::function<void(const gsl::span<char>& Buffer)> function;
src/windows/WslcSDK/IOCallback.h
+1
-1
@@ -35,6 +35,6 @@ private:
35
wil::com_ptr<IWSLCProcess> m_process;
36
std::unique_ptr<WslcContainerProcessIOCallbackOptions> m_callbackOptions;
37
std::thread m_thread;
38
- wsl::windows::common::relay::MultiHandleWait m_io;
38
+ wsl::windows::common::io::MultiHandleWait m_io;
39
wil::unique_event m_cancelEvent{wil::EventOptions::ManualReset};
40
};
src/windows/WslcSDK/wslcsdk.cpp
+1
-1
@@ -268,7 +268,7 @@ struct ImageFileResolver
268
}
269
270
private:
271
- wsl::windows::common::relay::HandleWrapper m_fileHandle;
271
+ wsl::windows::common::io::HandleWrapper m_fileHandle;
272
ULONGLONG m_length;
273
};
274
src/windows/common/CMakeLists.txt
+2
@@ -18,6 +18,7 @@ set(SOURCES
18
helpers.cpp
19
hvsocket.cpp
20
interop.cpp
21
+ HandleIO.cpp
22
Localization.cpp
23
lxssbusclient.cpp
24
lxssclient.cpp
@@ -101,6 +102,7 @@ set(HEADERS
102
hvsocket.hpp
103
INetworkingEngine.h
104
interop.hpp
105
+ HandleIO.h
106
LxssMessagePort.h
107
LxssPort.h
108
LxssSecurity.h
src/windows/common/HandleIO.cpp
new
+1047
@@ -0,0 +1,1047 @@
1
+// Copyright (C) Microsoft Corporation. All rights reserved.
2
+
3
+#include "precomp.h"
4
+#include "HandleIO.h"
5
+#pragma hdrstop
6
+
7
+using wsl::windows::common::io::BufferWrapper;
8
+using wsl::windows::common::io::DockerIORelayHandle;
9
+using wsl::windows::common::io::EventHandle;
10
+using wsl::windows::common::io::HandleWrapper;
11
+using wsl::windows::common::io::HTTPChunkBasedReadHandle;
12
+using wsl::windows::common::io::IOHandleStatus;
13
+using wsl::windows::common::io::LineBasedReadHandle;
14
+using wsl::windows::common::io::MultiHandleWait;
15
+using wsl::windows::common::io::OverlappedIOHandle;
16
+using wsl::windows::common::io::ReadHandle;
17
+using wsl::windows::common::io::ReadSocketMessageHandle;
18
+using wsl::windows::common::io::SingleAcceptHandle;
19
+using wsl::windows::common::io::WriteHandle;
20
+
21
+namespace {
22
+
23
+LARGE_INTEGER InitializeFileOffset(HANDLE File)
24
+{
25
+ LARGE_INTEGER Offset{};
26
+ if (GetFileType(File) == FILE_TYPE_DISK)
27
+ {
28
+ LOG_IF_WIN32_BOOL_FALSE(SetFilePointerEx(File, {}, &Offset, FILE_CURRENT));
29
+ }
30
+
31
+ return Offset;
32
+}
33
+
34
+void CancelPendingIo(auto Handle, OVERLAPPED& Overlapped)
35
+{
36
+ DWORD bytesTransferred{};
37
+ if (CancelIoEx((HANDLE)Handle, &Overlapped))
38
+ {
39
+ if constexpr (std::is_same_v<decltype(Handle), SOCKET>)
40
+ {
41
+ if (!WSAGetOverlappedResult(Handle, &Overlapped, &bytesTransferred, true, nullptr))
42
+ {
43
+ auto error = WSAGetLastError();
44
+ LOG_LAST_ERROR_IF(error != WSAECONNABORTED && error != WSA_OPERATION_ABORTED && error != WSAECONNRESET);
45
+ }
46
+ }
47
+ else
48
+ {
49
+ static_assert(std::is_same_v<decltype(Handle), HANDLE>);
50
+ if (!GetOverlappedResult(Handle, &Overlapped, &bytesTransferred, true))
51
+ {
52
+ auto error = GetLastError();
53
+ LOG_LAST_ERROR_IF(error != ERROR_CONNECTION_ABORTED && error != ERROR_OPERATION_ABORTED);
54
+ }
55
+ }
56
+ }
57
+ else
58
+ {
59
+ // ERROR_NOT_FOUND is returned if there was no IO to cancel.
60
+ LOG_LAST_ERROR_IF(GetLastError() != ERROR_NOT_FOUND);
61
+ }
62
+}
63
+
64
+} // namespace
65
+
66
+// HandleWrapper
67
+
68
+HandleWrapper::HandleWrapper(wil::unique_handle&& handle, std::function<void()>&& OnClose) :
69
+ Handle(handle.get()), OwnedHandle(std::move(handle)), OnClose(std::move(OnClose))
70
+{
71
+}
72
+
73
+HandleWrapper::HandleWrapper(wil::unique_socket&& handle, std::function<void()>&& OnClose) :
74
+ Handle((HANDLE)handle.get()), OwnedHandle(wil::unique_socket{handle.release()}), OnClose(std::move(OnClose))
75
+{
76
+}
77
+
78
+HandleWrapper::HandleWrapper(wil::unique_event&& handle, std::function<void()>&& OnClose) :
79
+ Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))
80
+{
81
+}
82
+
83
+HandleWrapper::HandleWrapper(SOCKET handle, std::function<void()>&& OnClose) :
84
+ Handle(reinterpret_cast<HANDLE>(handle)), OnClose(std::move(OnClose))
85
+{
86
+}
87
+
88
+HandleWrapper::HandleWrapper(HANDLE handle, std::function<void()>&& OnClose) : Handle(handle), OnClose(std::move(OnClose))
89
+{
90
+}
91
+
92
+HandleWrapper::HandleWrapper(wil::unique_hfile&& handle, std::function<void()>&& OnClose) :
93
+ Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))
94
+{
95
+}
96
+
97
+HandleWrapper::~HandleWrapper()
98
+{
99
+ Reset();
100
+}
101
+
102
+HANDLE HandleWrapper::Get() const
103
+{
104
+ return Handle;
105
+}
106
+
107
+void HandleWrapper::Reset()
108
+{
109
+ if (OnClose != nullptr)
110
+ {
111
+ OnClose();
112
+ OnClose = nullptr;
113
+ }
114
+
115
+ OwnedHandle = {};
116
+ Handle = nullptr;
117
+}
118
+
119
+// BufferWrapper
120
+
121
+BufferWrapper::BufferWrapper(size_t size) : m_owned(std::in_place, size)
122
+{
123
+}
124
+
125
+BufferWrapper::BufferWrapper(gsl::span<gsl::byte> span) : m_unowned(span)
126
+{
127
+}
128
+
129
+bool BufferWrapper::Owned() const noexcept
130
+{
131
+ return m_owned.has_value();
132
+}
133
+
134
+void BufferWrapper::Resize(size_t size)
135
+{
136
+ THROW_HR_IF_MSG(E_UNEXPECTED, !Owned(), "BufferWrapper::Resize called on a non-owned buffer");
137
+ m_owned->resize(size);
138
+}
139
+
140
+void BufferWrapper::Append(gsl::span<char> Span)
141
+{
142
+ THROW_HR_IF_MSG(E_UNEXPECTED, !Owned(), "BufferWrapper::Append called on a non-owned buffer");
143
+
144
+ m_owned->insert(m_owned->end(), Span.begin(), Span.end());
145
+}
146
+
147
+void BufferWrapper::Consume(size_t bytes) noexcept
148
+{
149
+ WI_ASSERT(bytes <= Size());
150
+ if (Owned())
151
+ {
152
+ m_owned->erase(m_owned->begin(), m_owned->begin() + bytes);
153
+ }
154
+ else
155
+ {
156
+ m_unowned = m_unowned.subspan(bytes);
157
+ }
158
+}
159
+
160
+gsl::span<gsl::byte> BufferWrapper::Span() noexcept
161
+{
162
+ return Owned() ? gsl::make_span(reinterpret_cast<gsl::byte*>(m_owned->data()), m_owned->size()) : m_unowned;
163
+}
164
+
165
+size_t BufferWrapper::Size() const noexcept
166
+{
167
+ return Owned() ? m_owned->size() : m_unowned.size();
168
+}
169
+
170
+// OverlappedIOHandle
171
+
172
+IOHandleStatus OverlappedIOHandle::GetState() const
173
+{
174
+ return State;
175
+}
176
+
177
+// EventHandle
178
+
179
+EventHandle::EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled) :
180
+ Handle(std::move(Handle)), OnSignalled(std::move(OnSignalled))
181
+{
182
+}
183
+
184
+void EventHandle::Schedule()
185
+{
186
+ State = IOHandleStatus::Pending;
187
+}
188
+
189
+void EventHandle::Collect()
190
+{
191
+ State = IOHandleStatus::Completed;
192
+ OnSignalled();
193
+}
194
+
195
+HANDLE EventHandle::GetHandle() const
196
+{
197
+ return Handle.Get();
198
+}
199
+
200
+// ReadHandle
201
+
202
+ReadHandle::ReadHandle(HandleWrapper&& MovedHandle, std::function<void(const gsl::span<char>& Buffer)>&& OnRead) :
203
+ Handle(std::move(MovedHandle)), OnRead(OnRead), Offset(InitializeFileOffset(Handle.Get()))
204
+{
205
+ Overlapped.hEvent = Event.get();
206
+}
207
+
208
+ReadHandle::~ReadHandle()
209
+{
210
+ if (State == IOHandleStatus::Pending)
211
+ {
212
+ CancelPendingIo(Handle.Get(), Overlapped);
213
+ }
214
+}
215
+
216
+void ReadHandle::Schedule()
217
+{
218
+ WI_ASSERT(State == IOHandleStatus::Standby);
219
+
220
+ Event.ResetEvent();
221
+
222
+ // Schedule the read.
223
+ DWORD bytesRead{};
224
+ Overlapped.Offset = Offset.LowPart;
225
+ Overlapped.OffsetHigh = Offset.HighPart;
226
+ auto* bufferData = reinterpret_cast<char*>(Buffer.Span().data());
227
+ if (ReadFile(Handle.Get(), bufferData, static_cast<DWORD>(Buffer.Size()), &bytesRead, &Overlapped))
228
+ {
229
+ Offset.QuadPart += bytesRead;
230
+
231
+ // Signal the read.
232
+ OnRead(gsl::make_span<char>(bufferData, static_cast<size_t>(bytesRead)));
233
+
234
+ // ReadFile completed immediately, process the result right away.
235
+ if (bytesRead == 0)
236
+ {
237
+ State = IOHandleStatus::Completed;
238
+ return; // Handle is completely read, don't try again.
239
+ }
240
+
241
+ // Read was done synchronously, remain in 'standby' state.
242
+ }
243
+ else
244
+ {
245
+ auto error = GetLastError();
246
+ if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE)
247
+ {
248
+ // Signal an empty read for EOF.
249
+ OnRead({});
250
+
251
+ State = IOHandleStatus::Completed;
252
+ return;
253
+ }
254
+
255
+ THROW_LAST_ERROR_IF_MSG(error != ERROR_IO_PENDING, "Handle: 0x%p", (void*)Handle.Get());
256
+
257
+ // The read is pending, update to 'Pending'
258
+ State = IOHandleStatus::Pending;
259
+ }
260
+}
261
+
262
+void ReadHandle::Collect()
263
+{
264
+ WI_ASSERT(State == IOHandleStatus::Pending);
265
+
266
+ // Transition back to standby
267
+ State = IOHandleStatus::Standby;
268
+
269
+ // Complete the read.
270
+ DWORD bytesRead{};
271
+ if (!GetOverlappedResult(Handle.Get(), &Overlapped, &bytesRead, false))
272
+ {
273
+ auto error = GetLastError();
274
+ THROW_WIN32_IF(error, error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE);
275
+
276
+ // We received ERROR_HANDLE_EOF or ERROR_BROKEN_PIPE. Validate that this was indeed a zero byte read.
277
+ WI_ASSERT(bytesRead == 0);
278
+ }
279
+
280
+ Offset.QuadPart += bytesRead;
281
+
282
+ // Signal the read.
283
+ OnRead(gsl::make_span<char>(reinterpret_cast<char*>(Buffer.Span().data()), static_cast<size_t>(bytesRead)));
284
+
285
+ // Transition to Complete if this was a zero byte read.
286
+ if (bytesRead == 0)
287
+ {
288
+ State = IOHandleStatus::Completed;
289
+ }
290
+}
291
+
292
+HANDLE ReadHandle::GetHandle() const
293
+{
294
+ return Event.get();
295
+}
296
+
297
+// SingleAcceptHandle
298
+
299
+SingleAcceptHandle::SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted) :
300
+ ListenSocket(std::move(ListenSocket)), AcceptedSocket(std::move(AcceptedSocket)), OnAccepted(std::move(OnAccepted))
301
+{
302
+ Overlapped.hEvent = Event.get();
303
+}
304
+
305
+SingleAcceptHandle::~SingleAcceptHandle()
306
+{
307
+ if (State == IOHandleStatus::Pending)
308
+ {
309
+ LOG_IF_WIN32_BOOL_FALSE(CancelIoEx(ListenSocket.Get(), &Overlapped));
310
+
311
+ DWORD bytesProcessed{};
312
+ DWORD flagsReturned{};
313
+ if (!WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesProcessed, TRUE, &flagsReturned))
314
+ {
315
+ auto error = GetLastError();
316
+ LOG_LAST_ERROR_IF(error != ERROR_CONNECTION_ABORTED && error != ERROR_OPERATION_ABORTED);
317
+ }
318
+ }
319
+}
320
+
321
+void SingleAcceptHandle::Schedule()
322
+{
323
+ WI_ASSERT(State == IOHandleStatus::Standby);
324
+
325
+ // Schedule the accept.
326
+ DWORD bytesReturned{};
327
+ if (AcceptEx((SOCKET)ListenSocket.Get(), (SOCKET)AcceptedSocket.Get(), &AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &bytesReturned, &Overlapped))
328
+ {
329
+ // Accept completed immediately.
330
+ State = IOHandleStatus::Completed;
331
+ OnAccepted();
332
+ }
333
+ else
334
+ {
335
+ auto error = WSAGetLastError();
336
+ THROW_HR_IF_MSG(HRESULT_FROM_WIN32(error), error != ERROR_IO_PENDING, "Handle: 0x%p", (void*)ListenSocket.Get());
337
+
338
+ State = IOHandleStatus::Pending;
339
+ }
340
+}
341
+
342
+void SingleAcceptHandle::Collect()
343
+{
344
+ WI_ASSERT(State == IOHandleStatus::Pending);
345
+
346
+ DWORD bytesReceived{};
347
+ DWORD flagsReturned{};
348
+
349
+ THROW_IF_WIN32_BOOL_FALSE(WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesReceived, false, &flagsReturned));
350
+
351
+ State = IOHandleStatus::Completed;
352
+ OnAccepted();
353
+}
354
+
355
+HANDLE SingleAcceptHandle::GetHandle() const
356
+{
357
+ return Event.get();
358
+}
359
+
360
+// LineBasedReadHandle
361
+
362
+LineBasedReadHandle::LineBasedReadHandle(HandleWrapper&& Handle, std::function<void(const gsl::span<char>& Line)>&& OnLine, bool Crlf) :
363
+ ReadHandle(std::move(Handle), [this](const gsl::span<char>& Buffer) { OnRead(Buffer); }), OnLine(OnLine), Crlf(Crlf)
364
+{
365
+}
366
+
367
+LineBasedReadHandle::~LineBasedReadHandle()
368
+{
369
+ // N.B. PendingBuffer can contain remaining data is an exception was thrown during parsing.
370
+}
371
+
372
+void LineBasedReadHandle::OnRead(const gsl::span<char>& Buffer)
373
+{
374
+ // If we reach of the end, signal a line with the remaining buffer.
375
+ if (Buffer.empty() && !PendingBuffer.empty())
376
+ {
377
+ OnLine(PendingBuffer);
378
+ PendingBuffer.clear();
379
+ return;
380
+ }
381
+
382
+ auto begin = Buffer.begin();
383
+ auto end = std::ranges::find(Buffer, Crlf ? '\r' : '\n');
384
+ while (end != Buffer.end())
385
+ {
386
+ if (Crlf)
387
+ {
388
+ end++; // Move to the following '\n'
389
+
390
+ if (end == Buffer.end() || *end != '\n') // Incomplete CRLF sequence. Append to buffer and continue.
391
+ {
392
+ PendingBuffer.insert(PendingBuffer.end(), begin, end);
393
+ begin = end;
394
+ end = std::ranges::find(end, Buffer.end(), '\r');
395
+ continue;
396
+ }
397
+ }
398
+
399
+ // Discard the '\r' in CRLF mode.
400
+ PendingBuffer.insert(PendingBuffer.end(), begin, Crlf ? end - 1 : end);
401
+
402
+ if (!PendingBuffer.empty())
403
+ {
404
+ OnLine(PendingBuffer);
405
+ PendingBuffer.clear();
406
+ }
407
+
408
+ begin = end + 1;
409
+ end = std::ranges::find(begin, Buffer.end(), Crlf ? '\r' : '\n');
410
+ }
411
+
412
+ PendingBuffer.insert(PendingBuffer.end(), begin, end);
413
+}
414
+
415
+// HTTPChunkBasedReadHandle
416
+
417
+HTTPChunkBasedReadHandle::HTTPChunkBasedReadHandle(HandleWrapper&& MovedHandle, std::function<void(const gsl::span<char>& Line)>&& OnChunk) :
418
+ ReadHandle(std::move(MovedHandle), [this](const gsl::span<char>& Buffer) { OnRead(Buffer); }), OnChunk(std::move(OnChunk))
419
+{
420
+}
421
+
422
+HTTPChunkBasedReadHandle::~HTTPChunkBasedReadHandle()
423
+{
424
+ // N.B. PendingBuffer can contain remaining data is an exception was thrown during parsing.
425
+ LOG_HR_IF(E_UNEXPECTED, !PendingBuffer.empty() || PendingChunkSize != 0 || ExpectHeader);
426
+}
427
+
428
+void HTTPChunkBasedReadHandle::OnRead(const gsl::span<char>& Input)
429
+{
430
+ // See: https://httpwg.org/specs/rfc9112.html#field.transfer-encoding
431
+
432
+ if (Input.empty())
433
+ {
434
+ // N.B. The body can be terminated by a zero-length chunk.
435
+ THROW_HR_IF(E_INVALIDARG, PendingChunkSize != 0 || ExpectHeader);
436
+ }
437
+
438
+ auto buffer = Input;
439
+
440
+ auto advance = [&](size_t count) {
441
+ WI_ASSERT(buffer.size() >= count);
442
+ buffer = buffer.subspan(count);
443
+ };
444
+
445
+ while (!buffer.empty())
446
+ {
447
+ if (PendingChunkSize == 0)
448
+ {
449
+ // Consume CRLF's between chunks.
450
+ if (PendingBuffer.empty() && (buffer.front() == '\r' || buffer.front() == '\n'))
451
+ {
452
+ advance(1);
453
+ continue;
454
+ }
455
+
456
+ ExpectHeader = true;
457
+
458
+ auto end = std::ranges::find(buffer, '\n');
459
+ PendingBuffer.insert(PendingBuffer.end(), buffer.begin(), end);
460
+ if (end == buffer.end())
461
+ {
462
+ // Incomplete size header, buffer until next read.
463
+ break;
464
+ }
465
+ // Advance beyond the LF
466
+ advance(end - buffer.begin() + 1);
467
+
468
+ THROW_HR_IF_MSG(
469
+ E_INVALIDARG,
470
+ PendingBuffer.size() < 2 || PendingBuffer.back() != '\r',
471
+ "Malformed chunk header: %hs",
472
+ PendingBuffer.c_str());
473
+ PendingBuffer.erase(PendingBuffer.end() - 1, PendingBuffer.end()); // Remove CR.
474
+
475
+#ifdef WSLC_HTTP_DEBUG
476
+
477
+ WSL_LOG("HTTPChunkHeader", TraceLoggingValue(PendingBuffer.c_str(), "Size"));
478
+
479
+#endif
480
+
481
+ try
482
+ {
483
+ size_t parsed{};
484
+ PendingChunkSize = std::stoul(PendingBuffer.c_str(), &parsed, 16);
485
+ THROW_HR_IF(E_INVALIDARG, parsed != PendingBuffer.size());
486
+ }
487
+ catch (...)
488
+ {
489
+ THROW_HR_MSG(E_INVALIDARG, "Failed to parse chunk size: %hs", PendingBuffer.c_str());
490
+ }
491
+
492
+ ExpectHeader = false;
493
+ PendingBuffer.clear();
494
+ }
495
+ else
496
+ {
497
+ // Consume the chunk.
498
+ auto consumedBytes = std::min(PendingChunkSize, buffer.size());
499
+ PendingBuffer.append(buffer.data(), consumedBytes);
500
+ advance(consumedBytes);
501
+
502
+ WI_ASSERT(PendingChunkSize >= consumedBytes);
503
+ PendingChunkSize -= consumedBytes;
504
+
505
+ if (PendingChunkSize == 0)
506
+ {
507
+
508
+#ifdef WSLC_HTTP_DEBUG
509
+
510
+ WSL_LOG("HTTPChunk", TraceLoggingValue(PendingBuffer.c_str(), "Content"));
511
+
512
+#endif
513
+ OnChunk(PendingBuffer);
514
+ PendingBuffer.clear();
515
+ }
516
+ }
517
+ }
518
+}
519
+
520
+// ReadSocketMessageHandle
521
+
522
+ReadSocketMessageHandle::ReadSocketMessageHandle(
523
+ HandleWrapper&& MovedSocket, std::vector<gsl::byte>& Buffer, std::function<void(const gsl::span<gsl::byte>& Message)>&& OnMessage) :
524
+ Socket(std::move(MovedSocket)), Buffer(Buffer), OnMessage(std::move(OnMessage))
525
+{
526
+ Overlapped.hEvent = Event.get();
527
+
528
+ if (Buffer.size() < sizeof(MESSAGE_HEADER))
529
+ {
530
+ Buffer.resize(sizeof(MESSAGE_HEADER));
531
+ }
532
+}
533
+
534
+ReadSocketMessageHandle::~ReadSocketMessageHandle()
535
+{
536
+ if (State == IOHandleStatus::Pending)
537
+ {
538
+ CancelPendingIo((SOCKET)Socket.Get(), Overlapped);
539
+ }
540
+}
541
+
542
+void ReadSocketMessageHandle::ScheduleRecv()
543
+{
544
+ Event.ResetEvent();
545
+
546
+ auto target = gsl::make_span(Buffer).subspan(CurrentOffset, BytesRemaining);
547
+ WSABUF wsaBuf = {gsl::narrow_cast<ULONG>(target.size()), reinterpret_cast<CHAR*>(target.data())};
548
+ DWORD bytesRead{};
549
+ DWORD flags = 0;
550
+ if (WSARecv(reinterpret_cast<SOCKET>(Socket.Get()), &wsaBuf, 1, &bytesRead, &flags, &Overlapped, nullptr) == 0)
551
+ {
552
+ ProcessRecvResult(bytesRead);
553
+ }
554
+ else
555
+ {
556
+ auto error = WSAGetLastError();
557
+ if (error == WSAECONNABORTED || error == WSAECONNRESET)
558
+ {
559
+ ProcessRecvResult(0);
560
+ return;
561
+ }
562
+
563
+ THROW_HR_IF_MSG(HRESULT_FROM_WIN32(error), error != WSA_IO_PENDING, "Socket: 0x%p", (void*)Socket.Get());
564
+
565
+ State = IOHandleStatus::Pending;
566
+ }
567
+}
568
+
569
+void ReadSocketMessageHandle::ProcessRecvResult(DWORD BytesRead)
570
+{
571
+ if (BytesRead == 0)
572
+ {
573
+ // If the socket was closed before any bytes of the next message were read, signal a clean end-of-stream.
574
+ // If some bytes were already buffered, the peer closed mid-message which is a protocol error.
575
+ THROW_HR_IF_MSG(
576
+ E_UNEXPECTED,
577
+ CurrentOffset > 0,
578
+ "Socket closed before a complete message could be read. ReadingHeader: %d, CurrentOffset: %zu, BytesRemaining: %zu",
579
+ ReadingHeader,
580
+ CurrentOffset,
581
+ BytesRemaining);
582
+
583
+ OnMessage({});
584
+ State = IOHandleStatus::Completed;
585
+ return;
586
+ }
587
+
588
+ CurrentOffset += BytesRead;
589
+ BytesRemaining -= BytesRead;
590
+
591
+ if (BytesRemaining > 0)
592
+ {
593
+ return;
594
+ }
595
+
596
+ if (ReadingHeader)
597
+ {
598
+ auto messageSize = gslhelpers::get_struct<MESSAGE_HEADER>(gsl::make_span(Buffer.data(), sizeof(MESSAGE_HEADER)))->MessageSize;
599
+
600
+ THROW_HR_IF_MSG(E_UNEXPECTED, messageSize < sizeof(MESSAGE_HEADER), "Unexpected message size: %u", messageSize);
601
+ THROW_HR_IF_MSG(E_UNEXPECTED, messageSize > 4 * 1024 * 1024, "Message size too large: %u", messageSize);
602
+
603
+ if (messageSize == sizeof(MESSAGE_HEADER))
604
+ {
605
+ OnMessage(gsl::make_span(Buffer.data(), messageSize));
606
+ State = IOHandleStatus::Completed;
607
+ return;
608
+ }
609
+
610
+ if (Buffer.size() < messageSize)
611
+ {
612
+ Buffer.resize(messageSize);
613
+ }
614
+
615
+ ReadingHeader = false;
616
+ CurrentOffset = sizeof(MESSAGE_HEADER);
617
+ BytesRemaining = messageSize - sizeof(MESSAGE_HEADER);
618
+ }
619
+ else
620
+ {
621
+ auto messageSize = gslhelpers::get_struct<MESSAGE_HEADER>(gsl::make_span(Buffer.data(), sizeof(MESSAGE_HEADER)))->MessageSize;
622
+ OnMessage(gsl::make_span(Buffer.data(), messageSize));
623
+ State = IOHandleStatus::Completed;
624
+ }
625
+}
626
+
627
+void ReadSocketMessageHandle::Schedule()
628
+{
629
+ WI_ASSERT(State == IOHandleStatus::Standby);
630
+ ScheduleRecv();
631
+}
632
+
633
+void ReadSocketMessageHandle::Collect()
634
+{
635
+ WI_ASSERT(State == IOHandleStatus::Pending);
636
+
637
+ State = IOHandleStatus::Standby;
638
+
639
+ DWORD bytesRead{};
640
+ DWORD flags{};
641
+ if (!WSAGetOverlappedResult(reinterpret_cast<SOCKET>(Socket.Get()), &Overlapped, &bytesRead, FALSE, &flags))
642
+ {
643
+ long error = WSAGetLastError();
644
+ THROW_WIN32_IF(error, error != WSAECONNABORTED && error != WSAECONNRESET);
645
+
646
+ WI_ASSERT(bytesRead == 0);
647
+ }
648
+
649
+ ProcessRecvResult(bytesRead);
650
+}
651
+
652
+HANDLE ReadSocketMessageHandle::GetHandle() const
653
+{
654
+ return Event.get();
655
+}
656
+
657
+// WriteHandle
658
+
659
+WriteHandle::WriteHandle(HandleWrapper&& MovedHandle, const std::vector<char>& Source) :
660
+ Handle(std::move(MovedHandle)), Buffer(Source.size()), Offset(InitializeFileOffset(Handle.Get()))
661
+{
662
+ std::memcpy(Buffer.Span().data(), Source.data(), Source.size());
663
+ Overlapped.hEvent = Event.get();
664
+}
665
+
666
+WriteHandle::WriteHandle(HandleWrapper&& MovedHandle, gsl::span<gsl::byte> Source) :
667
+ Handle(std::move(MovedHandle)), Buffer(Source), Offset(InitializeFileOffset(Handle.Get()))
668
+{
669
+ Overlapped.hEvent = Event.get();
670
+}
671
+
672
+WriteHandle::~WriteHandle()
673
+{
674
+ if (State == IOHandleStatus::Pending)
675
+ {
676
+ CancelPendingIo(Handle.Get(), Overlapped);
677
+ }
678
+}
679
+
680
+void WriteHandle::Schedule()
681
+{
682
+ WI_ASSERT(State == IOHandleStatus::Standby);
683
+
684
+ Event.ResetEvent();
685
+
686
+ Overlapped.Offset = Offset.LowPart;
687
+ Overlapped.OffsetHigh = Offset.HighPart;
688
+
689
+ // Schedule the write.
690
+ const auto buffer = Buffer.Span();
691
+ DWORD bytesWritten{};
692
+ if (WriteFile(Handle.Get(), buffer.data(), static_cast<DWORD>(buffer.size()), &bytesWritten, &Overlapped))
693
+ {
694
+ Offset.QuadPart += bytesWritten;
695
+
696
+ Buffer.Consume(bytesWritten);
697
+ if (Buffer.Size() == 0)
698
+ {
699
+ State = IOHandleStatus::Completed;
700
+ }
701
+ }
702
+ else
703
+ {
704
+ auto error = GetLastError();
705
+ THROW_LAST_ERROR_IF_MSG(error != ERROR_IO_PENDING, "Handle: 0x%p", (void*)Handle.Get());
706
+
707
+ // The write is pending, update to 'Pending'
708
+ State = IOHandleStatus::Pending;
709
+ }
710
+}
711
+
712
+void WriteHandle::Collect()
713
+{
714
+ WI_ASSERT(State == IOHandleStatus::Pending);
715
+
716
+ // Transition back to standby
717
+ State = IOHandleStatus::Standby;
718
+
719
+ // Complete the write.
720
+ DWORD bytesWritten{};
721
+ THROW_IF_WIN32_BOOL_FALSE(GetOverlappedResult(Handle.Get(), &Overlapped, &bytesWritten, false));
722
+ Offset.QuadPart += bytesWritten;
723
+
724
+ Buffer.Consume(bytesWritten);
725
+ if (Buffer.Size() == 0)
726
+ {
727
+ State = IOHandleStatus::Completed;
728
+ }
729
+}
730
+
731
+void WriteHandle::Push(const gsl::span<char>& Content)
732
+{
733
+ // Don't write if a WriteFile() is pending, since that could cause the buffer to reallocate.
734
+ WI_ASSERT(State == IOHandleStatus::Standby || State == IOHandleStatus::Completed);
735
+ WI_ASSERT(!Content.empty());
736
+
737
+ // Resize() throws E_UNEXPECTED if Buffer does not own its storage.
738
+ Buffer.Append(Content);
739
+
740
+ State = IOHandleStatus::Standby;
741
+}
742
+
743
+HANDLE WriteHandle::GetHandle() const
744
+{
745
+ return Event.get();
746
+}
747
+
748
+// DockerIORelayHandle
749
+
750
+DockerIORelayHandle::DockerIORelayHandle(HandleWrapper&& ReadHandle, HandleWrapper&& Stdout, HandleWrapper&& Stderr, Format ReadFormat) :
751
+ WriteStdout(std::move(Stdout)), WriteStderr(std::move(Stderr))
752
+{
753
+ if (ReadFormat == Format::HttpChunked)
754
+ {
755
+ Read = std::make_unique<HTTPChunkBasedReadHandle>(
756
+ std::move(ReadHandle), [this](const gsl::span<char>& Line) { this->OnRead(Line); });
757
+ }
758
+ else
759
+ {
760
+ Read =
761
+ std::make_unique<io::ReadHandle>(std::move(ReadHandle), [this](const gsl::span<char>& Buffer) { this->OnRead(Buffer); });
762
+ }
763
+}
764
+
765
+void DockerIORelayHandle::Schedule()
766
+{
767
+ WI_ASSERT(State == IOHandleStatus::Standby);
768
+ WI_ASSERT(Read->GetState() != IOHandleStatus::Pending);
769
+
770
+ // If we have an active handle and a buffer, try to flush that first.
771
+ if (ActiveHandle != nullptr && !PendingBuffer.empty())
772
+ {
773
+ // Push the data to the selected handle.
774
+ DWORD bytesToWrite = std::min(static_cast<DWORD>(RemainingBytes), static_cast<DWORD>(PendingBuffer.size()));
775
+
776
+ ActiveHandle->Push(gsl::make_span(PendingBuffer.data(), bytesToWrite));
777
+
778
+ // Consume the written bytes.
779
+ RemainingBytes -= bytesToWrite;
780
+ PendingBuffer.erase(PendingBuffer.begin(), PendingBuffer.begin() + bytesToWrite);
781
+
782
+ // Schedule the write.
783
+ ActiveHandle->Schedule();
784
+
785
+ // If the write is pending, update to 'Pending'
786
+ if (ActiveHandle->GetState() == IOHandleStatus::Pending)
787
+ {
788
+ State = IOHandleStatus::Pending;
789
+ }
790
+ else if (ActiveHandle->GetState() == IOHandleStatus::Completed)
791
+ {
792
+ if (RemainingBytes == 0)
793
+ {
794
+ // Switch back to reading if we've written all bytes for this chunk.
795
+ ActiveHandle = nullptr;
796
+
797
+ ProcessNextHeader();
798
+ }
799
+ }
800
+ }
801
+ else
802
+ {
803
+ if (Read->GetState() == IOHandleStatus::Completed)
804
+ {
805
+ LOG_HR_IF(E_UNEXPECTED, ActiveHandle != nullptr);
806
+
807
+ // No more data to read, we're done.
808
+ State = IOHandleStatus::Completed;
809
+ return;
810
+ }
811
+
812
+ // Schedule a read from the input.
813
+ Read->Schedule();
814
+ if (Read->GetState() == IOHandleStatus::Pending)
815
+ {
816
+ State = IOHandleStatus::Pending;
817
+ }
818
+ }
819
+}
820
+
821
+void DockerIORelayHandle::Collect()
822
+{
823
+ WI_ASSERT(State == IOHandleStatus::Pending);
824
+
825
+ if (ActiveHandle != nullptr && ActiveHandle->GetState() == IOHandleStatus::Pending)
826
+ {
827
+ // Complete the write.
828
+ ActiveHandle->Collect();
829
+
830
+ // If the write is completed, switch back to reading.
831
+ if (RemainingBytes == 0)
832
+ {
833
+ if (ActiveHandle->GetState() == IOHandleStatus::Completed)
834
+ {
835
+ ActiveHandle = nullptr;
836
+ }
837
+ }
838
+
839
+ // Transition back to standby if there's still data to read.
840
+ // Otherwise switch to Completed since everything is done.
841
+ if (Read->GetState() == IOHandleStatus::Completed)
842
+ {
843
+ LOG_HR_IF(E_UNEXPECTED, RemainingBytes != 0);
844
+
845
+ State = IOHandleStatus::Completed;
846
+ }
847
+ else
848
+ {
849
+ State = IOHandleStatus::Standby;
850
+ }
851
+ }
852
+ else
853
+ {
854
+ WI_ASSERT(Read->GetState() == IOHandleStatus::Pending);
855
+
856
+ // Complete the read.
857
+ Read->Collect();
858
+
859
+ // Transition back to standby.
860
+ State = IOHandleStatus::Standby;
861
+ }
862
+}
863
+
864
+HANDLE DockerIORelayHandle::GetHandle() const
865
+{
866
+ if (ActiveHandle != nullptr && ActiveHandle->GetState() == IOHandleStatus::Pending)
867
+ {
868
+ return ActiveHandle->GetHandle();
869
+ }
870
+ else
871
+ {
872
+ return Read->GetHandle();
873
+ }
874
+}
875
+
876
+void DockerIORelayHandle::ProcessNextHeader()
877
+{
878
+ if (PendingBuffer.size() < sizeof(MultiplexedHeader))
879
+ {
880
+ // Not enough data for a header yet.
881
+ return;
882
+ }
883
+
884
+ const auto* header = reinterpret_cast<const MultiplexedHeader*>(PendingBuffer.data());
885
+ RemainingBytes = ntohl(header->Length);
886
+
887
+ if (header->Fd == 1)
888
+ {
889
+ ActiveHandle = &WriteStdout;
890
+ }
891
+ else if (header->Fd == 2)
892
+ {
893
+ ActiveHandle = &WriteStderr;
894
+ }
895
+ else
896
+ {
897
+ THROW_HR_MSG(E_INVALIDARG, "Invalid Docker IO multiplexed header fd: %u", header->Fd);
898
+ }
899
+
900
+ // Consume the header.
901
+ PendingBuffer.erase(PendingBuffer.begin(), PendingBuffer.begin() + sizeof(MultiplexedHeader));
902
+}
903
+
904
+void DockerIORelayHandle::OnRead(const gsl::span<char>& Buffer)
905
+{
906
+ PendingBuffer.insert(PendingBuffer.end(), Buffer.begin(), Buffer.end());
907
+
908
+ if (ActiveHandle == nullptr)
909
+ {
910
+ // If no handle is active, expect a header.
911
+ ProcessNextHeader();
912
+ }
913
+}
914
+
915
+// MultiHandleWait
916
+
917
+void MultiHandleWait::AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags)
918
+{
919
+ m_handles.emplace_back(flags, std::move(handle));
920
+}
921
+
922
+void MultiHandleWait::Cancel()
923
+{
924
+ m_cancel = true;
925
+}
926
+
927
+bool MultiHandleWait::Run(std::optional<std::chrono::milliseconds> Timeout)
928
+{
929
+ m_cancel = false; // Run may be called multiple times.
930
+
931
+ std::optional<std::chrono::steady_clock::time_point> deadline;
932
+
933
+ if (Timeout.has_value())
934
+ {
935
+ deadline = std::chrono::steady_clock::now() + Timeout.value();
936
+ }
937
+
938
+ // Run until all handles are completed.
939
+
940
+ while (!m_handles.empty() && !m_cancel)
941
+ {
942
+ // Schedule IO on each handle until all are either pending, or completed.
943
+ for (size_t i = 0; i < m_handles.size() && !m_cancel; i++)
944
+ {
945
+ while (m_handles[i].second->GetState() == IOHandleStatus::Standby && !m_cancel)
946
+ {
947
+ try
948
+ {
949
+ m_handles[i].second->Schedule();
950
+ }
951
+ catch (...)
952
+ {
953
+ if (WI_IsFlagSet(m_handles[i].first, Flags::IgnoreErrors))
954
+ {
955
+ m_handles[i].second.reset(); // Reset the handle so it can be deleted.
956
+ break;
957
+ }
958
+ else
959
+ {
960
+ throw;
961
+ }
962
+ }
963
+ }
964
+ }
965
+
966
+ // Remove completed handles from m_handles.
967
+ bool hasHandleToWaitFor = false;
968
+ for (auto it = m_handles.begin(); it != m_handles.end();)
969
+ {
970
+ if (!it->second)
971
+ {
972
+ it = m_handles.erase(it);
973
+ }
974
+ else if (it->second->GetState() == IOHandleStatus::Completed)
975
+ {
976
+ if (WI_IsFlagSet(it->first, Flags::CancelOnCompleted))
977
+ {
978
+ m_cancel = true; // Cancel the IO if a handle with CancelOnCompleted is in the completed state.
979
+ }
980
+
981
+ it = m_handles.erase(it);
982
+ }
983
+ else
984
+ {
985
+ // If only NeedNotComplete handles are left, we want to exit Run.
986
+ if (WI_IsFlagClear(it->first, Flags::NeedNotComplete))
987
+ {
988
+ hasHandleToWaitFor = true;
989
+ }
990
+ ++it;
991
+ }
992
+ }
993
+
994
+ if (!hasHandleToWaitFor || m_cancel)
995
+ {
996
+ break;
997
+ }
998
+
999
+ // Wait for the next operation to complete.
1000
+ std::vector<HANDLE> waitHandles;
1001
+ for (const auto& e : m_handles)
1002
+ {
1003
+ waitHandles.emplace_back(e.second->GetHandle());
1004
+ }
1005
+
1006
+ DWORD waitTimeout = INFINITE;
1007
+ if (deadline.has_value())
1008
+ {
1009
+ auto miliseconds =
1010
+ std::chrono::duration_cast<std::chrono::milliseconds>(deadline.value() - std::chrono::steady_clock::now()).count();
1011
+
1012
+ waitTimeout = static_cast<DWORD>(std::max(0LL, miliseconds));
1013
+ }
1014
+
1015
+ auto result = WaitForMultipleObjects(static_cast<DWORD>(waitHandles.size()), waitHandles.data(), false, waitTimeout);
1016
+ if (result == WAIT_TIMEOUT)
1017
+ {
1018
+ THROW_WIN32(ERROR_TIMEOUT);
1019
+ }
1020
+ else if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size())
1021
+ {
1022
+ auto index = result - WAIT_OBJECT_0;
1023
+
1024
+ try
1025
+ {
1026
+ m_handles[index].second->Collect();
1027
+ }
1028
+ catch (...)
1029
+ {
1030
+ if (WI_IsFlagSet(m_handles[index].first, Flags::IgnoreErrors))
1031
+ {
1032
+ m_handles.erase(m_handles.begin() + index);
1033
+ }
1034
+ else
1035
+ {
1036
+ throw;
1037
+ }
1038
+ }
1039
+ }
1040
+ else
1041
+ {
1042
+ THROW_LAST_ERROR_MSG("Timeout: %lu, Count: %llu", waitTimeout, waitHandles.size());
1043
+ }
1044
+ }
1045
+
1046
+ return !m_cancel;
1047
+}
src/windows/common/HandleIO.h
new
+380
@@ -0,0 +1,380 @@
1
+// Copyright (C) Microsoft Corporation. All rights reserved.
2
+
3
+#pragma once
4
+
5
+#define LX_RELAY_BUFFER_SIZE 0x1000
6
+
7
+namespace wsl::windows::common::io {
8
+
9
+enum class IOHandleStatus
10
+{
11
+ Standby,
12
+ Pending,
13
+ Completed
14
+};
15
+
16
+struct HandleWrapper
17
+{
18
+ DEFAULT_MOVABLE(HandleWrapper);
19
+ NON_COPYABLE(HandleWrapper)
20
+
21
+ HandleWrapper(wil::unique_handle&& handle, std::function<void()>&& OnClose = []() {});
22
+ HandleWrapper(wil::unique_socket&& handle, std::function<void()>&& OnClose = []() {});
23
+ HandleWrapper(wil::unique_event&& handle, std::function<void()>&& OnClose = []() {});
24
+ HandleWrapper(SOCKET handle, std::function<void()>&& OnClose = []() {});
25
+ HandleWrapper(HANDLE handle, std::function<void()>&& OnClose = []() {});
26
+ HandleWrapper(wil::unique_hfile&& handle, std::function<void()>&& OnClose = []() {});
27
+ ~HandleWrapper();
28
+
29
+ HANDLE Get() const;
30
+ void Reset();
31
+
32
+private:
33
+ HANDLE Handle{};
34
+ std::variant<wil::unique_handle, wil::unique_socket> OwnedHandle;
35
+ std::function<void()> OnClose;
36
+};
37
+
38
+// A buffer that may either own its underlying storage (constructed from a size, allocating an
39
+// internal std::vector<char>) or borrow it from a caller-provided gsl::span<gsl::byte>.
40
+class BufferWrapper
41
+{
42
+public:
43
+ DEFAULT_MOVABLE(BufferWrapper);
44
+ NON_COPYABLE(BufferWrapper);
45
+
46
+ explicit BufferWrapper(size_t size);
47
+ explicit BufferWrapper(gsl::span<gsl::byte> span);
48
+
49
+ bool Owned() const noexcept;
50
+ void Resize(size_t size);
51
+ void Append(gsl::span<char> Span);
52
+ void Consume(size_t bytes) noexcept;
53
+ gsl::span<gsl::byte> Span() noexcept;
54
+ size_t Size() const noexcept;
55
+
56
+private:
57
+ std::optional<std::vector<char>> m_owned;
58
+ gsl::span<gsl::byte> m_unowned;
59
+};
60
+
61
+class OverlappedIOHandle
62
+{
63
+public:
64
+ NON_COPYABLE(OverlappedIOHandle)
65
+ NON_MOVABLE(OverlappedIOHandle)
66
+
67
+ OverlappedIOHandle() = default;
68
+ virtual ~OverlappedIOHandle() = default;
69
+ virtual void Schedule() = 0;
70
+ virtual void Collect() = 0;
71
+ virtual HANDLE GetHandle() const = 0;
72
+ IOHandleStatus GetState() const;
73
+
74
+protected:
75
+ IOHandleStatus State = IOHandleStatus::Standby;
76
+};
77
+
78
+class EventHandle : public OverlappedIOHandle
79
+{
80
+public:
81
+ NON_COPYABLE(EventHandle)
82
+ NON_MOVABLE(EventHandle)
83
+
84
+ EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled = []() {});
85
+ void Schedule() override;
86
+ void Collect() override;
87
+ HANDLE GetHandle() const override;
88
+
89
+private:
90
+ HandleWrapper Handle;
91
+ std::function<void()> OnSignalled;
92
+};
93
+
94
+class ReadHandle : public OverlappedIOHandle
95
+{
96
+public:
97
+ NON_COPYABLE(ReadHandle);
98
+ NON_MOVABLE(ReadHandle);
99
+
100
+ ReadHandle(HandleWrapper&& MovedHandle, std::function<void(const gsl::span<char>& Buffer)>&& OnRead);
101
+ virtual ~ReadHandle();
102
+
103
+ void Schedule() override;
104
+ void Collect() override;
105
+ HANDLE GetHandle() const override;
106
+
107
+private:
108
+ HandleWrapper Handle;
109
+ std::function<void(const gsl::span<char>& Buffer)> OnRead;
110
+ wil::unique_event Event{wil::EventOptions::ManualReset};
111
+ OVERLAPPED Overlapped{};
112
+ BufferWrapper Buffer{LX_RELAY_BUFFER_SIZE};
113
+ LARGE_INTEGER Offset{};
114
+};
115
+
116
+class SingleAcceptHandle : public OverlappedIOHandle
117
+{
118
+public:
119
+ NON_COPYABLE(SingleAcceptHandle)
120
+ NON_MOVABLE(SingleAcceptHandle)
121
+
122
+ SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted);
123
+ ~SingleAcceptHandle();
124
+
125
+ void Schedule() override;
126
+ void Collect() override;
127
+ HANDLE GetHandle() const override;
128
+
129
+private:
130
+ HandleWrapper ListenSocket;
131
+ HandleWrapper AcceptedSocket;
132
+ wil::unique_event Event{wil::EventOptions::ManualReset};
133
+ OVERLAPPED Overlapped{};
134
+ std::function<void()> OnAccepted;
135
+ char AcceptBuffer[2 * sizeof(SOCKADDR_STORAGE)];
136
+};
137
+
138
+class LineBasedReadHandle : public ReadHandle
139
+{
140
+public:
141
+ NON_COPYABLE(LineBasedReadHandle);
142
+ NON_MOVABLE(LineBasedReadHandle);
143
+
144
+ LineBasedReadHandle(HandleWrapper&& Handle, std::function<void(const gsl::span<char>& Buffer)>&& OnLine, bool Crlf);
145
+ ~LineBasedReadHandle();
146
+
147
+private:
148
+ void OnRead(const gsl::span<char>& Buffer);
149
+
150
+ std::function<void(const gsl::span<char>& Buffer)> OnLine;
151
+ std::string PendingBuffer;
152
+ bool Crlf{};
153
+};
154
+
155
+class HTTPChunkBasedReadHandle : public ReadHandle
156
+{
157
+public:
158
+ NON_COPYABLE(HTTPChunkBasedReadHandle);
159
+ NON_MOVABLE(HTTPChunkBasedReadHandle);
160
+
161
+ HTTPChunkBasedReadHandle(HandleWrapper&& Handler, std::function<void(const gsl::span<char>& Buffer)>&& OnChunk);
162
+ ~HTTPChunkBasedReadHandle();
163
+
164
+ void OnRead(const gsl::span<char>& Line);
165
+
166
+private:
167
+ std::function<void(const gsl::span<char>& Buffer)> OnChunk;
168
+ std::string PendingBuffer;
169
+ uint64_t PendingChunkSize = 0;
170
+ bool ExpectHeader = true;
171
+};
172
+
173
+class ReadSocketMessageHandle : public OverlappedIOHandle
174
+{
175
+public:
176
+ NON_COPYABLE(ReadSocketMessageHandle);
177
+ NON_MOVABLE(ReadSocketMessageHandle);
178
+
179
+ ReadSocketMessageHandle(HandleWrapper&& Socket, std::vector<gsl::byte>& Buffer, std::function<void(const gsl::span<gsl::byte>& Message)>&& OnMessage);
180
+ ~ReadSocketMessageHandle();
181
+
182
+ void Schedule() override;
183
+ void Collect() override;
184
+ HANDLE GetHandle() const override;
185
+
186
+private:
187
+ void ScheduleRecv();
188
+ void ProcessRecvResult(DWORD BytesRead);
189
+
190
+ HandleWrapper Socket;
191
+ std::vector<gsl::byte>& Buffer;
192
+ std::function<void(const gsl::span<gsl::byte>& Message)> OnMessage;
193
+ wil::unique_event Event{wil::EventOptions::ManualReset};
194
+ OVERLAPPED Overlapped{};
195
+ bool ReadingHeader = true;
196
+ size_t BytesRemaining = sizeof(MESSAGE_HEADER);
197
+ size_t CurrentOffset = 0;
198
+};
199
+
200
+class WriteHandle : public OverlappedIOHandle
201
+{
202
+public:
203
+ NON_COPYABLE(WriteHandle);
204
+ NON_MOVABLE(WriteHandle);
205
+
206
+ WriteHandle(HandleWrapper&& Handle, const std::vector<char>& Buffer = {});
207
+ WriteHandle(HandleWrapper&& Handle, gsl::span<gsl::byte> Span);
208
+ ~WriteHandle();
209
+ void Schedule() override;
210
+ void Collect() override;
211
+ HANDLE GetHandle() const override;
212
+ void Push(const gsl::span<char>& Buffer);
213
+
214
+private:
215
+ HandleWrapper Handle;
216
+ wil::unique_event Event{wil::EventOptions::ManualReset};
217
+ OVERLAPPED Overlapped{};
218
+ BufferWrapper Buffer;
219
+ LARGE_INTEGER Offset{};
220
+};
221
+
222
+template <typename TRead = ReadHandle>
223
+class RelayHandle : public OverlappedIOHandle
224
+{
225
+public:
226
+ NON_COPYABLE(RelayHandle);
227
+ NON_MOVABLE(RelayHandle);
228
+
229
+ RelayHandle(HandleWrapper&& Input, HandleWrapper&& Output) :
230
+ Read(std::move(Input), [this](const gsl::span<char>& Buffer) { return OnRead(Buffer); }), Write(std::move(Output))
231
+ {
232
+ }
233
+
234
+ void Schedule() override
235
+ {
236
+ WI_ASSERT(State == IOHandleStatus::Standby);
237
+
238
+ // If the Buffer is empty, then we're reading.
239
+ if (PendingBuffer.empty())
240
+ {
241
+ // If the output buffer is empty and the reading end is completed, then we're done.
242
+ if (Read.GetState() == IOHandleStatus::Completed)
243
+ {
244
+ State = IOHandleStatus::Completed;
245
+ return;
246
+ }
247
+
248
+ Read.Schedule();
249
+
250
+ // If the read is pending, update to 'Pending'
251
+ if (Read.GetState() == IOHandleStatus::Pending)
252
+ {
253
+ State = IOHandleStatus::Pending;
254
+ }
255
+ }
256
+ else
257
+ {
258
+ Write.Push(PendingBuffer);
259
+ PendingBuffer.clear();
260
+
261
+ Write.Schedule();
262
+
263
+ if (Write.GetState() == IOHandleStatus::Pending)
264
+ {
265
+ // The write is pending, update to 'Pending'
266
+ State = IOHandleStatus::Pending;
267
+ }
268
+ }
269
+ }
270
+
271
+ void Collect() override
272
+ {
273
+ WI_ASSERT(State == IOHandleStatus::Pending);
274
+
275
+ // Transition back to standby
276
+ State = IOHandleStatus::Standby;
277
+
278
+ if (Read.GetState() == IOHandleStatus::Pending)
279
+ {
280
+ Read.Collect();
281
+ }
282
+ else
283
+ {
284
+ WI_ASSERT(Write.GetState() == IOHandleStatus::Pending);
285
+ Write.Collect();
286
+ }
287
+ }
288
+
289
+ HANDLE GetHandle() const override
290
+ {
291
+ if (Read.GetState() == IOHandleStatus::Pending)
292
+ {
293
+ return Read.GetHandle();
294
+ }
295
+ else
296
+ {
297
+ WI_ASSERT(Write.GetState() == IOHandleStatus::Pending);
298
+ return Write.GetHandle();
299
+ }
300
+ }
301
+
302
+private:
303
+ void OnRead(const gsl::span<char>& Content)
304
+ {
305
+ PendingBuffer.insert(PendingBuffer.end(), Content.begin(), Content.end());
306
+ }
307
+
308
+ TRead Read;
309
+ WriteHandle Write;
310
+ std::vector<char> PendingBuffer;
311
+};
312
+
313
+class DockerIORelayHandle : public OverlappedIOHandle
314
+{
315
+public:
316
+ NON_COPYABLE(DockerIORelayHandle);
317
+ NON_MOVABLE(DockerIORelayHandle);
318
+
319
+ enum class Format
320
+ {
321
+ Raw,
322
+ HttpChunked
323
+ };
324
+
325
+ DockerIORelayHandle(HandleWrapper&& Input, HandleWrapper&& Stdout, HandleWrapper&& Stderr, Format ReadFormat);
326
+ void Schedule() override;
327
+ void Collect() override;
328
+ HANDLE GetHandle() const override;
329
+
330
+#pragma pack(push, 1)
331
+ struct MultiplexedHeader
332
+ {
333
+ uint8_t Fd;
334
+ char Zeroes[3];
335
+ uint32_t Length;
336
+ };
337
+#pragma pack(pop)
338
+
339
+ static_assert(sizeof(MultiplexedHeader) == 8);
340
+
341
+private:
342
+ void OnRead(const gsl::span<char>& Buffer);
343
+ void ProcessNextHeader();
344
+
345
+ std::unique_ptr<OverlappedIOHandle> Read;
346
+ WriteHandle WriteStdout;
347
+ WriteHandle WriteStderr;
348
+ std::vector<char> PendingBuffer;
349
+ WriteHandle* ActiveHandle = nullptr;
350
+ size_t RemainingBytes = 0;
351
+};
352
+
353
+class MultiHandleWait
354
+{
355
+public:
356
+ NON_COPYABLE(MultiHandleWait);
357
+ DEFAULT_MOVABLE(MultiHandleWait);
358
+
359
+ enum Flags
360
+ {
361
+ None = 0,
362
+ CancelOnCompleted = 1,
363
+ IgnoreErrors = 2,
364
+ NeedNotComplete = 4,
365
+ };
366
+
367
+ MultiHandleWait() = default;
368
+
369
+ void AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags = Flags::None);
370
+ bool Run(std::optional<std::chrono::milliseconds> Timeout);
371
+ void Cancel();
372
+
373
+private:
374
+ std::vector<std::pair<Flags, std::unique_ptr<OverlappedIOHandle>>> m_handles;
375
+ bool m_cancel = false;
376
+};
377
+
378
+DEFINE_ENUM_FLAG_OPERATORS(MultiHandleWait::Flags);
379
+
380
+} // namespace wsl::windows::common::io
src/windows/common/SubProcess.cpp
+4
-4
@@ -16,7 +16,7 @@ Abstract:
16
17
#include "SubProcess.h"
18
19
-using namespace wsl::windows::common::relay;
19
+using namespace wsl::windows::common::io;
20
using wsl::windows::common::SubProcess;
21
22
SubProcess::SubProcess(LPCWSTR ApplicationName, LPCWSTR CommandLine, DWORD Flags, DWORD StartupFlags) :
@@ -224,11 +224,11 @@ SubProcess::ProcessOutput SubProcess::RunAndCaptureOutput(DWORD Timeout, HANDLE
224
225
m_stdOut = stdoutWrite.get();
226
227
- relay::MultiHandleWait io;
227
+ io::MultiHandleWait io;
228
std::string stdoutNative;
229
std::string stderrNative;
230
231
- io.AddHandle(std::make_unique<relay::ReadHandle>(
231
+ io.AddHandle(std::make_unique<io::ReadHandle>(
232
std::move(stdoutRead), [&](const gsl::span<char>& buffer) { stdoutNative.append(buffer.data(), buffer.size()); }));
233
234
wil::unique_hfile stderrWrite;
@@ -240,7 +240,7 @@ SubProcess::ProcessOutput SubProcess::RunAndCaptureOutput(DWORD Timeout, HANDLE
240
241
m_stdErr = stderrWrite.get();
242
243
- io.AddHandle(std::make_unique<relay::ReadHandle>(
243
+ io.AddHandle(std::make_unique<io::ReadHandle>(
244
std::move(stderrRead), [&](const gsl::span<char>& buffer) { stderrNative.append(buffer.data(), buffer.size()); }));
245
}
246
else
src/windows/common/WSLCProcessLauncher.cpp
+4
-4
@@ -132,11 +132,11 @@ int RunningWSLCProcess::Wait(DWORD TimeoutMs)
132
return GetExitCode();
133
}
134
135
-RunningWSLCProcess::ProcessResult RunningWSLCProcess::WaitAndCaptureOutput(DWORD TimeoutMs, std::vector<std::unique_ptr<relay::OverlappedIOHandle>>&& ExtraHandles)
135
+RunningWSLCProcess::ProcessResult RunningWSLCProcess::WaitAndCaptureOutput(DWORD TimeoutMs, std::vector<std::unique_ptr<io::OverlappedIOHandle>>&& ExtraHandles)
136
{
137
RunningWSLCProcess::ProcessResult result;
138
139
- relay::MultiHandleWait io;
139
+ io::MultiHandleWait io;
140
141
// Add a callback on IO for each std handle.
142
@@ -148,7 +148,7 @@ RunningWSLCProcess::ProcessResult RunningWSLCProcess::WaitAndCaptureOutput(DWORD
148
result.Output[Index].insert(result.Output[Index].end(), Content.begin(), Content.end());
149
};
150
151
- io.AddHandle(std::make_unique<relay::ReadHandle>(std::move(stdHandle), std::move(ioCallback)));
151
+ io.AddHandle(std::make_unique<io::ReadHandle>(std::move(stdHandle), std::move(ioCallback)));
152
};
153
154
if (WI_IsFlagSet(m_flags, WSLCProcessFlagsTty))
@@ -169,7 +169,7 @@ RunningWSLCProcess::ProcessResult RunningWSLCProcess::WaitAndCaptureOutput(DWORD
169
// Add a callback for when the process exits.
170
auto exitCallback = [&]() { result.Code = GetExitCode(); };
171
172
- io.AddHandle(std::make_unique<relay::EventHandle>(GetExitEvent(), std::move(exitCallback)));
172
+ io.AddHandle(std::make_unique<io::EventHandle>(GetExitEvent(), std::move(exitCallback)));
173
174
io.Run(std::chrono::milliseconds(TimeoutMs));
175
src/windows/common/WSLCProcessLauncher.h
+1
-1
@@ -35,7 +35,7 @@ public:
35
NON_COPYABLE(RunningWSLCProcess);
36
DEFAULT_MOVABLE(RunningWSLCProcess);
37
38
- ProcessResult WaitAndCaptureOutput(DWORD TimeoutMs = INFINITE, std::vector<std::unique_ptr<relay::OverlappedIOHandle>>&& ExtraHandles = {});
38
+ ProcessResult WaitAndCaptureOutput(DWORD TimeoutMs = INFINITE, std::vector<std::unique_ptr<io::OverlappedIOHandle>>&& ExtraHandles = {});
39
int Wait(DWORD TimeoutMs = INFINITE);
40
virtual wil::unique_handle GetStdHandle(int Index) = 0;
41
virtual wil::unique_event GetExitEvent() = 0;
src/windows/common/relay.cpp
-871
@@ -16,21 +16,8 @@ Abstract:
16
#include "relay.hpp"
17
#pragma hdrstop
18
19
-using wsl::windows::common::relay::DockerIORelayHandle;
20
-using wsl::windows::common::relay::EventHandle;
21
-using wsl::windows::common::relay::HandleWrapper;
22
-using wsl::windows::common::relay::HTTPChunkBasedReadHandle;
23
-using wsl::windows::common::relay::IOHandleStatus;
24
-using wsl::windows::common::relay::LineBasedReadHandle;
25
-using wsl::windows::common::relay::MultiHandleWait;
26
-using wsl::windows::common::relay::OverlappedIOHandle;
27
-using wsl::windows::common::relay::ReadHandle;
28
-using wsl::windows::common::relay::ReadSocketMessageHandle;
29
-using wsl::windows::common::relay::RelayHandle;
19
using wsl::windows::common::relay::ScopedMultiRelay;
20
using wsl::windows::common::relay::ScopedRelay;
32
-using wsl::windows::common::relay::SingleAcceptHandle;
33
-using wsl::windows::common::relay::WriteHandle;
21
22
namespace {
23
@@ -991,861 +978,3 @@ try
978
}
979
}
980
CATCH_LOG()
994
-
995
-void MultiHandleWait::AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags)
996
-{
997
- m_handles.emplace_back(flags, std::move(handle));
998
-}
999
-
1000
-void MultiHandleWait::Cancel()
1001
-{
1002
- m_cancel = true;
1003
-}
1004
-bool MultiHandleWait::Run(std::optional<std::chrono::milliseconds> Timeout)
1005
-{
1006
- m_cancel = false; // Run may be called multiple times.
1007
-
1008
- std::optional<std::chrono::steady_clock::time_point> deadline;
1009
-
1010
- if (Timeout.has_value())
1011
- {
1012
- deadline = std::chrono::steady_clock::now() + Timeout.value();
1013
- }
1014
-
1015
- // Run until all handles are completed.
1016
-
1017
- while (!m_handles.empty() && !m_cancel)
1018
- {
1019
- // Schedule IO on each handle until all are either pending, or completed.
1020
- for (size_t i = 0; i < m_handles.size() && !m_cancel; i++)
1021
- {
1022
- while (m_handles[i].second->GetState() == IOHandleStatus::Standby && !m_cancel)
1023
- {
1024
- try
1025
- {
1026
- m_handles[i].second->Schedule();
1027
- }
1028
- catch (...)
1029
- {
1030
- if (WI_IsFlagSet(m_handles[i].first, Flags::IgnoreErrors))
1031
- {
1032
- m_handles[i].second.reset(); // Reset the handle so it can be deleted.
1033
- break;
1034
- }
1035
- else
1036
- {
1037
- throw;
1038
- }
1039
- }
1040
- }
1041
- }
1042
-
1043
- // Remove completed handles from m_handles.
1044
- bool hasHandleToWaitFor = false;
1045
- for (auto it = m_handles.begin(); it != m_handles.end();)
1046
- {
1047
- if (!it->second)
1048
- {
1049
- it = m_handles.erase(it);
1050
- }
1051
- else if (it->second->GetState() == IOHandleStatus::Completed)
1052
- {
1053
- if (WI_IsFlagSet(it->first, Flags::CancelOnCompleted))
1054
- {
1055
- m_cancel = true; // Cancel the IO if a handle with CancelOnCompleted is in the completed state.
1056
- }
1057
-
1058
- it = m_handles.erase(it);
1059
- }
1060
- else
1061
- {
1062
- // If only NeedNotComplete handles are left, we want to exit Run.
1063
- if (WI_IsFlagClear(it->first, Flags::NeedNotComplete))
1064
- {
1065
- hasHandleToWaitFor = true;
1066
- }
1067
- ++it;
1068
- }
1069
- }
1070
-
1071
- if (!hasHandleToWaitFor || m_cancel)
1072
- {
1073
- break;
1074
- }
1075
-
1076
- // Wait for the next operation to complete.
1077
- std::vector<HANDLE> waitHandles;
1078
- for (const auto& e : m_handles)
1079
- {
1080
- waitHandles.emplace_back(e.second->GetHandle());
1081
- }
1082
-
1083
- DWORD waitTimeout = INFINITE;
1084
- if (deadline.has_value())
1085
- {
1086
- auto miliseconds =
1087
- std::chrono::duration_cast<std::chrono::milliseconds>(deadline.value() - std::chrono::steady_clock::now()).count();
1088
-
1089
- waitTimeout = static_cast<DWORD>(std::max(0LL, miliseconds));
1090
- }
1091
-
1092
- auto result = WaitForMultipleObjects(static_cast<DWORD>(waitHandles.size()), waitHandles.data(), false, waitTimeout);
1093
- if (result == WAIT_TIMEOUT)
1094
- {
1095
- THROW_WIN32(ERROR_TIMEOUT);
1096
- }
1097
- else if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size())
1098
- {
1099
- auto index = result - WAIT_OBJECT_0;
1100
-
1101
- try
1102
- {
1103
- m_handles[index].second->Collect();
1104
- }
1105
- catch (...)
1106
- {
1107
- if (WI_IsFlagSet(m_handles[index].first, Flags::IgnoreErrors))
1108
- {
1109
- m_handles.erase(m_handles.begin() + index);
1110
- }
1111
- else
1112
- {
1113
- throw;
1114
- }
1115
- }
1116
- }
1117
- else
1118
- {
1119
- THROW_LAST_ERROR_MSG("Timeout: %lu, Count: %llu", waitTimeout, waitHandles.size());
1120
- }
1121
- }
1122
-
1123
- return !m_cancel;
1124
-}
1125
-
1126
-IOHandleStatus OverlappedIOHandle::GetState() const
1127
-{
1128
- return State;
1129
-}
1130
-
1131
-EventHandle::EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled) :
1132
- Handle(std::move(Handle)), OnSignalled(std::move(OnSignalled))
1133
-{
1134
-}
1135
-
1136
-void EventHandle::Schedule()
1137
-{
1138
- State = IOHandleStatus::Pending;
1139
-}
1140
-
1141
-void EventHandle::Collect()
1142
-{
1143
- State = IOHandleStatus::Completed;
1144
- OnSignalled();
1145
-}
1146
-
1147
-HANDLE EventHandle::GetHandle() const
1148
-{
1149
- return Handle.Get();
1150
-}
1151
-
1152
-ReadHandle::ReadHandle(HandleWrapper&& MovedHandle, std::function<void(const gsl::span<char>& Buffer)>&& OnRead) :
1153
- Handle(std::move(MovedHandle)), OnRead(OnRead), Offset(InitializeFileOffset(Handle.Get()))
1154
-{
1155
- Overlapped.hEvent = Event.get();
1156
-}
1157
-
1158
-ReadHandle::~ReadHandle()
1159
-{
1160
- if (State == IOHandleStatus::Pending)
1161
- {
1162
- CancelPendingIo(Handle.Get(), Overlapped);
1163
- }
1164
-}
1165
-
1166
-void ReadHandle::Schedule()
1167
-{
1168
- WI_ASSERT(State == IOHandleStatus::Standby);
1169
-
1170
- Event.ResetEvent();
1171
-
1172
- // Schedule the read.
1173
- DWORD bytesRead{};
1174
- Overlapped.Offset = Offset.LowPart;
1175
- Overlapped.OffsetHigh = Offset.HighPart;
1176
- auto* bufferData = reinterpret_cast<char*>(Buffer.Span().data());
1177
- if (ReadFile(Handle.Get(), bufferData, static_cast<DWORD>(Buffer.Size()), &bytesRead, &Overlapped))
1178
- {
1179
- Offset.QuadPart += bytesRead;
1180
-
1181
- // Signal the read.
1182
- OnRead(gsl::make_span<char>(bufferData, static_cast<size_t>(bytesRead)));
1183
-
1184
- // ReadFile completed immediately, process the result right away.
1185
- if (bytesRead == 0)
1186
- {
1187
- State = IOHandleStatus::Completed;
1188
- return; // Handle is completely read, don't try again.
1189
- }
1190
-
1191
- // Read was done synchronously, remain in 'standby' state.
1192
- }
1193
- else
1194
- {
1195
- auto error = GetLastError();
1196
- if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE)
1197
- {
1198
- // Signal an empty read for EOF.
1199
- OnRead({});
1200
-
1201
- State = IOHandleStatus::Completed;
1202
- return;
1203
- }
1204
-
1205
- THROW_LAST_ERROR_IF_MSG(error != ERROR_IO_PENDING, "Handle: 0x%p", (void*)Handle.Get());
1206
-
1207
- // The read is pending, update to 'Pending'
1208
- State = IOHandleStatus::Pending;
1209
- }
1210
-}
1211
-
1212
-void ReadHandle::Collect()
1213
-{
1214
- WI_ASSERT(State == IOHandleStatus::Pending);
1215
-
1216
- // Transition back to standby
1217
- State = IOHandleStatus::Standby;
1218
-
1219
- // Complete the read.
1220
- DWORD bytesRead{};
1221
- if (!GetOverlappedResult(Handle.Get(), &Overlapped, &bytesRead, false))
1222
- {
1223
- auto error = GetLastError();
1224
- THROW_WIN32_IF(error, error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE);
1225
-
1226
- // We received ERROR_HANDLE_EOF or ERROR_BROKEN_PIPE. Validate that this was indeed a zero byte read.
1227
- WI_ASSERT(bytesRead == 0);
1228
- }
1229
-
1230
- Offset.QuadPart += bytesRead;
1231
-
1232
- // Signal the read.
1233
- OnRead(gsl::make_span<char>(reinterpret_cast<char*>(Buffer.Span().data()), static_cast<size_t>(bytesRead)));
1234
-
1235
- // Transition to Complete if this was a zero byte read.
1236
- if (bytesRead == 0)
1237
- {
1238
- State = IOHandleStatus::Completed;
1239
- }
1240
-}
1241
-
1242
-HANDLE ReadHandle::GetHandle() const
1243
-{
1244
- return Event.get();
1245
-}
1246
-
1247
-SingleAcceptHandle::SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted) :
1248
- ListenSocket(std::move(ListenSocket)), AcceptedSocket(std::move(AcceptedSocket)), OnAccepted(std::move(OnAccepted))
1249
-{
1250
- Overlapped.hEvent = Event.get();
1251
-}
1252
-
1253
-SingleAcceptHandle::~SingleAcceptHandle()
1254
-{
1255
- if (State == IOHandleStatus::Pending)
1256
- {
1257
- LOG_IF_WIN32_BOOL_FALSE(CancelIoEx(ListenSocket.Get(), &Overlapped));
1258
-
1259
- DWORD bytesProcessed{};
1260
- DWORD flagsReturned{};
1261
- if (!WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesProcessed, TRUE, &flagsReturned))
1262
- {
1263
- auto error = GetLastError();
1264
- LOG_LAST_ERROR_IF(error != ERROR_CONNECTION_ABORTED && error != ERROR_OPERATION_ABORTED);
1265
- }
1266
- }
1267
-}
1268
-
1269
-void SingleAcceptHandle::Schedule()
1270
-{
1271
- WI_ASSERT(State == IOHandleStatus::Standby);
1272
-
1273
- // Schedule the accept.
1274
- DWORD bytesReturned{};
1275
- if (AcceptEx((SOCKET)ListenSocket.Get(), (SOCKET)AcceptedSocket.Get(), &AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &bytesReturned, &Overlapped))
1276
- {
1277
- // Accept completed immediately.
1278
- State = IOHandleStatus::Completed;
1279
- OnAccepted();
1280
- }
1281
- else
1282
- {
1283
- auto error = WSAGetLastError();
1284
- THROW_HR_IF_MSG(HRESULT_FROM_WIN32(error), error != ERROR_IO_PENDING, "Handle: 0x%p", (void*)ListenSocket.Get());
1285
-
1286
- State = IOHandleStatus::Pending;
1287
- }
1288
-}
1289
-
1290
-void SingleAcceptHandle::Collect()
1291
-{
1292
- WI_ASSERT(State == IOHandleStatus::Pending);
1293
-
1294
- DWORD bytesReceived{};
1295
- DWORD flagsReturned{};
1296
-
1297
- THROW_IF_WIN32_BOOL_FALSE(WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesReceived, false, &flagsReturned));
1298
-
1299
- State = IOHandleStatus::Completed;
1300
- OnAccepted();
1301
-}
1302
-
1303
-HANDLE SingleAcceptHandle::GetHandle() const
1304
-{
1305
- return Event.get();
1306
-}
1307
-
1308
-LineBasedReadHandle::LineBasedReadHandle(HandleWrapper&& Handle, std::function<void(const gsl::span<char>& Line)>&& OnLine, bool Crlf) :
1309
- ReadHandle(std::move(Handle), [this](const gsl::span<char>& Buffer) { OnRead(Buffer); }), OnLine(OnLine), Crlf(Crlf)
1310
-{
1311
-}
1312
-
1313
-LineBasedReadHandle::~LineBasedReadHandle()
1314
-{
1315
- // N.B. PendingBuffer can contain remaining data is an exception was thrown during parsing.
1316
-}
1317
-
1318
-void LineBasedReadHandle::OnRead(const gsl::span<char>& Buffer)
1319
-{
1320
- // If we reach of the end, signal a line with the remaining buffer.
1321
- if (Buffer.empty() && !PendingBuffer.empty())
1322
- {
1323
- OnLine(PendingBuffer);
1324
- PendingBuffer.clear();
1325
- return;
1326
- }
1327
-
1328
- auto begin = Buffer.begin();
1329
- auto end = std::ranges::find(Buffer, Crlf ? '\r' : '\n');
1330
- while (end != Buffer.end())
1331
- {
1332
- if (Crlf)
1333
- {
1334
- end++; // Move to the following '\n'
1335
-
1336
- if (end == Buffer.end() || *end != '\n') // Incomplete CRLF sequence. Append to buffer and continue.
1337
- {
1338
- PendingBuffer.insert(PendingBuffer.end(), begin, end);
1339
- begin = end;
1340
- end = std::ranges::find(end, Buffer.end(), '\r');
1341
- continue;
1342
- }
1343
- }
1344
-
1345
- // Discard the '\r' in CRLF mode.
1346
- PendingBuffer.insert(PendingBuffer.end(), begin, Crlf ? end - 1 : end);
1347
-
1348
- if (!PendingBuffer.empty())
1349
- {
1350
- OnLine(PendingBuffer);
1351
- PendingBuffer.clear();
1352
- }
1353
-
1354
- begin = end + 1;
1355
- end = std::ranges::find(begin, Buffer.end(), Crlf ? '\r' : '\n');
1356
- }
1357
-
1358
- PendingBuffer.insert(PendingBuffer.end(), begin, end);
1359
-}
1360
-
1361
-HTTPChunkBasedReadHandle::HTTPChunkBasedReadHandle(HandleWrapper&& MovedHandle, std::function<void(const gsl::span<char>& Line)>&& OnChunk) :
1362
- ReadHandle(std::move(MovedHandle), [this](const gsl::span<char>& Buffer) { OnRead(Buffer); }), OnChunk(std::move(OnChunk))
1363
-{
1364
-}
1365
-
1366
-HTTPChunkBasedReadHandle::~HTTPChunkBasedReadHandle()
1367
-{
1368
- // N.B. PendingBuffer can contain remaining data is an exception was thrown during parsing.
1369
- LOG_HR_IF(E_UNEXPECTED, !PendingBuffer.empty() || PendingChunkSize != 0 || ExpectHeader);
1370
-}
1371
-
1372
-void HTTPChunkBasedReadHandle::OnRead(const gsl::span<char>& Input)
1373
-{
1374
- // See: https://httpwg.org/specs/rfc9112.html#field.transfer-encoding
1375
-
1376
- if (Input.empty())
1377
- {
1378
- // N.B. The body can be terminated by a zero-length chunk.
1379
- THROW_HR_IF(E_INVALIDARG, PendingChunkSize != 0 || ExpectHeader);
1380
- }
1381
-
1382
- auto buffer = Input;
1383
-
1384
- auto advance = [&](size_t count) {
1385
- WI_ASSERT(buffer.size() >= count);
1386
- buffer = buffer.subspan(count);
1387
- };
1388
-
1389
- while (!buffer.empty())
1390
- {
1391
- if (PendingChunkSize == 0)
1392
- {
1393
- // Consume CRLF's between chunks.
1394
- if (PendingBuffer.empty() && (buffer.front() == '\r' || buffer.front() == '\n'))
1395
- {
1396
- advance(1);
1397
- continue;
1398
- }
1399
-
1400
- ExpectHeader = true;
1401
-
1402
- auto end = std::ranges::find(buffer, '\n');
1403
- PendingBuffer.insert(PendingBuffer.end(), buffer.begin(), end);
1404
- if (end == buffer.end())
1405
- {
1406
- // Incomplete size header, buffer until next read.
1407
- break;
1408
- }
1409
- // Advance beyond the LF
1410
- advance(end - buffer.begin() + 1);
1411
-
1412
- THROW_HR_IF_MSG(
1413
- E_INVALIDARG,
1414
- PendingBuffer.size() < 2 || PendingBuffer.back() != '\r',
1415
- "Malformed chunk header: %hs",
1416
- PendingBuffer.c_str());
1417
- PendingBuffer.erase(PendingBuffer.end() - 1, PendingBuffer.end()); // Remove CR.
1418
-
1419
-#ifdef WSLC_HTTP_DEBUG
1420
-
1421
- WSL_LOG("HTTPChunkHeader", TraceLoggingValue(PendingBuffer.c_str(), "Size"));
1422
-
1423
-#endif
1424
-
1425
- try
1426
- {
1427
- size_t parsed{};
1428
- PendingChunkSize = std::stoul(PendingBuffer.c_str(), &parsed, 16);
1429
- THROW_HR_IF(E_INVALIDARG, parsed != PendingBuffer.size());
1430
- }
1431
- catch (...)
1432
- {
1433
- THROW_HR_MSG(E_INVALIDARG, "Failed to parse chunk size: %hs", PendingBuffer.c_str());
1434
- }
1435
-
1436
- ExpectHeader = false;
1437
- PendingBuffer.clear();
1438
- }
1439
- else
1440
- {
1441
- // Consume the chunk.
1442
- auto consumedBytes = std::min(PendingChunkSize, buffer.size());
1443
- PendingBuffer.append(buffer.data(), consumedBytes);
1444
- advance(consumedBytes);
1445
-
1446
- WI_ASSERT(PendingChunkSize >= consumedBytes);
1447
- PendingChunkSize -= consumedBytes;
1448
-
1449
- if (PendingChunkSize == 0)
1450
- {
1451
-
1452
-#ifdef WSLC_HTTP_DEBUG
1453
-
1454
- WSL_LOG("HTTPChunk", TraceLoggingValue(PendingBuffer.c_str(), "Content"));
1455
-
1456
-#endif
1457
- OnChunk(PendingBuffer);
1458
- PendingBuffer.clear();
1459
- }
1460
- }
1461
- }
1462
-}
1463
-
1464
-ReadSocketMessageHandle::ReadSocketMessageHandle(
1465
- HandleWrapper&& MovedSocket, std::vector<gsl::byte>& Buffer, std::function<void(const gsl::span<gsl::byte>& Message)>&& OnMessage) :
1466
- Socket(std::move(MovedSocket)), Buffer(Buffer), OnMessage(std::move(OnMessage))
1467
-{
1468
- Overlapped.hEvent = Event.get();
1469
-
1470
- if (Buffer.size() < sizeof(MESSAGE_HEADER))
1471
- {
1472
- Buffer.resize(sizeof(MESSAGE_HEADER));
1473
- }
1474
-}
1475
-
1476
-ReadSocketMessageHandle::~ReadSocketMessageHandle()
1477
-{
1478
- if (State == IOHandleStatus::Pending)
1479
- {
1480
- CancelPendingIo((SOCKET)Socket.Get(), Overlapped);
1481
- }
1482
-}
1483
-
1484
-void ReadSocketMessageHandle::ScheduleRecv()
1485
-{
1486
- Event.ResetEvent();
1487
-
1488
- auto target = gsl::make_span(Buffer).subspan(CurrentOffset, BytesRemaining);
1489
- WSABUF wsaBuf = {gsl::narrow_cast<ULONG>(target.size()), reinterpret_cast<CHAR*>(target.data())};
1490
- DWORD bytesRead{};
1491
- DWORD flags = 0;
1492
- if (WSARecv(reinterpret_cast<SOCKET>(Socket.Get()), &wsaBuf, 1, &bytesRead, &flags, &Overlapped, nullptr) == 0)
1493
- {
1494
- ProcessRecvResult(bytesRead);
1495
- }
1496
- else
1497
- {
1498
- auto error = WSAGetLastError();
1499
- if (error == WSAECONNABORTED || error == WSAECONNRESET)
1500
- {
1501
- ProcessRecvResult(0);
1502
- return;
1503
- }
1504
-
1505
- THROW_HR_IF_MSG(HRESULT_FROM_WIN32(error), error != WSA_IO_PENDING, "Socket: 0x%p", (void*)Socket.Get());
1506
-
1507
- State = IOHandleStatus::Pending;
1508
- }
1509
-}
1510
-
1511
-void ReadSocketMessageHandle::ProcessRecvResult(DWORD BytesRead)
1512
-{
1513
- if (BytesRead == 0)
1514
- {
1515
- // If the socket was closed before any bytes of the next message were read, signal a clean end-of-stream.
1516
- // If some bytes were already buffered, the peer closed mid-message which is a protocol error.
1517
- THROW_HR_IF_MSG(
1518
- E_UNEXPECTED,
1519
- CurrentOffset > 0,
1520
- "Socket closed before a complete message could be read. ReadingHeader: %d, CurrentOffset: %zu, BytesRemaining: %zu",
1521
- ReadingHeader,
1522
- CurrentOffset,
1523
- BytesRemaining);
1524
-
1525
- OnMessage({});
1526
- State = IOHandleStatus::Completed;
1527
- return;
1528
- }
1529
-
1530
- CurrentOffset += BytesRead;
1531
- BytesRemaining -= BytesRead;
1532
-
1533
- if (BytesRemaining > 0)
1534
- {
1535
- return;
1536
- }
1537
-
1538
- if (ReadingHeader)
1539
- {
1540
- auto messageSize = gslhelpers::get_struct<MESSAGE_HEADER>(gsl::make_span(Buffer.data(), sizeof(MESSAGE_HEADER)))->MessageSize;
1541
-
1542
- THROW_HR_IF_MSG(E_UNEXPECTED, messageSize < sizeof(MESSAGE_HEADER), "Unexpected message size: %u", messageSize);
1543
- THROW_HR_IF_MSG(E_UNEXPECTED, messageSize > 4 * 1024 * 1024, "Message size too large: %u", messageSize);
1544
-
1545
- if (messageSize == sizeof(MESSAGE_HEADER))
1546
- {
1547
- OnMessage(gsl::make_span(Buffer.data(), messageSize));
1548
- State = IOHandleStatus::Completed;
1549
- return;
1550
- }
1551
-
1552
- if (Buffer.size() < messageSize)
1553
- {
1554
- Buffer.resize(messageSize);
1555
- }
1556
-
1557
- ReadingHeader = false;
1558
- CurrentOffset = sizeof(MESSAGE_HEADER);
1559
- BytesRemaining = messageSize - sizeof(MESSAGE_HEADER);
1560
- }
1561
- else
1562
- {
1563
- auto messageSize = gslhelpers::get_struct<MESSAGE_HEADER>(gsl::make_span(Buffer.data(), sizeof(MESSAGE_HEADER)))->MessageSize;
1564
- OnMessage(gsl::make_span(Buffer.data(), messageSize));
1565
- State = IOHandleStatus::Completed;
1566
- }
1567
-}
1568
-
1569
-void ReadSocketMessageHandle::Schedule()
1570
-{
1571
- WI_ASSERT(State == IOHandleStatus::Standby);
1572
- ScheduleRecv();
1573
-}
1574
-
1575
-void ReadSocketMessageHandle::Collect()
1576
-{
1577
- WI_ASSERT(State == IOHandleStatus::Pending);
1578
-
1579
- State = IOHandleStatus::Standby;
1580
-
1581
- DWORD bytesRead{};
1582
- DWORD flags{};
1583
- if (!WSAGetOverlappedResult(reinterpret_cast<SOCKET>(Socket.Get()), &Overlapped, &bytesRead, FALSE, &flags))
1584
- {
1585
- long error = WSAGetLastError();
1586
- THROW_WIN32_IF(error, error != WSAECONNABORTED && error != WSAECONNRESET);
1587
-
1588
- WI_ASSERT(bytesRead == 0);
1589
- }
1590
-
1591
- ProcessRecvResult(bytesRead);
1592
-}
1593
-
1594
-HANDLE ReadSocketMessageHandle::GetHandle() const
1595
-{
1596
- return Event.get();
1597
-}
1598
-
1599
-WriteHandle::WriteHandle(HandleWrapper&& MovedHandle, const std::vector<char>& Source) :
1600
- Handle(std::move(MovedHandle)), Buffer(Source.size()), Offset(InitializeFileOffset(Handle.Get()))
1601
-{
1602
- std::memcpy(Buffer.Span().data(), Source.data(), Source.size());
1603
- Overlapped.hEvent = Event.get();
1604
-}
1605
-
1606
-WriteHandle::WriteHandle(HandleWrapper&& MovedHandle, gsl::span<gsl::byte> Source) :
1607
- Handle(std::move(MovedHandle)), Buffer(Source), Offset(InitializeFileOffset(Handle.Get()))
1608
-{
1609
- Overlapped.hEvent = Event.get();
1610
-}
1611
-
1612
-WriteHandle::~WriteHandle()
1613
-{
1614
- if (State == IOHandleStatus::Pending)
1615
- {
1616
- CancelPendingIo(Handle.Get(), Overlapped);
1617
- }
1618
-}
1619
-
1620
-void WriteHandle::Schedule()
1621
-{
1622
- WI_ASSERT(State == IOHandleStatus::Standby);
1623
-
1624
- Event.ResetEvent();
1625
-
1626
- Overlapped.Offset = Offset.LowPart;
1627
- Overlapped.OffsetHigh = Offset.HighPart;
1628
-
1629
- // Schedule the write.
1630
- const auto buffer = Buffer.Span();
1631
- DWORD bytesWritten{};
1632
- if (WriteFile(Handle.Get(), buffer.data(), static_cast<DWORD>(buffer.size()), &bytesWritten, &Overlapped))
1633
- {
1634
- Offset.QuadPart += bytesWritten;
1635
-
1636
- Buffer.Consume(bytesWritten);
1637
- if (Buffer.Size() == 0)
1638
- {
1639
- State = IOHandleStatus::Completed;
1640
- }
1641
- }
1642
- else
1643
- {
1644
- auto error = GetLastError();
1645
- THROW_LAST_ERROR_IF_MSG(error != ERROR_IO_PENDING, "Handle: 0x%p", (void*)Handle.Get());
1646
-
1647
- // The write is pending, update to 'Pending'
1648
- State = IOHandleStatus::Pending;
1649
- }
1650
-}
1651
-
1652
-void WriteHandle::Collect()
1653
-{
1654
- WI_ASSERT(State == IOHandleStatus::Pending);
1655
-
1656
- // Transition back to standby
1657
- State = IOHandleStatus::Standby;
1658
-
1659
- // Complete the write.
1660
- DWORD bytesWritten{};
1661
- THROW_IF_WIN32_BOOL_FALSE(GetOverlappedResult(Handle.Get(), &Overlapped, &bytesWritten, false));
1662
- Offset.QuadPart += bytesWritten;
1663
-
1664
- Buffer.Consume(bytesWritten);
1665
- if (Buffer.Size() == 0)
1666
- {
1667
- State = IOHandleStatus::Completed;
1668
- }
1669
-}
1670
-
1671
-void WriteHandle::Push(const gsl::span<char>& Content)
1672
-{
1673
- // Don't write if a WriteFile() is pending, since that could cause the buffer to reallocate.
1674
- WI_ASSERT(State == IOHandleStatus::Standby || State == IOHandleStatus::Completed);
1675
- WI_ASSERT(!Content.empty());
1676
-
1677
- // Resize() throws E_UNEXPECTED if Buffer does not own its storage.
1678
- Buffer.Append(Content);
1679
-
1680
- State = IOHandleStatus::Standby;
1681
-}
1682
-
1683
-HANDLE WriteHandle::GetHandle() const
1684
-{
1685
- return Event.get();
1686
-}
1687
-
1688
-DockerIORelayHandle::DockerIORelayHandle(HandleWrapper&& ReadHandle, HandleWrapper&& Stdout, HandleWrapper&& Stderr, Format ReadFormat) :
1689
- WriteStdout(std::move(Stdout)), WriteStderr(std::move(Stderr))
1690
-{
1691
- if (ReadFormat == Format::HttpChunked)
1692
- {
1693
- Read = std::make_unique<HTTPChunkBasedReadHandle>(
1694
- std::move(ReadHandle), [this](const gsl::span<char>& Line) { this->OnRead(Line); });
1695
- }
1696
- else
1697
- {
1698
- Read = std::make_unique<relay::ReadHandle>(
1699
- std::move(ReadHandle), [this](const gsl::span<char>& Buffer) { this->OnRead(Buffer); });
1700
- }
1701
-}
1702
-
1703
-void DockerIORelayHandle::Schedule()
1704
-{
1705
- WI_ASSERT(State == IOHandleStatus::Standby);
1706
- WI_ASSERT(Read->GetState() != IOHandleStatus::Pending);
1707
-
1708
- // If we have an active handle and a buffer, try to flush that first.
1709
- if (ActiveHandle != nullptr && !PendingBuffer.empty())
1710
- {
1711
- // Push the data to the selected handle.
1712
- DWORD bytesToWrite = std::min(static_cast<DWORD>(RemainingBytes), static_cast<DWORD>(PendingBuffer.size()));
1713
-
1714
- ActiveHandle->Push(gsl::make_span(PendingBuffer.data(), bytesToWrite));
1715
-
1716
- // Consume the written bytes.
1717
- RemainingBytes -= bytesToWrite;
1718
- PendingBuffer.erase(PendingBuffer.begin(), PendingBuffer.begin() + bytesToWrite);
1719
-
1720
- // Schedule the write.
1721
- ActiveHandle->Schedule();
1722
-
1723
- // If the write is pending, update to 'Pending'
1724
- if (ActiveHandle->GetState() == IOHandleStatus::Pending)
1725
- {
1726
- State = IOHandleStatus::Pending;
1727
- }
1728
- else if (ActiveHandle->GetState() == IOHandleStatus::Completed)
1729
- {
1730
- if (RemainingBytes == 0)
1731
- {
1732
- // Switch back to reading if we've written all bytes for this chunk.
1733
- ActiveHandle = nullptr;
1734
-
1735
- ProcessNextHeader();
1736
- }
1737
- }
1738
- }
1739
- else
1740
- {
1741
- if (Read->GetState() == IOHandleStatus::Completed)
1742
- {
1743
- LOG_HR_IF(E_UNEXPECTED, ActiveHandle != nullptr);
1744
-
1745
- // No more data to read, we're done.
1746
- State = IOHandleStatus::Completed;
1747
- return;
1748
- }
1749
-
1750
- // Schedule a read from the input.
1751
- Read->Schedule();
1752
- if (Read->GetState() == IOHandleStatus::Pending)
1753
- {
1754
- State = IOHandleStatus::Pending;
1755
- }
1756
- }
1757
-}
1758
-
1759
-void DockerIORelayHandle::Collect()
1760
-{
1761
- WI_ASSERT(State == IOHandleStatus::Pending);
1762
-
1763
- if (ActiveHandle != nullptr && ActiveHandle->GetState() == IOHandleStatus::Pending)
1764
- {
1765
- // Complete the write.
1766
- ActiveHandle->Collect();
1767
-
1768
- // If the write is completed, switch back to reading.
1769
- if (RemainingBytes == 0)
1770
- {
1771
- if (ActiveHandle->GetState() == IOHandleStatus::Completed)
1772
- {
1773
- ActiveHandle = nullptr;
1774
- }
1775
- }
1776
-
1777
- // Transition back to standby if there's still data to read.
1778
- // Otherwise switch to Completed since everything is done.
1779
- if (Read->GetState() == IOHandleStatus::Completed)
1780
- {
1781
- LOG_HR_IF(E_UNEXPECTED, RemainingBytes != 0);
1782
-
1783
- State = IOHandleStatus::Completed;
1784
- }
1785
- else
1786
- {
1787
- State = IOHandleStatus::Standby;
1788
- }
1789
- }
1790
- else
1791
- {
1792
- WI_ASSERT(Read->GetState() == IOHandleStatus::Pending);
1793
-
1794
- // Complete the read.
1795
- Read->Collect();
1796
-
1797
- // Transition back to standby.
1798
- State = IOHandleStatus::Standby;
1799
- }
1800
-}
1801
-
1802
-HANDLE DockerIORelayHandle::GetHandle() const
1803
-{
1804
- if (ActiveHandle != nullptr && ActiveHandle->GetState() == IOHandleStatus::Pending)
1805
- {
1806
- return ActiveHandle->GetHandle();
1807
- }
1808
- else
1809
- {
1810
- return Read->GetHandle();
1811
- }
1812
-}
1813
-
1814
-void DockerIORelayHandle::ProcessNextHeader()
1815
-{
1816
- if (PendingBuffer.size() < sizeof(MultiplexedHeader))
1817
- {
1818
- // Not enough data for a header yet.
1819
- return;
1820
- }
1821
-
1822
- const auto* header = reinterpret_cast<const MultiplexedHeader*>(PendingBuffer.data());
1823
- RemainingBytes = ntohl(header->Length);
1824
-
1825
- if (header->Fd == 1)
1826
- {
1827
- ActiveHandle = &WriteStdout;
1828
- }
1829
- else if (header->Fd == 2)
1830
- {
1831
- ActiveHandle = &WriteStderr;
1832
- }
1833
- else
1834
- {
1835
- THROW_HR_MSG(E_INVALIDARG, "Invalid Docker IO multiplexed header fd: %u", header->Fd);
1836
- }
1837
-
1838
- // Consume the header.
1839
- PendingBuffer.erase(PendingBuffer.begin(), PendingBuffer.begin() + sizeof(MultiplexedHeader));
1840
-}
1841
-
1842
-void DockerIORelayHandle::OnRead(const gsl::span<char>& Buffer)
1843
-{
1844
- PendingBuffer.insert(PendingBuffer.end(), Buffer.begin(), Buffer.end());
1845
-
1846
- if (ActiveHandle == nullptr)
1847
- {
1848
- // If no handle is active, expect a header.
1849
- ProcessNextHeader();
1850
- }
1851
-}
src/windows/common/relay.hpp
+3
-457
@@ -16,11 +16,12 @@ Abstract:
16
17
#include <winsock2.h>
18
#include "ConsoleState.h"
19
-
20
-#define LX_RELAY_BUFFER_SIZE 0x1000
19
+#include "HandleIO.h"
20
21
namespace wsl::windows::common::relay {
22
23
+using namespace wsl::windows::common::io;
24
+
25
std::thread CreateThread(_In_ HANDLE InputHandle, _In_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle = nullptr, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);
26
27
std::thread CreateThread(_In_ wil::unique_handle&& InputHandle, _In_ HANDLE OutputHandle, _In_opt_ HANDLE ExitHandle = nullptr, _In_ size_t BufferSize = LX_RELAY_BUFFER_SIZE);
@@ -157,459 +158,4 @@ private:
158
std::function<void()> m_onDestroy;
159
};
160
160
-enum class IOHandleStatus
161
-{
162
- Standby,
163
- Pending,
164
- Completed
165
-};
166
-
167
-struct HandleWrapper
168
-{
169
- DEFAULT_MOVABLE(HandleWrapper);
170
- NON_COPYABLE(HandleWrapper)
171
-
172
- HandleWrapper(
173
- wil::unique_handle&& handle, std::function<void()>&& OnClose = []() {}) :
174
- Handle(handle.get()), OwnedHandle(std::move(handle)), OnClose(std::move(OnClose))
175
- {
176
- }
177
-
178
- HandleWrapper(
179
- wil::unique_socket&& handle, std::function<void()>&& OnClose = []() {}) :
180
- Handle((HANDLE)handle.get()), OwnedHandle(wil::unique_socket{handle.release()}), OnClose(std::move(OnClose))
181
- {
182
- }
183
-
184
- HandleWrapper(
185
- wil::unique_event&& handle, std::function<void()>&& OnClose = []() {}) :
186
- Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))
187
- {
188
- }
189
-
190
- HandleWrapper(
191
- SOCKET handle, std::function<void()>&& OnClose = []() {}) :
192
- Handle(reinterpret_cast<HANDLE>(handle)), OnClose(std::move(OnClose))
193
- {
194
- }
195
-
196
- HandleWrapper(HANDLE handle, std::function<void()>&& OnClose = []() {}) : Handle(handle), OnClose(std::move(OnClose))
197
- {
198
- }
199
-
200
- HandleWrapper(
201
- wil::unique_hfile&& handle, std::function<void()>&& OnClose = []() {}) :
202
- Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))
203
- {
204
- }
205
-
206
- ~HandleWrapper()
207
- {
208
- Reset();
209
- }
210
-
211
- HANDLE Get() const
212
- {
213
- return Handle;
214
- }
215
-
216
- void Reset()
217
- {
218
- if (OnClose != nullptr)
219
- {
220
- OnClose();
221
- OnClose = nullptr;
222
- }
223
-
224
- OwnedHandle = {};
225
- Handle = nullptr;
226
- }
227
-
228
-private:
229
- HANDLE Handle{};
230
- std::variant<wil::unique_handle, wil::unique_socket> OwnedHandle;
231
- std::function<void()> OnClose;
232
-};
233
-
234
-// A buffer that may either own its underlying storage (constructed from a size, allocating an
235
-// internal std::vector<char>) or borrow it from a caller-provided gsl::span<gsl::byte>.
236
-class BufferWrapper
237
-{
238
-public:
239
- DEFAULT_MOVABLE(BufferWrapper);
240
- NON_COPYABLE(BufferWrapper);
241
-
242
- explicit BufferWrapper(size_t size) : m_owned(std::in_place, size)
243
- {
244
- }
245
-
246
- explicit BufferWrapper(gsl::span<gsl::byte> span) : m_unowned(span)
247
- {
248
- }
249
-
250
- bool Owned() const noexcept
251
- {
252
- return m_owned.has_value();
253
- }
254
-
255
- void Resize(size_t size)
256
- {
257
- THROW_HR_IF_MSG(E_UNEXPECTED, !Owned(), "BufferWrapper::Resize called on a non-owned buffer");
258
- m_owned->resize(size);
259
- }
260
-
261
- void Append(gsl::span<char> Span)
262
- {
263
- THROW_HR_IF_MSG(E_UNEXPECTED, !Owned(), "BufferWrapper::Append called on a non-owned buffer");
264
-
265
- m_owned->insert(m_owned->end(), Span.begin(), Span.end());
266
- }
267
-
268
- void Consume(size_t bytes) noexcept
269
- {
270
- WI_ASSERT(bytes <= Size());
271
- if (Owned())
272
- {
273
- m_owned->erase(m_owned->begin(), m_owned->begin() + bytes);
274
- }
275
- else
276
- {
277
- m_unowned = m_unowned.subspan(bytes);
278
- }
279
- }
280
-
281
- gsl::span<gsl::byte> Span() noexcept
282
- {
283
- return Owned() ? gsl::make_span(reinterpret_cast<gsl::byte*>(m_owned->data()), m_owned->size()) : m_unowned;
284
- }
285
-
286
- size_t Size() const noexcept
287
- {
288
- return Owned() ? m_owned->size() : m_unowned.size();
289
- }
290
-
291
-private:
292
- std::optional<std::vector<char>> m_owned;
293
- gsl::span<gsl::byte> m_unowned;
294
-};
295
-
296
-class OverlappedIOHandle
297
-{
298
-public:
299
- NON_COPYABLE(OverlappedIOHandle)
300
- NON_MOVABLE(OverlappedIOHandle)
301
-
302
- OverlappedIOHandle() = default;
303
- virtual ~OverlappedIOHandle() = default;
304
- virtual void Schedule() = 0;
305
- virtual void Collect() = 0;
306
- virtual HANDLE GetHandle() const = 0;
307
- IOHandleStatus GetState() const;
308
-
309
-protected:
310
- IOHandleStatus State = IOHandleStatus::Standby;
311
-};
312
-
313
-class EventHandle : public OverlappedIOHandle
314
-{
315
-public:
316
- NON_COPYABLE(EventHandle)
317
- NON_MOVABLE(EventHandle)
318
-
319
- EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled = []() {});
320
- void Schedule() override;
321
- void Collect() override;
322
- HANDLE GetHandle() const override;
323
-
324
-private:
325
- HandleWrapper Handle;
326
- std::function<void()> OnSignalled;
327
-};
328
-
329
-class ReadHandle : public OverlappedIOHandle
330
-{
331
-public:
332
- NON_COPYABLE(ReadHandle);
333
- NON_MOVABLE(ReadHandle);
334
-
335
- ReadHandle(HandleWrapper&& MovedHandle, std::function<void(const gsl::span<char>& Buffer)>&& OnRead);
336
- virtual ~ReadHandle();
337
-
338
- void Schedule() override;
339
- void Collect() override;
340
- HANDLE GetHandle() const override;
341
-
342
-private:
343
- HandleWrapper Handle;
344
- std::function<void(const gsl::span<char>& Buffer)> OnRead;
345
- wil::unique_event Event{wil::EventOptions::ManualReset};
346
- OVERLAPPED Overlapped{};
347
- BufferWrapper Buffer{LX_RELAY_BUFFER_SIZE};
348
- LARGE_INTEGER Offset{};
349
-};
350
-
351
-class SingleAcceptHandle : public OverlappedIOHandle
352
-{
353
-public:
354
- NON_COPYABLE(SingleAcceptHandle)
355
- NON_MOVABLE(SingleAcceptHandle)
356
-
357
- SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted);
358
- ~SingleAcceptHandle();
359
-
360
- void Schedule() override;
361
- void Collect() override;
362
- HANDLE GetHandle() const override;
363
-
364
-private:
365
- HandleWrapper ListenSocket;
366
- HandleWrapper AcceptedSocket;
367
- wil::unique_event Event{wil::EventOptions::ManualReset};
368
- OVERLAPPED Overlapped{};
369
- std::function<void()> OnAccepted;
370
- char AcceptBuffer[2 * sizeof(SOCKADDR_STORAGE)];
371
-};
372
-
373
-class LineBasedReadHandle : public ReadHandle
374
-{
375
-public:
376
- NON_COPYABLE(LineBasedReadHandle);
377
- NON_MOVABLE(LineBasedReadHandle);
378
-
379
- LineBasedReadHandle(HandleWrapper&& Handle, std::function<void(const gsl::span<char>& Buffer)>&& OnLine, bool Crlf);
380
- ~LineBasedReadHandle();
381
-
382
-private:
383
- void OnRead(const gsl::span<char>& Buffer);
384
-
385
- std::function<void(const gsl::span<char>& Buffer)> OnLine;
386
- std::string PendingBuffer;
387
- bool Crlf{};
388
-};
389
-
390
-class HTTPChunkBasedReadHandle : public ReadHandle
391
-{
392
-public:
393
- NON_COPYABLE(HTTPChunkBasedReadHandle);
394
- NON_MOVABLE(HTTPChunkBasedReadHandle);
395
-
396
- HTTPChunkBasedReadHandle(HandleWrapper&& Handler, std::function<void(const gsl::span<char>& Buffer)>&& OnChunk);
397
- ~HTTPChunkBasedReadHandle();
398
-
399
- void OnRead(const gsl::span<char>& Line);
400
-
401
-private:
402
- std::function<void(const gsl::span<char>& Buffer)> OnChunk;
403
- std::string PendingBuffer;
404
- uint64_t PendingChunkSize = 0;
405
- bool ExpectHeader = true;
406
-};
407
-
408
-class ReadSocketMessageHandle : public OverlappedIOHandle
409
-{
410
-public:
411
- NON_COPYABLE(ReadSocketMessageHandle);
412
- NON_MOVABLE(ReadSocketMessageHandle);
413
-
414
- ReadSocketMessageHandle(HandleWrapper&& Socket, std::vector<gsl::byte>& Buffer, std::function<void(const gsl::span<gsl::byte>& Message)>&& OnMessage);
415
- ~ReadSocketMessageHandle();
416
-
417
- void Schedule() override;
418
- void Collect() override;
419
- HANDLE GetHandle() const override;
420
-
421
-private:
422
- void ScheduleRecv();
423
- void ProcessRecvResult(DWORD BytesRead);
424
-
425
- HandleWrapper Socket;
426
- std::vector<gsl::byte>& Buffer;
427
- std::function<void(const gsl::span<gsl::byte>& Message)> OnMessage;
428
- wil::unique_event Event{wil::EventOptions::ManualReset};
429
- OVERLAPPED Overlapped{};
430
- bool ReadingHeader = true;
431
- size_t BytesRemaining = sizeof(MESSAGE_HEADER);
432
- size_t CurrentOffset = 0;
433
-};
434
-
435
-class WriteHandle : public OverlappedIOHandle
436
-{
437
-public:
438
- NON_COPYABLE(WriteHandle);
439
- NON_MOVABLE(WriteHandle);
440
-
441
- WriteHandle(HandleWrapper&& Handle, const std::vector<char>& Buffer = {});
442
- WriteHandle(HandleWrapper&& Handle, gsl::span<gsl::byte> Span);
443
- ~WriteHandle();
444
- void Schedule() override;
445
- void Collect() override;
446
- HANDLE GetHandle() const override;
447
- void Push(const gsl::span<char>& Buffer);
448
-
449
-private:
450
- HandleWrapper Handle;
451
- wil::unique_event Event{wil::EventOptions::ManualReset};
452
- OVERLAPPED Overlapped{};
453
- BufferWrapper Buffer;
454
- LARGE_INTEGER Offset{};
455
-};
456
-
457
-template <typename TRead = ReadHandle>
458
-class RelayHandle : public OverlappedIOHandle
459
-{
460
-public:
461
- NON_COPYABLE(RelayHandle);
462
- NON_MOVABLE(RelayHandle);
463
-
464
- RelayHandle(HandleWrapper&& Input, HandleWrapper&& Output) :
465
- Read(std::move(Input), [this](const gsl::span<char>& Buffer) { return OnRead(Buffer); }), Write(std::move(Output))
466
- {
467
- }
468
-
469
- void Schedule() override
470
- {
471
- WI_ASSERT(State == IOHandleStatus::Standby);
472
-
473
- // If the Buffer is empty, then we're reading.
474
- if (PendingBuffer.empty())
475
- {
476
- // If the output buffer is empty and the reading end is completed, then we're done.
477
- if (Read.GetState() == IOHandleStatus::Completed)
478
- {
479
- State = IOHandleStatus::Completed;
480
- return;
481
- }
482
-
483
- Read.Schedule();
484
-
485
- // If the read is pending, update to 'Pending'
486
- if (Read.GetState() == IOHandleStatus::Pending)
487
- {
488
- State = IOHandleStatus::Pending;
489
- }
490
- }
491
- else
492
- {
493
- Write.Push(PendingBuffer);
494
- PendingBuffer.clear();
495
-
496
- Write.Schedule();
497
-
498
- if (Write.GetState() == IOHandleStatus::Pending)
499
- {
500
- // The write is pending, update to 'Pending'
501
- State = IOHandleStatus::Pending;
502
- }
503
- }
504
- }
505
-
506
- void Collect() override
507
- {
508
- WI_ASSERT(State == IOHandleStatus::Pending);
509
-
510
- // Transition back to standby
511
- State = IOHandleStatus::Standby;
512
-
513
- if (Read.GetState() == IOHandleStatus::Pending)
514
- {
515
- Read.Collect();
516
- }
517
- else
518
- {
519
- WI_ASSERT(Write.GetState() == IOHandleStatus::Pending);
520
- Write.Collect();
521
- }
522
- }
523
-
524
- HANDLE GetHandle() const override
525
- {
526
- if (Read.GetState() == IOHandleStatus::Pending)
527
- {
528
- return Read.GetHandle();
529
- }
530
- else
531
- {
532
- WI_ASSERT(Write.GetState() == IOHandleStatus::Pending);
533
- return Write.GetHandle();
534
- }
535
- }
536
-
537
-private:
538
- void OnRead(const gsl::span<char>& Content)
539
- {
540
- PendingBuffer.insert(PendingBuffer.end(), Content.begin(), Content.end());
541
- }
542
-
543
- TRead Read;
544
- WriteHandle Write;
545
- std::vector<char> PendingBuffer;
546
-};
547
-
548
-class DockerIORelayHandle : public OverlappedIOHandle
549
-{
550
-public:
551
- NON_COPYABLE(DockerIORelayHandle);
552
- NON_MOVABLE(DockerIORelayHandle);
553
-
554
- enum class Format
555
- {
556
- Raw,
557
- HttpChunked
558
- };
559
-
560
- DockerIORelayHandle(HandleWrapper&& Input, HandleWrapper&& Stdout, HandleWrapper&& Stderr, Format ReadFormat);
561
- void Schedule() override;
562
- void Collect() override;
563
- HANDLE GetHandle() const override;
564
-
565
-#pragma pack(push, 1)
566
- struct MultiplexedHeader
567
- {
568
- uint8_t Fd;
569
- char Zeroes[3];
570
- uint32_t Length;
571
- };
572
-#pragma pack(pop)
573
-
574
- static_assert(sizeof(MultiplexedHeader) == 8);
575
-
576
-private:
577
- void OnRead(const gsl::span<char>& Buffer);
578
- void ProcessNextHeader();
579
-
580
- std::unique_ptr<OverlappedIOHandle> Read;
581
- WriteHandle WriteStdout;
582
- WriteHandle WriteStderr;
583
- std::vector<char> PendingBuffer;
584
- WriteHandle* ActiveHandle = nullptr;
585
- size_t RemainingBytes = 0;
586
-};
587
-
588
-class MultiHandleWait
589
-{
590
-public:
591
- NON_COPYABLE(MultiHandleWait);
592
- DEFAULT_MOVABLE(MultiHandleWait);
593
-
594
- enum Flags
595
- {
596
- None = 0,
597
- CancelOnCompleted = 1,
598
- IgnoreErrors = 2,
599
- NeedNotComplete = 4,
600
- };
601
-
602
- MultiHandleWait() = default;
603
-
604
- void AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags = Flags::None);
605
- bool Run(std::optional<std::chrono::milliseconds> Timeout);
606
- void Cancel();
607
-
608
-private:
609
- std::vector<std::pair<Flags, std::unique_ptr<OverlappedIOHandle>>> m_handles;
610
- bool m_cancel = false;
611
-};
612
-
613
-DEFINE_ENUM_FLAG_OPERATORS(MultiHandleWait::Flags);
614
-
161
} // namespace wsl::windows::common::relay
src/windows/common/socket.cpp
+3
-3
@@ -20,15 +20,15 @@ Abstract:
20
bool wsl::windows::common::socket::CancellableAccept(
21
_In_ SOCKET ListenSocket, _In_ SOCKET Socket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
22
{
23
- relay::MultiHandleWait io;
23
+ io::MultiHandleWait io;
24
25
bool accepted = false;
26
27
- io.AddHandle(std::make_unique<relay::SingleAcceptHandle>(ListenSocket, Socket, [&]() { accepted = true; }), relay::MultiHandleWait::CancelOnCompleted);
27
+ io.AddHandle(std::make_unique<io::SingleAcceptHandle>(ListenSocket, Socket, [&]() { accepted = true; }), io::MultiHandleWait::CancelOnCompleted);
28
29
if (ExitHandle != nullptr)
30
{
31
- io.AddHandle(std::make_unique<relay::EventHandle>(ExitHandle), relay::MultiHandleWait::CancelOnCompleted);
31
+ io.AddHandle(std::make_unique<io::EventHandle>(ExitHandle), io::MultiHandleWait::CancelOnCompleted);
32
}
33
34
io.Run(std::chrono::milliseconds(Timeout));
src/windows/wslc/services/ConsoleService.cpp
+3
-3
@@ -18,8 +18,8 @@ Abstract:
18
namespace wsl::windows::wslc::services {
19
20
using wsl::windows::common::ClientRunningWSLCProcess;
21
-using wsl::windows::common::relay::ReadHandle;
22
-using wsl::windows::common::relay::RelayHandle;
21
+using wsl::windows::common::io::ReadHandle;
22
+using wsl::windows::common::io::RelayHandle;
23
24
bool ConsoleService::RelayInteractiveTty(ClientRunningWSLCProcess& Process, HANDLE Tty, bool triggerRefresh)
25
{
@@ -70,7 +70,7 @@ bool ConsoleService::RelayInteractiveTty(ClientRunningWSLCProcess& Process, HAND
70
71
void ConsoleService::RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_handle&& Stdout, wil::unique_handle&& Stderr)
72
{
73
- wsl::windows::common::relay::MultiHandleWait io;
73
+ wsl::windows::common::io::MultiHandleWait io;
74
75
// Create a thread to relay stdin to the pipe.
76
wil::unique_event exitEvent(wil::EventOptions::ManualReset);
src/windows/wslc/services/ContainerService.cpp
+3
-3
@@ -521,13 +521,13 @@ void ContainerService::Logs(Session& session, const std::string& id, bool follow
521
522
THROW_IF_FAILED(container->Logs(flags, &stdoutHandle, &stderrHandle, 0, 0, tail));
523
524
- wsl::windows::common::relay::MultiHandleWait io;
525
- io.AddHandle(std::make_unique<wsl::windows::common::relay::RelayHandle<wsl::windows::common::relay::ReadHandle>>(
524
+ wsl::windows::common::io::MultiHandleWait io;
525
+ io.AddHandle(std::make_unique<wsl::windows::common::io::RelayHandle<wsl::windows::common::io::ReadHandle>>(
526
stdoutHandle.Release(), GetStdHandle(STD_OUTPUT_HANDLE)));
527
528
if (!stderrHandle.Empty()) // This handle is only used for non-tty processes.
529
{
530
- io.AddHandle(std::make_unique<wsl::windows::common::relay::RelayHandle<wsl::windows::common::relay::ReadHandle>>(
530
+ io.AddHandle(std::make_unique<wsl::windows::common::io::RelayHandle<wsl::windows::common::io::ReadHandle>>(
531
stderrHandle.Release(), GetStdHandle(STD_ERROR_HANDLE)));
532
}
533
src/windows/wslcsession/DockerEventTracker.cpp
+1
-1
@@ -83,7 +83,7 @@ DockerEventTracker::DockerEventTracker(DockerHTTPClient& dockerClient, WSLCSessi
83
84
auto socket = dockerClient.MonitorEvents();
85
86
- relay.AddHandle(std::make_unique<common::relay::HTTPChunkBasedReadHandle>(std::move(socket), std::move(onChunk)));
86
+ relay.AddHandle(std::make_unique<common::io::HTTPChunkBasedReadHandle>(std::move(socket), std::move(onChunk)));
87
}
88
89
DockerEventTracker::~DockerEventTracker()
src/windows/wslcsession/DockerHTTPClient.cpp
+6
-6
@@ -31,8 +31,8 @@ Abstract:
31
namespace http = boost::beast::http;
32
using boost::beast::http::verb;
33
using wsl::windows::common::docker_schema::EmptyRequest;
34
-using wsl::windows::common::relay::HandleWrapper;
35
-using wsl::windows::common::relay::MultiHandleWait;
34
+using wsl::windows::common::io::HandleWrapper;
35
+using wsl::windows::common::io::MultiHandleWait;
36
using wsl::windows::service::wslc::DockerHTTPClient;
37
using namespace wsl::windows::common;
38
@@ -618,7 +618,7 @@ std::pair<DockerHTTPClient::HTTPResponse, std::string> DockerHTTPClient::SendReq
618
auto onHttpResponse = [&](const auto& response) { responseHeader = response; };
619
MultiHandleWait io;
620
621
- io.AddHandle(std::make_unique<relay::EventHandle>(m_exitingEvent, [&]() { THROW_HR(E_ABORT); }));
621
+ io.AddHandle(std::make_unique<io::EventHandle>(m_exitingEvent, [&]() { THROW_HR(E_ABORT); }));
622
io.AddHandle(std::make_unique<DockerHttpResponseHandle>(*context, std::move(onHttpResponse), std::move(OnResponse)), MultiHandleWait::CancelOnCompleted);
623
624
io.Run({});
@@ -633,7 +633,7 @@ DockerHTTPClient::DockerHttpResponseHandle::DockerHttpResponseHandle(
633
std::function<void(const HTTPResponse&)>&& onResponseHeader,
634
std::function<void(const gsl::span<char>&)>&& onResponseBytes,
635
std::function<void()>&& onCompleted) :
636
- common::relay::ReadHandle(
636
+ common::io::ReadHandle(
637
HandleWrapper{context.stream.native_handle()}, std::bind(&DockerHttpResponseHandle::OnRead, this, std::placeholders::_1)),
638
Context(context),
639
OnResponseHeader(std::move(onResponseHeader)),
@@ -644,7 +644,7 @@ DockerHTTPClient::DockerHttpResponseHandle::DockerHttpResponseHandle(
644
645
DockerHTTPClient::DockerHttpResponseHandle::~DockerHttpResponseHandle()
646
{
647
- if (State == common::relay::IOHandleStatus::Completed)
647
+ if (State == common::io::IOHandleStatus::Completed)
648
{
649
OnCompleted();
650
}
@@ -731,7 +731,7 @@ void DockerHTTPClient::DockerHttpResponseHandle::OnResponseBytes(const gsl::span
731
*RemainingContentLength -= consume;
732
if (*RemainingContentLength == 0)
733
{
734
- State = common::relay::IOHandleStatus::Completed;
734
+ State = common::io::IOHandleStatus::Completed;
735
}
736
737
span = span.subspan(0, consume);
src/windows/wslcsession/DockerHTTPClient.h
+2
-2
@@ -188,7 +188,7 @@ public:
188
189
wil::unique_socket MonitorEvents();
190
191
- struct DockerHttpResponseHandle : public common::relay::ReadHandle
191
+ struct DockerHttpResponseHandle : public common::io::ReadHandle
192
{
193
NON_COPYABLE(DockerHttpResponseHandle);
194
NON_MOVABLE(DockerHttpResponseHandle);
@@ -212,7 +212,7 @@ public:
212
boost::beast::http::response_parser<boost::beast::http::buffer_body> Parser;
213
size_t LineFeeds = 0;
214
std::optional<size_t> RemainingContentLength;
215
- std::optional<common::relay::HTTPChunkBasedReadHandle> ResponseParser;
215
+ std::optional<common::io::HTTPChunkBasedReadHandle> ResponseParser;
216
};
217
218
private:
src/windows/wslcsession/IORelay.cpp
+8
-8
@@ -14,9 +14,9 @@ Abstract:
14
15
#include "IORelay.h"
16
17
-using wsl::windows::common::relay::DockerIORelayHandle;
18
-using wsl::windows::common::relay::MultiHandleWait;
19
-using wsl::windows::common::relay::OverlappedIOHandle;
17
+using wsl::windows::common::io::DockerIORelayHandle;
18
+using wsl::windows::common::io::MultiHandleWait;
19
+using wsl::windows::common::io::OverlappedIOHandle;
20
using wsl::windows::service::wslc::IORelay;
21
22
IORelay::IORelay()
@@ -29,15 +29,15 @@ IORelay::~IORelay()
29
Stop();
30
}
31
32
-void IORelay::AddHandle(std::unique_ptr<common::relay::OverlappedIOHandle>&& Handle)
32
+void IORelay::AddHandle(std::unique_ptr<common::io::OverlappedIOHandle>&& Handle)
33
{
34
- std::vector<std::unique_ptr<common::relay::OverlappedIOHandle>> handles;
34
+ std::vector<std::unique_ptr<common::io::OverlappedIOHandle>> handles;
35
handles.emplace_back(std::move(Handle));
36
37
AddHandles(std::move(handles));
38
}
39
40
-void IORelay::AddHandles(std::vector<std::unique_ptr<common::relay::OverlappedIOHandle>>&& Handles)
40
+void IORelay::AddHandles(std::vector<std::unique_ptr<common::io::OverlappedIOHandle>>&& Handles)
41
{
42
WI_ASSERT(!m_exit);
43
@@ -73,7 +73,7 @@ try
73
{
74
common::wslutil::SetThreadDescription(L"IORelay");
75
76
- windows::common::relay::MultiHandleWait io;
76
+ windows::common::io::MultiHandleWait io;
77
78
// N.B. All the IO must happen on the thread.
79
// If the thread that scheduled the IO exits, the IO is cancelled.
@@ -90,7 +90,7 @@ try
90
m_pendingHandles.clear();
91
}
92
93
- io.AddHandle(std::make_unique<common::relay::EventHandle>(m_refreshEvent.get()), MultiHandleWait::CancelOnCompleted);
93
+ io.AddHandle(std::make_unique<common::io::EventHandle>(m_refreshEvent.get()), MultiHandleWait::CancelOnCompleted);
94
io.Run({});
95
}
96
}
src/windows/wslcsession/IORelay.h
+3
-3
@@ -25,8 +25,8 @@ public:
25
IORelay();
26
~IORelay();
27
28
- void AddHandles(std::vector<std::unique_ptr<common::relay::OverlappedIOHandle>>&& Handles);
29
- void AddHandle(std::unique_ptr<common::relay::OverlappedIOHandle>&& Handle);
28
+ void AddHandles(std::vector<std::unique_ptr<common::io::OverlappedIOHandle>>&& Handles);
29
+ void AddHandle(std::unique_ptr<common::io::OverlappedIOHandle>&& Handle);
30
31
void Stop();
32
@@ -36,7 +36,7 @@ private:
36
37
std::mutex m_pendingHandlesLock;
38
wil::unique_event m_refreshEvent{wil::EventOptions::None};
39
- std::vector<std::unique_ptr<common::relay::OverlappedIOHandle>> m_pendingHandles;
39
+ std::vector<std::unique_ptr<common::io::OverlappedIOHandle>> m_pendingHandles;
40
41
std::thread m_thread;
42
std::atomic<bool> m_exit = false;
src/windows/wslcsession/PortRelayHandle.cpp
+7
-7
@@ -34,7 +34,7 @@ PortRelayAcceptHandle::PortRelayAcceptHandle(
34
35
PortRelayAcceptHandle::~PortRelayAcceptHandle()
36
{
37
- if (State == relay::IOHandleStatus::Pending)
37
+ if (State == io::IOHandleStatus::Pending)
38
{
39
LOG_IF_WIN32_BOOL_FALSE(CancelIoEx(reinterpret_cast<HANDLE>(ListenSocket.get()), &Overlapped));
40
@@ -50,7 +50,7 @@ PortRelayAcceptHandle::~PortRelayAcceptHandle()
50
51
void PortRelayAcceptHandle::Schedule()
52
{
53
- WI_ASSERT(State == relay::IOHandleStatus::Standby);
53
+ WI_ASSERT(State == io::IOHandleStatus::Standby);
54
55
// Create a new socket for accepting
56
AcceptedSocket.reset(WSASocket(Family, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
@@ -61,22 +61,22 @@ void PortRelayAcceptHandle::Schedule()
61
if (AcceptEx(ListenSocket.get(), AcceptedSocket.get(), AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &bytesReturned, &Overlapped))
62
{
63
// Accept completed immediately
64
- State = relay::IOHandleStatus::Completed;
64
+ State = io::IOHandleStatus::Completed;
65
}
66
else
67
{
68
auto error = WSAGetLastError();
69
THROW_HR_IF_MSG(HRESULT_FROM_WIN32(error), error != ERROR_IO_PENDING, "Handle: 0x%p", reinterpret_cast<void*>(ListenSocket.get()));
70
71
- State = relay::IOHandleStatus::Pending;
71
+ State = io::IOHandleStatus::Pending;
72
}
73
}
74
75
void PortRelayAcceptHandle::Collect()
76
{
77
- WI_ASSERT(State == relay::IOHandleStatus::Pending || State == relay::IOHandleStatus::Completed);
77
+ WI_ASSERT(State == io::IOHandleStatus::Pending || State == io::IOHandleStatus::Completed);
78
79
- if (State == relay::IOHandleStatus::Pending)
79
+ if (State == io::IOHandleStatus::Pending)
80
{
81
DWORD bytesReceived{};
82
DWORD flagsReturned{};
@@ -87,7 +87,7 @@ void PortRelayAcceptHandle::Collect()
87
LaunchRelay(std::move(AcceptedSocket));
88
89
// Go back to standby to accept the next connection
90
- State = relay::IOHandleStatus::Standby;
90
+ State = io::IOHandleStatus::Standby;
91
}
92
93
HANDLE PortRelayAcceptHandle::GetHandle() const
src/windows/wslcsession/PortRelayHandle.h
+1
-1
@@ -20,7 +20,7 @@ namespace wsl::windows::service::wslc {
20
21
class IORelay;
22
23
-class PortRelayAcceptHandle : public common::relay::OverlappedIOHandle
23
+class PortRelayAcceptHandle : public common::io::OverlappedIOHandle
24
{
25
public:
26
NON_COPYABLE(PortRelayAcceptHandle)
src/windows/wslcsession/WSLCContainer.cpp
+10
-10
@@ -25,12 +25,12 @@ Abstract:
25
26
using wsl::windows::common::COMServiceExecutionContext;
27
using wsl::windows::common::docker_schema::ErrorResponse;
28
-using wsl::windows::common::relay::DockerIORelayHandle;
29
-using wsl::windows::common::relay::HandleWrapper;
30
-using wsl::windows::common::relay::HTTPChunkBasedReadHandle;
31
-using wsl::windows::common::relay::OverlappedIOHandle;
32
-using wsl::windows::common::relay::ReadHandle;
33
-using wsl::windows::common::relay::RelayHandle;
28
+using wsl::windows::common::io::DockerIORelayHandle;
29
+using wsl::windows::common::io::HandleWrapper;
30
+using wsl::windows::common::io::HTTPChunkBasedReadHandle;
31
+using wsl::windows::common::io::OverlappedIOHandle;
32
+using wsl::windows::common::io::ReadHandle;
33
+using wsl::windows::common::io::RelayHandle;
34
using wsl::windows::service::wslc::ContainerPortMapping;
35
using wsl::windows::service::wslc::IWSLCVolume;
36
using wsl::windows::service::wslc::NetworkEntry;
@@ -47,7 +47,7 @@ using wsl::windows::service::wslc::WSLCSession;
47
using wsl::windows::service::wslc::WSLCVirtualMachine;
48
using wsl::windows::service::wslc::WSLCVolumeMount;
49
50
-using namespace wsl::windows::common::relay;
50
+using namespace wsl::windows::common::io;
51
using namespace wsl::windows::common::docker_schema;
52
using namespace wsl::windows::common::wslutil;
53
using namespace std::chrono_literals;
@@ -1023,7 +1023,7 @@ void WSLCContainerImpl::Export(WSLCHandle OutHandle) const
1023
1024
auto userHandle = m_wslcSession.OpenUserHandle(OutHandle);
1025
1026
- wsl::windows::common::relay::MultiHandleWait io = m_wslcSession.CreateIOContext();
1026
+ wsl::windows::common::io::MultiHandleWait io = m_wslcSession.CreateIOContext();
1027
1028
std::string errorJson;
1029
auto accumulateError = [&](const gsl::span<char>& buffer) {
@@ -1039,7 +1039,7 @@ void WSLCContainerImpl::Export(WSLCHandle OutHandle) const
1039
{
1040
io.AddHandle(
1041
std::make_unique<RelayHandle<HTTPChunkBasedReadHandle>>(HandleWrapper{std::move(SocketCodePair.second)}, userHandle.Get()),
1042
- wsl::windows::common::relay::MultiHandleWait::CancelOnCompleted);
1042
+ wsl::windows::common::io::MultiHandleWait::CancelOnCompleted);
1043
}
1044
1045
// Release the lock so the container can still be interacted with while the export is in progress.
@@ -1884,7 +1884,7 @@ std::unique_ptr<RelayedProcessIO> WSLCContainerImpl::CreateRelayedProcessIO(wil:
1884
fds.emplace(WSLCFDStderr, TypedHandle{wil::unique_handle{stderrRead.release()}, WSLCHandleTypePipe});
1885
1886
ioHandles.emplace_back(std::make_unique<DockerIORelayHandle>(
1887
- std::move(stream), std::move(stdoutWrite), std::move(stderrWrite), common::relay::DockerIORelayHandle::Format::Raw));
1887
+ std::move(stream), std::move(stdoutWrite), std::move(stderrWrite), common::io::DockerIORelayHandle::Format::Raw));
1888
1889
m_ioRelay.AddHandles(std::move(ioHandles));
1890
src/windows/wslcsession/WSLCProcessIO.cpp
+1
-1
@@ -20,7 +20,7 @@ using wsl::windows::service::wslc::RelayedProcessIO;
20
using wsl::windows::service::wslc::TTYProcessIO;
21
using wsl::windows::service::wslc::TypedHandle;
22
using wsl::windows::service::wslc::VMProcessIO;
23
-using namespace wsl::windows::common::relay;
23
+using namespace wsl::windows::common::io;
24
25
RelayedProcessIO::RelayedProcessIO(std::map<ULONG, TypedHandle>&& fds) : m_relayedHandles(std::move(fds))
26
{
src/windows/wslcsession/WSLCSession.cpp
+23
-25
@@ -22,7 +22,7 @@ Abstract:
22
#include "wslpolicies.h"
23
24
using namespace wsl::windows::common;
25
-using relay::MultiHandleWait;
25
+using io::MultiHandleWait;
26
using wsl::shared::Localization;
27
using wsl::windows::service::wslc::UserCOMCallback;
28
using wsl::windows::service::wslc::UserHandle;
@@ -311,8 +311,7 @@ try
311
m_volumes.emplace(m_dockerClient.value(), m_virtualMachine.value(), m_eventTracker.value(), m_storageVhdPath.parent_path());
312
313
// Monitor for unexpected VM exit.
314
- m_ioRelay.AddHandle(
315
- std::make_unique<windows::common::relay::EventHandle>(m_vmExitedEvent.get(), std::bind(&WSLCSession::OnVmExited, this)));
314
+ m_ioRelay.AddHandle(std::make_unique<windows::common::io::EventHandle>(m_vmExitedEvent.get(), std::bind(&WSLCSession::OnVmExited, this)));
315
316
// Recover any existing resources from storage.
317
RecoverExistingNetworks();
@@ -492,13 +491,13 @@ ServiceRunningProcess WSLCSession::StartProcess(
491
492
auto process = launcher.Launch(*m_virtualMachine);
493
495
- m_ioRelay.AddHandle(std::make_unique<windows::common::relay::LineBasedReadHandle>(
494
+ m_ioRelay.AddHandle(std::make_unique<windows::common::io::LineBasedReadHandle>(
495
process.GetStdHandle(1), [this, LogSource](const auto& data) { OnProcessLog(data, LogSource); }, false));
496
498
- m_ioRelay.AddHandle(std::make_unique<windows::common::relay::LineBasedReadHandle>(
497
+ m_ioRelay.AddHandle(std::make_unique<windows::common::io::LineBasedReadHandle>(
498
process.GetStdHandle(2), [this, LogSource](const auto& data) { OnProcessLog(data, LogSource); }, false));
499
501
- m_ioRelay.AddHandle(std::make_unique<windows::common::relay::EventHandle>(process.GetExitEvent(), std::move(ExitCallback)));
500
+ m_ioRelay.AddHandle(std::make_unique<windows::common::io::EventHandle>(process.GetExitEvent(), std::move(ExitCallback)));
501
502
return process;
503
}
@@ -762,8 +761,8 @@ try
761
762
auto io = CreateIOContext();
763
765
- io.AddHandle(std::make_unique<relay::RelayHandle<relay::ReadHandle>>(
766
- buildFileHandle.Get(), common::relay::HandleWrapper{buildProcess.GetStdHandle(WSLCFDStdin)}));
764
+ io.AddHandle(std::make_unique<io::RelayHandle<io::ReadHandle>>(
765
+ buildFileHandle.Get(), common::io::HandleWrapper{buildProcess.GetStdHandle(WSLCFDStdin)}));
766
767
bool verbose = WI_IsFlagSet(Options->Flags, WSLCBuildImageFlagsVerbose);
768
std::string allOutput;
@@ -920,11 +919,11 @@ try
919
// With --progress=rawjson, docker writes progress to stderr and the final image ID to stdout on success (empty on
920
// failure). Stdout is drained into allOutput (shown only on error) and its EOF signals build completion.
921
io.AddHandle(
923
- std::make_unique<relay::ReadHandle>(
922
+ std::make_unique<io::ReadHandle>(
923
buildProcess.GetStdHandle(1), [&](const auto& content) { allOutput.append(content.begin(), content.end()); }),
925
- relay::MultiHandleWait::CancelOnCompleted);
924
+ io::MultiHandleWait::CancelOnCompleted);
925
927
- io.AddHandle(std::make_unique<relay::LineBasedReadHandle>(buildProcess.GetStdHandle(2), captureOutput, false));
926
+ io.AddHandle(std::make_unique<io::LineBasedReadHandle>(buildProcess.GetStdHandle(2), captureOutput, false));
927
928
// Handle cancellation within the IO loop (NeedNotComplete) so pipes keep draining.
929
bool cancelled = false;
@@ -935,7 +934,7 @@ try
934
THROW_LAST_ERROR_IF_NULL(killTimer);
935
936
io.AddHandle(
938
- std::make_unique<relay::EventHandle>(
937
+ std::make_unique<io::EventHandle>(
938
CancelEvent,
939
[&]() {
940
cancelled = true;
@@ -943,12 +942,12 @@ try
942
LARGE_INTEGER dueTime{.QuadPart = -10LL * 10 * 1000 * 1000}; // 10 seconds
943
THROW_IF_WIN32_BOOL_FALSE(SetWaitableTimer(killTimer.get(), &dueTime, 0, nullptr, nullptr, FALSE));
944
}),
946
- relay::MultiHandleWait::NeedNotComplete);
945
+ io::MultiHandleWait::NeedNotComplete);
946
947
io.AddHandle(
949
- std::make_unique<relay::EventHandle>(
948
+ std::make_unique<io::EventHandle>(
949
killTimer.get(), [&]() { LOG_IF_FAILED(buildProcess.Get().Signal(WSLCSignalSIGKILL)); }),
951
- relay::MultiHandleWait::NeedNotComplete);
950
+ io::MultiHandleWait::NeedNotComplete);
951
}
952
953
try
@@ -1111,9 +1110,8 @@ void WSLCSession::ImportImageImpl(DockerHTTPClient::HTTPRequestContext& Request,
1110
LOG_LAST_ERROR_IF(shutdown(socket, SD_SEND) == SOCKET_ERROR);
1111
};
1112
1114
- io.AddHandle(std::make_unique<relay::RelayHandle<relay::ReadHandle>>(
1115
- common::relay::HandleWrapper{userHandle.Get(), std::move(onInputComplete)},
1116
- common::relay::HandleWrapper{Request.stream.native_handle()}));
1113
+ io.AddHandle(std::make_unique<io::RelayHandle<io::ReadHandle>>(
1114
+ common::io::HandleWrapper{userHandle.Get(), std::move(onInputComplete)}, common::io::HandleWrapper{Request.stream.native_handle()}));
1115
1116
io.AddHandle(
1117
std::make_unique<DockerHTTPClient::DockerHttpResponseHandle>(Request, std::move(onHttpResponse), std::move(onProgress)),
@@ -1170,14 +1168,14 @@ void WSLCSession::SaveImageImpl(std::pair<uint32_t, wil::unique_socket>& SocketC
1168
};
1169
1170
io.AddHandle(
1173
- std::make_unique<relay::ReadHandle>(common::relay::HandleWrapper{std::move(SocketCodePair.second)}, std::move(accumulateError)),
1171
+ std::make_unique<io::ReadHandle>(common::io::HandleWrapper{std::move(SocketCodePair.second)}, std::move(accumulateError)),
1172
MultiHandleWait::CancelOnCompleted);
1173
}
1174
else
1175
{
1176
io.AddHandle(
1179
- std::make_unique<relay::RelayHandle<relay::HTTPChunkBasedReadHandle>>(
1180
- common::relay::HandleWrapper{std::move(SocketCodePair.second)}, userHandle.Get()),
1177
+ std::make_unique<io::RelayHandle<io::HTTPChunkBasedReadHandle>>(
1178
+ common::io::HandleWrapper{std::move(SocketCodePair.second)}, userHandle.Get()),
1179
MultiHandleWait::CancelOnCompleted);
1180
}
1181
@@ -2644,20 +2642,20 @@ HRESULT WSLCSession::InterfaceSupportsErrorInfo(REFIID riid)
2642
2643
MultiHandleWait WSLCSession::CreateIOContext(HANDLE CancelHandle)
2644
{
2647
- relay::MultiHandleWait io;
2645
+ io::MultiHandleWait io;
2646
2647
// Cancel with E_ABORT if the session is terminating.
2650
- io.AddHandle(std::make_unique<relay::EventHandle>(
2648
+ io.AddHandle(std::make_unique<io::EventHandle>(
2649
m_sessionTerminatingEvent.get(), [this]() { THROW_HR_MSG(E_ABORT, "Session %lu is terminating", m_id); }));
2650
2651
// Cancel with E_ABORT if the client process exits.
2654
- io.AddHandle(std::make_unique<relay::EventHandle>(
2652
+ io.AddHandle(std::make_unique<io::EventHandle>(
2653
wslutil::OpenCallingProcess(SYNCHRONIZE), [this]() { THROW_HR_MSG(E_ABORT, "Client process has exited"); }));
2654
2655
if (CancelHandle != nullptr)
2656
{
2657
io.AddHandle(
2660
- std::make_unique<relay::EventHandle>(CancelHandle, []() { THROW_HR_MSG(E_ABORT, "Cancellation handle was signaled"); }));
2658
+ std::make_unique<io::EventHandle>(CancelHandle, []() { THROW_HR_MSG(E_ABORT, "Cancellation handle was signaled"); }));
2659
}
2660
2661
return io;
src/windows/wslcsession/WSLCSession.h
+1
-1
@@ -150,7 +150,7 @@ public:
150
IFACEMETHOD(MapVmPort)(_In_ int Family, _In_ unsigned short WindowsPort, _In_ unsigned short LinuxPort) override;
151
IFACEMETHOD(UnmapVmPort)(_In_ int Family, _In_ unsigned short WindowsPort, _In_ unsigned short LinuxPort) override;
152
153
- common::relay::MultiHandleWait CreateIOContext(HANDLE CancelHandle = nullptr);
153
+ common::io::MultiHandleWait CreateIOContext(HANDLE CancelHandle = nullptr);
154
155
UserHandle OpenUserHandle(WSLCHandle Handle);
156
void ReleaseUserHandle(HANDLE Handle);
test/windows/Common.cpp
+4
-4
@@ -2814,13 +2814,13 @@ try
2814
}
2815
CATCH_LOG();
2816
2817
-class ReadHandleWithTargetValue : public wsl::windows::common::relay::ReadHandle
2817
+class ReadHandleWithTargetValue : public wsl::windows::common::io::ReadHandle
2818
{
2819
public:
2820
NON_COPYABLE(ReadHandleWithTargetValue);
2821
NON_MOVABLE(ReadHandleWithTargetValue);
2822
2823
- ReadHandleWithTargetValue(wsl::windows::common::relay::HandleWrapper&& MovedHandle, std::string_view targetValue) :
2823
+ ReadHandleWithTargetValue(wsl::windows::common::io::HandleWrapper&& MovedHandle, std::string_view targetValue) :
2824
ReadHandle(std::move(MovedHandle), [this](const auto& buffer) { m_readBuffer.append(buffer.data(), buffer.size()); }),
2825
m_targetValue(targetValue)
2826
{
@@ -2841,7 +2841,7 @@ public:
2841
private:
2842
void CheckIfTargetFound()
2843
{
2844
- using namespace wsl::windows::common::relay;
2844
+ using namespace wsl::windows::common::io;
2845
2846
if (State == IOHandleStatus::Standby || State == IOHandleStatus::Completed)
2847
{
@@ -2867,7 +2867,7 @@ private:
2867
2868
void WaitForOutput(wil::unique_handle handle, std::string_view targetValue, std::chrono::milliseconds timeout)
2869
{
2870
- wsl::windows::common::relay::MultiHandleWait io;
2870
+ wsl::windows::common::io::MultiHandleWait io;
2871
io.AddHandle(std::make_unique<ReadHandleWithTargetValue>(std::move(handle), targetValue));
2872
io.Run(timeout);
2873
}
test/windows/UnitTests.cpp
+3
-3
@@ -6729,9 +6729,9 @@ Error code: Wsl/InstallDistro/WSL_E_INVALID_JSON\r\n",
6729
bool callbackInvoked = false;
6730
std::vector<gsl::byte> message;
6731
6732
- wsl::windows::common::relay::MultiHandleWait io;
6733
- io.AddHandle(std::make_unique<wsl::windows::common::relay::ReadSocketMessageHandle>(
6734
- wsl::windows::common::relay::HandleWrapper{std::move(server)}, buffer, [&callbackInvoked, &message](const gsl::span<gsl::byte>& received) {
6732
+ wsl::windows::common::io::MultiHandleWait io;
6733
+ io.AddHandle(std::make_unique<wsl::windows::common::io::ReadSocketMessageHandle>(
6734
+ wsl::windows::common::io::HandleWrapper{std::move(server)}, buffer, [&callbackInvoked, &message](const gsl::span<gsl::byte>& received) {
6735
callbackInvoked = true;
6736
message.assign(received.begin(), received.end());
6737
}));
test/windows/WSLCTests.cpp
+16
-16
@@ -28,8 +28,8 @@ using wsl::windows::common::RunningWSLCContainer;
28
using wsl::windows::common::RunningWSLCProcess;
29
using wsl::windows::common::WSLCContainerLauncher;
30
using wsl::windows::common::WSLCProcessLauncher;
31
-using wsl::windows::common::relay::OverlappedIOHandle;
32
-using wsl::windows::common::relay::WriteHandle;
31
+using wsl::windows::common::io::OverlappedIOHandle;
32
+using wsl::windows::common::io::WriteHandle;
33
using namespace wsl::windows::common::wslutil;
34
35
extern std::wstring g_testDataPath;
@@ -7204,12 +7204,12 @@ class WSLCTests
7204
std::vector<std::string> lines;
7205
auto onData = [&](const gsl::span<char>& data) { lines.emplace_back(data.data(), data.size()); };
7206
7207
- wsl::windows::common::relay::MultiHandleWait io;
7207
+ wsl::windows::common::io::MultiHandleWait io;
7208
7209
- io.AddHandle(std::make_unique<wsl::windows::common::relay::LineBasedReadHandle>(std::move(readPipe), std::move(onData), Crlf));
7209
+ io.AddHandle(std::make_unique<wsl::windows::common::io::LineBasedReadHandle>(std::move(readPipe), std::move(onData), Crlf));
7210
7211
std::vector<char> buffer{Data.begin(), Data.end()};
7212
- io.AddHandle(std::make_unique<wsl::windows::common::relay::WriteHandle>(std::move(writePipe), buffer));
7212
+ io.AddHandle(std::make_unique<wsl::windows::common::io::WriteHandle>(std::move(writePipe), buffer));
7213
7214
io.Run({});
7215
@@ -7268,12 +7268,12 @@ class WSLCTests
7268
std::vector<std::string> chunks;
7269
auto onData = [&](const gsl::span<char>& data) { chunks.emplace_back(data.data(), data.size()); };
7270
7271
- wsl::windows::common::relay::MultiHandleWait io;
7271
+ wsl::windows::common::io::MultiHandleWait io;
7272
7273
- io.AddHandle(std::make_unique<wsl::windows::common::relay::HTTPChunkBasedReadHandle>(std::move(readPipe), std::move(onData)));
7273
+ io.AddHandle(std::make_unique<wsl::windows::common::io::HTTPChunkBasedReadHandle>(std::move(readPipe), std::move(onData)));
7274
7275
std::vector<char> buffer{Data.begin(), Data.end()};
7276
- io.AddHandle(std::make_unique<wsl::windows::common::relay::WriteHandle>(std::move(writePipe), buffer));
7276
+ io.AddHandle(std::make_unique<wsl::windows::common::io::WriteHandle>(std::move(writePipe), buffer));
7277
7278
io.Run({});
7279
@@ -7336,8 +7336,8 @@ class WSLCTests
7336
std::vector<std::string> chunks;
7337
auto onData = [&](const gsl::span<char>& data) { chunks.emplace_back(data.data(), data.size()); };
7338
7339
- auto reader = std::make_unique<wsl::windows::common::relay::HTTPChunkBasedReadHandle>(
7340
- wsl::windows::common::relay::HandleWrapper{nullptr}, std::move(onData));
7339
+ auto reader = std::make_unique<wsl::windows::common::io::HTTPChunkBasedReadHandle>(
7340
+ wsl::windows::common::io::HandleWrapper{nullptr}, std::move(onData));
7341
7342
std::string allData;
7343
for (const auto& datum : Data)
@@ -7407,9 +7407,9 @@ class WSLCTests
7407
auto [readPipe, writePipe] = wsl::windows::common::wslutil::OpenAnonymousPipe(16 * 1024, true, false);
7408
7409
std::string readData;
7410
- wsl::windows::common::relay::MultiHandleWait io;
7410
+ wsl::windows::common::io::MultiHandleWait io;
7411
7412
- io.AddHandle(std::make_unique<wsl::windows::common::relay::ReadHandle>(std::move(readPipe), [&](const gsl::span<char>& buffer) {
7412
+ io.AddHandle(std::make_unique<wsl::windows::common::io::ReadHandle>(std::move(readPipe), [&](const gsl::span<char>& buffer) {
7413
if (!buffer.empty())
7414
{
7415
readData.append(buffer.data(), buffer.size());
@@ -7441,7 +7441,7 @@ class WSLCTests
7441
std::filesystem::remove("write-handle-test");
7442
});
7443
7444
- wsl::windows::common::relay::MultiHandleWait io;
7444
+ wsl::windows::common::io::MultiHandleWait io;
7445
io.AddHandle(std::make_unique<WriteHandle>(outputFile.get(), writeBuffer));
7446
io.Run({});
7447
@@ -7461,7 +7461,7 @@ class WSLCTests
7461
7462
TEST_METHOD(DockerIORelay)
7463
{
7464
- using namespace wsl::windows::common::relay;
7464
+ using namespace wsl::windows::common::io;
7465
7466
auto runTest = [](const std::vector<char>& Input, const std::string& ExpectedStdout, const std::string& ExpectedStderr) {
7467
auto [readPipe, writePipe] = wsl::windows::common::wslutil::OpenAnonymousPipe(16 * 1024, true, false);
@@ -8881,8 +8881,8 @@ class WSLCTests
8881
std::string output;
8882
auto onRead = [&](const gsl::span<char>& data) { output.append(data.data(), data.size()); };
8883
8884
- wsl::windows::common::relay::MultiHandleWait io;
8885
- io.AddHandle(std::make_unique<wsl::windows::common::relay::ReadHandle>(TtyOut, std::move(onRead)));
8884
+ wsl::windows::common::io::MultiHandleWait io;
8885
+ io.AddHandle(std::make_unique<wsl::windows::common::io::ReadHandle>(TtyOut, std::move(onRead)));
8886
8887
io.Run(60s);
8888
test/windows/WslcSdkTests.cpp
+3
-3
@@ -85,12 +85,12 @@ ProcessOutput WaitForProcessOutput(WslcProcess process, std::chrono::millisecond
85
86
// Read stdout / stderr concurrently so that full pipe buffers do not stall the process.
87
ProcessOutput output;
88
- wsl::windows::common::relay::MultiHandleWait io;
88
+ wsl::windows::common::io::MultiHandleWait io;
89
90
- io.AddHandle(std::make_unique<wsl::windows::common::relay::ReadHandle>(
90
+ io.AddHandle(std::make_unique<wsl::windows::common::io::ReadHandle>(
91
std::move(ownedStdout), [&](const auto& buffer) { output.stdoutOutput.append(buffer.data(), buffer.size()); }));
92
93
- io.AddHandle(std::make_unique<wsl::windows::common::relay::ReadHandle>(
93
+ io.AddHandle(std::make_unique<wsl::windows::common::io::ReadHandle>(
94
std::move(ownedStderr), [&](const auto& buffer) { output.stderrOutput.append(buffer.data(), buffer.size()); }));
95
96
auto timeoutTime = std::chrono::steady_clock::now() + timeout;