Repository: nats-io/nats.go
Pull request #774 (2021-07-13 11:45:31)

In-process connections
This PR is an extension of the work from nats-io/nats-server#2360 by allowing a client to use an in-process connection to the NATS server, rather than a TCP connection. Much of the justification is in the other PR, but this lets us do something like:

```
natsServer, err = natsserver.NewServer(&server.Options{
	ServerName: "monolith",
	DontListen: true,
	JetStream: true,
})
if err != nil {
	panic(err)
}

go natsServer.Start()

if !natsServer.ReadyForConnections(time.Second * 10) {
	panic("not ready in time")
}

nc, err := natsclient.Connect("", natsclient.InProcessServer(natsServer))
if err != nil {
	panic(err)
}
```

coveralls: 
[![Coverage Status](https://coveralls.io/builds/41391729/badge)](https://coveralls.io/builds/41391729)

Coverage decreased (-0.2%) to 86.734% when pulling **cd5b4d5a89fe6238c5d38d3b819b01d949d17130 on neilalexander:neilalexander/inprocessconn** into **c7fc3c7fdf53ba0fef1a28400253d7f8d2c1a037 on nats-io:master**.

neilalexander: Rebased against `main`.
neilalexander: @kozlovic Yes, good point :-) I've added a test to create an in-process connection and to perform an RTT test.
neilalexander: @kozlovic Seems to be looking better now!
ripienaar: Be great to get this merged before next release. 
kozlovic: Oops, that fell through the cracks. Thanks @ripienaar for the reminder. @neilalexander Apologies, could you resolve conflicts and I promise I will merge this right after :-)
kozlovic: @neilalexander while at it, would not hurt to rebase from main and squash your commits if possible.

Patch:
diff --git a/nats.go b/nats.go
index f4d454b..12176b7 100644
--- a/nats.go
+++ b/nats.go
@@ -283,6 +283,10 @@ type CustomDialer interface {
 	Dial(network, address string) (net.Conn, error)
 }
 
+type InProcessConnProvider interface {
+	InProcessConn() (net.Conn, error)
+}
+
 // Options can be used to create a customized connection.
 type Options struct {
 
@@ -291,6 +295,11 @@ type Options struct {
 	// then becomes the first server in the Servers array.
 	Url string
 
+	// InProcessServer represents a NATS server running within the
+	// same process. If this is set then we will attempt to connect
+	// to the server directly rather than using external TCP conns.
+	InProcessServer InProcessConnProvider
+
 	// Servers is a configured set of servers which this client
 	// will use when attempting to connect.
 	Servers []string
@@ -783,6 +792,15 @@ func Name(name string) Option {
 	}
 }
 
+// InProcessServer is an Option that will try to establish a direction to a NATS server
+// running within the process instead of dialing via TCP.
+func InProcessServer(server InProcessConnProvider) Option {
+	return func(o *Options) error {
+		o.InProcessServer = server
+		return nil
+	}
+}
+
 // Secure is an Option to enable TLS secure connections that skip server verification by default.
 // Pass a TLS Configuration for proper TLS.
 // NOTE: This should NOT be used in a production setting.
@@ -1749,6 +1767,18 @@ func (nc *Conn) createConn() (err error) {
 		return ErrNoServers
 	}
 
+	// If we have a reference to an in-process server then establish a
+	// connection using that.
+	if nc.Opts.InProcessServer != nil {
+		conn, err := nc.Opts.InProcessServer.InProcessConn()
+		if err != nil {
+			return fmt.Errorf("failed to get in-process connection: %w", err)
+		}
+		nc.conn = conn
+		nc.bindToNewConn()
+		return nil
+	}
+
 	// We will auto-expand host names if they resolve to multiple IPs
 	hosts := []string{}
 	u := nc.current.url


Test patch:
diff --git a/nats_test.go b/nats_test.go
index 8a81d49..1b5ce5e 100644
--- a/nats_test.go
+++ b/nats_test.go
@@ -2883,3 +2883,25 @@ func TestRespInbox(t *testing.T) {
 		t.Fatalf("Error: %s", resp.Data)
 	}
 }
+
+func TestInProcessConn(t *testing.T) {
+	s := RunServerOnPort(-1)
+	defer s.Shutdown()
+
+	nc, err := Connect("", InProcessServer(s))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	defer nc.Close()
+
+	// Status should be connected.
+	if nc.Status() != CONNECTED {
+		t.Fatal("should be status CONNECTED")
+	}
+
+	// The server should respond to a request.
+	if _, err := nc.RTT(); err != nil {
+		t.Fatal(err)
+	}
+}