1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package jsonrpc2
6
7import (
8	"context"
9	"io"
10	"net"
11	"os"
12	"time"
13)
14
15// This file contains implementations of the transport primitives that use the standard network
16// package.
17
18// NetListenOptions is the optional arguments to the NetListen function.
19type NetListenOptions struct {
20	NetListenConfig net.ListenConfig
21	NetDialer       net.Dialer
22}
23
24// NetListener returns a new Listener that listents on a socket using the net package.
25func NetListener(ctx context.Context, network, address string, options NetListenOptions) (Listener, error) {
26	ln, err := options.NetListenConfig.Listen(ctx, network, address)
27	if err != nil {
28		return nil, err
29	}
30	return &netListener{net: ln}, nil
31}
32
33// netListener is the implementation of Listener for connections made using the net package.
34type netListener struct {
35	net net.Listener
36}
37
38// Accept blocks waiting for an incoming connection to the listener.
39func (l *netListener) Accept(ctx context.Context) (io.ReadWriteCloser, error) {
40	return l.net.Accept()
41}
42
43// Close will cause the listener to stop listening. It will not close any connections that have
44// already been accepted.
45func (l *netListener) Close() error {
46	addr := l.net.Addr()
47	err := l.net.Close()
48	if addr.Network() == "unix" {
49		rerr := os.Remove(addr.String())
50		if rerr != nil && err == nil {
51			err = rerr
52		}
53	}
54	return err
55}
56
57// Dialer returns a dialer that can be used to connect to the listener.
58func (l *netListener) Dialer() Dialer {
59	return NetDialer(l.net.Addr().Network(), l.net.Addr().String(), net.Dialer{
60		Timeout: 5 * time.Second,
61	})
62}
63
64// NetDialer returns a Dialer using the supplied standard network dialer.
65func NetDialer(network, address string, nd net.Dialer) Dialer {
66	return &netDialer{
67		network: network,
68		address: address,
69		dialer:  nd,
70	}
71}
72
73type netDialer struct {
74	network string
75	address string
76	dialer  net.Dialer
77}
78
79func (n *netDialer) Dial(ctx context.Context) (io.ReadWriteCloser, error) {
80	return n.dialer.DialContext(ctx, n.network, n.address)
81}
82
83// NetPipeListener returns a new Listener that listens using net.Pipe.
84// It is only possibly to connect to it using the Dialier returned by the
85// Dialer method, each call to that method will generate a new pipe the other
86// side of which will be returnd from the Accept call.
87func NetPipeListener(ctx context.Context) (Listener, error) {
88	return &netPiper{
89		done:   make(chan struct{}),
90		dialed: make(chan io.ReadWriteCloser),
91	}, nil
92}
93
94// netPiper is the implementation of Listener build on top of net.Pipes.
95type netPiper struct {
96	done   chan struct{}
97	dialed chan io.ReadWriteCloser
98}
99
100// Accept blocks waiting for an incoming connection to the listener.
101func (l *netPiper) Accept(ctx context.Context) (io.ReadWriteCloser, error) {
102	// block until we have a listener, or are closed or cancelled
103	select {
104	case rwc := <-l.dialed:
105		return rwc, nil
106	case <-l.done:
107		return nil, io.EOF
108	case <-ctx.Done():
109		return nil, ctx.Err()
110	}
111}
112
113// Close will cause the listener to stop listening. It will not close any connections that have
114// already been accepted.
115func (l *netPiper) Close() error {
116	// unblock any accept calls that are pending
117	close(l.done)
118	return nil
119}
120
121func (l *netPiper) Dialer() Dialer {
122	return l
123}
124
125func (l *netPiper) Dial(ctx context.Context) (io.ReadWriteCloser, error) {
126	client, server := net.Pipe()
127	l.dialed <- server
128	return client, nil
129}
130