Skip to content

Add DebugAdapterNamedPipeServer#103210

Merged
weinand merged 5 commits intomicrosoft:masterfrom
TylerLeonhardt:add-debug-adapter-named-pipe-server
Aug 19, 2020
Merged

Add DebugAdapterNamedPipeServer#103210
weinand merged 5 commits intomicrosoft:masterfrom
TylerLeonhardt:add-debug-adapter-named-pipe-server

Conversation

@TylerLeonhardt
Copy link
Copy Markdown
Member

Fixes #84643

This adds a DebugAdapterNamedPipeServer that can be used to to connect to a debug adapter running as a Named Pipe server (on Windows) or a UNIX Domain Socket (on non-Windows).

	/**
	 * Represents a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server.
	 */
	export class DebugAdapterNamedPipeServer {
		/**
		 * The path to the NamedPipe/UNIX Domain Socket.
		 */
		readonly path: string;

		/**
		 * Create a description for a debug adapter running as a socket based server.
		 */
		constructor(path: string);
	}

Named Pipes/UNIX Domain sockets are easier to lock down (ACL'ing to the current user) rather than locking down a TCP socket which can be tricky.

This also adds some unit tests for the StreamDebugAdapters (both Socket and NamedPipe)

startSession(): Promise<void> {
return new Promise<void>((resolve, reject) => {
let connected = false;
this.socket = net.createConnection(this.adapterServer.path, () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the logic is otherwise identical to SocketDebugAdapter, I would probably pull this logic into an abstract class NetworkDebugAdapter that has an abstract createConnection method that the Socket and NamedPipe debug adapters can implement.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - refactored that to a NetworkDebugAdapter abstract class.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to also move the rest of the logic within startSession() to the parent class, since the only thing different is the arg to net.createConnection 🙂

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah of course :) now I have an abstract createConnection that SocketDebugAdapter and NamedPipeDebugAdapter implement.

return Math.floor(Math.random() * (max - min) + min);
}

function rndName(): string {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small tip: I usually just use crypto.randomBytes in cases like these. Slower but works just fine for tests.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@weinand weinand added the debug Debug viewlet, configurations, breakpoints, adapter issues label Jul 23, 2020
@weinand weinand added this to the July 2020 milestone Jul 23, 2020
Copy link
Copy Markdown
Contributor

@weinand weinand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[please ignore: comment moved...]

/**
* Represents a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server.
*/
export class DebugAdapterNamedPipeServer {
Copy link
Copy Markdown
Contributor

@weinand weinand Jul 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a PR for an extension API request, we will have to go through the API review request, which starts by making a proposal in vscode.proposed.d.ts.
However, moving the DebugAdapterNamedPipeServer to vscode.proposed.d.ts does not allow to add that type to the OR-type DebugAdapterDescriptor in vscode.d.ts (which is missing in your PR anyway). So we have no choice but to leave the DebugAdapterNamedPipeServer in vscode.d.ts....

When looking at the resulting type:

export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation;

I got the impression that there is no real need to add a new type DebugAdapterNamedPipeServer because the name DebugAdapterServer covers the named pipe case nicely and we could just overload the constructor.
The only problem with this approach is the mandatory port variable which doesn't makes sense in the named pipe case...

Any ideas how to fold DebugAdapterNamedPipeServer into the DebugAdapterServer (in order to keep the API surface area small)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you. I would love to merge the two (I wasn't quite sure what you might think of changing the existing definition...)

One thing we could do is have a type property (when no type is specified we assume socket:

	/**
	 * Represents a debug adapter running as a socket based server.
	 */
	export class DebugAdapterServer {
		/**
		* The type.
		*/
		readonly type?: 'socket' | 'namedPipe' | undefined
		/**
		* The port.
		*/
		readonly port?: number;
		/**
		 * The host.
		 */
		readonly host?: string;
		/**
		 * The named pipe path. We could reuse host for this?
		 */
		readonly path?: string;
		/**
		 * Create a description for a debug adapter running as a socket based server.
		 */
		constructor(port: number, host?: string);
		/**
		 * Create a description for a debug adapter running as a named pipe based server.
		 */
		constructor(path: string);
	}

I made port optional... but if we have to keep it mandatory (although I don't see how making something not mandatory anymore is breaking), we could assign port to -1 in the named pipe case.

What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I suggested Tyler make a new type is so that the presence of host and port versus path is better typed. There should be no case where port is set but path is not, and no case host is set but port is not. I prefer to express cases like this in the type system when I can, but the expense is a more bulky API surface as you mention.

One way that this can be done is to make some endpoint: { path: string } | { host: string; port: string } property, but this is a breaking change and not necessarily much nicer.

Copy link
Copy Markdown
Contributor

@weinand weinand Jul 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TylerLeonhardt if we make port optional, this breaks all code that assumes that this property always exists (because that code doesn't handle the undefined case). So we cannot make port optional.
But we could keep port mandatory and set it to 0 in the named pipe case... but this is somewhat ugly.

@connor4312 I fully agree with you on the "better typing". The only reason why I brought this up was the "better naming" of things: DebugAdapterServer is the "good name" that covers all servers, e.g. DebugAdapterSocketServer, DebugAdapterNamedPipeServer, DebugAdapterSmokeSignalServer... ;-)

With DebugAdapterSocketServer we get a new name without the corresponding peer DebugAdapterSocketServer.

But since we cannot solve the port issue in an elegant way, I think we should swallow the new type DebugAdapterNamedPipeServer.

@TylerLeonhardt could you please add DebugAdapterNamedPipeServer to the DebugAdapterDescriptor OR-type.
Since we cannot publish this new API to vscode.proposed.d.ts I suggest that we hold this PR back until it was discussed once in the API sync.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weinand

How come we can't publish this new API to proposed? Is it because it needs to go into the OR-type of DebugAdapterDescriptor?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, if DebugAdapterNamedPipeServer is not part of DebugAdapterDescriptor it will be difficult to return an instance of it from DebugAdapterDescriptorFactory.createDebugAdapterDescriptor.

Copy link
Copy Markdown
Contributor

@weinand weinand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@TylerLeonhardt
Copy link
Copy Markdown
Member Author

PR for vscode-mock-debug: microsoft/vscode-mock-debug#46

@weinand weinand modified the milestones: July 2020, August 2020 Aug 3, 2020
@weinand weinand merged commit b6ff910 into microsoft:master Aug 19, 2020
@weinand
Copy link
Copy Markdown
Contributor

weinand commented Aug 19, 2020

@TylerLeonhardt thanks for the PR!

@TylerLeonhardt TylerLeonhardt deleted the add-debug-adapter-named-pipe-server branch August 19, 2020 14:31
@github-actions github-actions bot locked and limited conversation to collaborators Oct 3, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

debug Debug viewlet, configurations, breakpoints, adapter issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Named Pipe & Unix Domain Sockets as debug adapter entry point

3 participants