This commit is contained in:
eyedeekay
2025-02-07 15:25:50 -05:00
parent 64c07c3913
commit 95c7710557
5 changed files with 209 additions and 20 deletions

View File

@ -0,0 +1,68 @@
# go-webrtc-net
Go library providing standard Go network interfaces (net.Conn, net.PacketConn, net.Listener) over WebRTC connections.
## Installation
```bash
go get github.com/go-i2p/go-webrtc-net
```
## Features
- Standard Go network interfaces over WebRTC
- Support for both reliable (TCP-like) and unreliable (UDP-like) modes
- Thread-safe implementation
- Context-aware connection management
- Built on pure Go WebRTC implementation ([pion/webrtc](https://github.com/pion/webrtc))
## Usage
```go
// Create a WebRTC connection
conn, err := webrtc.DialConn(underlying, "remote-peer-address")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// Use it like a regular net.Conn
_, err = conn.Write([]byte("Hello WebRTC!"))
```
```go
// Create a WebRTC listener
listener, err := webrtc.Listen(tcpListener)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
// Accept connections
conn, err := listener.Accept()
```
```go
// Create a WebRTC packet connection
pconn, err := webrtc.DialPacketConn(underlying, "remote-peer-address")
if err != nil {
log.Fatal(err)
}
defer pconn.Close()
```
## Testing
```bash
go test ./...
```
## License
MIT License
## Contributing
1. Fork the repository
2. Create your feature branch
3. Submit a pull request

121
conn.go
View File

@ -1 +1,122 @@
package webrtc
import (
"context"
"net"
"time"
"github.com/pion/webrtc/v3"
)
// DialConn creates a new WebRTC connection using the provided net.Conn for signaling
func DialConn(conn net.Conn, addr string) (net.Conn, error) {
ctx, cancel := context.WithCancel(context.Background())
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{URLs: []string{"stun:stun.l.google.com:19302"}},
},
})
if err != nil {
cancel()
return nil, err
}
c := &RTCConn{
pc: pc,
localAddr: conn.LocalAddr(),
ctx: ctx,
cancel: cancel,
readChan: make(chan []byte, 100),
}
// Set up data channel
dc, err := pc.CreateDataChannel("data", nil)
if err != nil {
cancel()
return nil, err
}
c.dc = dc
dc.OnMessage(func(msg webrtc.DataChannelMessage) {
select {
case c.readChan <- msg.Data:
case <-c.ctx.Done():
}
})
// Handle signaling
go c.handleSignaling(conn)
return c, nil
}
// Implementation of net.Conn interface methods for conn type
func (c *RTCConn) Read(b []byte) (n int, err error) {
c.mu.RLock()
if c.closed {
c.mu.RUnlock()
return 0, ErrConnectionClosed
}
c.mu.RUnlock()
select {
case data := <-c.readChan:
return copy(b, data), nil
case <-c.ctx.Done():
return 0, ErrConnectionClosed
}
}
func (c *RTCConn) Write(b []byte) (n int, err error) {
c.mu.RLock()
if c.closed {
c.mu.RUnlock()
return 0, ErrConnectionClosed
}
c.mu.RUnlock()
err = c.dc.Send(b)
if err != nil {
return 0, err
}
return len(b), nil
}
func (c *RTCConn) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return nil
}
c.closed = true
c.cancel()
if c.dc != nil {
c.dc.Close()
}
if c.pc != nil {
return c.pc.Close()
}
return nil
}
func (c *RTCConn) LocalAddr() net.Addr { return c.localAddr }
func (c *RTCConn) RemoteAddr() net.Addr { return c.remoteAddr }
func (c *RTCConn) SetDeadline(t time.Time) error {
// Implementation using context deadline
return nil
}
func (c *RTCConn) SetReadDeadline(t time.Time) error {
// Implementation using context deadline for reads
return nil
}
func (c *RTCConn) SetWriteDeadline(t time.Time) error {
// Implementation using context deadline for writes
return nil
}

View File

@ -11,11 +11,11 @@ import (
func Listen(lstn net.Listener) (net.Listener, error) {
ctx, cancel := context.WithCancel(context.Background())
l := &listener{
l := &RTCListener{
underlying: lstn,
config: &webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{URLs: []string{"stun:stun.l.google.com:19302"}},
{URLs: STUN_SERVER_URLS},
},
},
acceptChan: make(chan net.Conn),
@ -27,7 +27,7 @@ func Listen(lstn net.Listener) (net.Listener, error) {
return l, nil
}
func (l *listener) Accept() (net.Conn, error) {
func (l *RTCListener) Accept() (net.Conn, error) {
select {
case conn := <-l.acceptChan:
return conn, nil
@ -36,7 +36,7 @@ func (l *listener) Accept() (net.Conn, error) {
}
}
func (l *listener) Close() error {
func (l *RTCListener) Close() error {
l.mu.Lock()
defer l.mu.Unlock()
@ -49,11 +49,11 @@ func (l *listener) Close() error {
return l.underlying.Close()
}
func (l *listener) Addr() net.Addr {
func (l *RTCListener) Addr() net.Addr {
return l.underlying.Addr()
}
func (l *listener) acceptLoop() {
func (l *RTCListener) acceptLoop() {
for {
conn, err := l.underlying.Accept()
if err != nil {

View File

@ -9,7 +9,7 @@ import (
)
// Close implements net.PacketConn.
func (p *packetConn) Close() error {
func (p *RTCPacketConn) Close() error {
p.cancel()
if p.dc != nil {
if err := p.dc.Close(); err != nil {
@ -23,12 +23,12 @@ func (p *packetConn) Close() error {
}
// LocalAddr implements net.PacketConn.
func (p *packetConn) LocalAddr() net.Addr {
func (p *RTCPacketConn) LocalAddr() net.Addr {
return p.localAddr
}
// ReadFrom implements net.PacketConn.
func (p *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
func (p *RTCPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
select {
case <-p.ctx.Done():
return 0, nil, p.ctx.Err()
@ -39,7 +39,7 @@ func (p *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
}
// SetDeadline implements net.PacketConn.
func (p *packetConn) SetDeadline(t time.Time) error {
func (p *RTCPacketConn) SetDeadline(t time.Time) error {
err1 := p.SetReadDeadline(t)
err2 := p.SetWriteDeadline(t)
if err1 != nil {
@ -49,7 +49,7 @@ func (p *packetConn) SetDeadline(t time.Time) error {
}
// SetReadDeadline implements net.PacketConn.
func (p *packetConn) SetReadDeadline(t time.Time) error {
func (p *RTCPacketConn) SetReadDeadline(t time.Time) error {
if p.readDeadline == nil {
p.readDeadline = make(chan time.Time, 1)
}
@ -63,7 +63,7 @@ func (p *packetConn) SetReadDeadline(t time.Time) error {
}
// SetWriteDeadline implements net.PacketConn.
func (p *packetConn) SetWriteDeadline(t time.Time) error {
func (p *RTCPacketConn) SetWriteDeadline(t time.Time) error {
if p.writeDeadline == nil {
p.writeDeadline = make(chan time.Time, 1)
}
@ -77,7 +77,7 @@ func (p *packetConn) SetWriteDeadline(t time.Time) error {
}
// WriteTo implements net.PacketConn.
func (p *packetConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
func (p *RTCPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
if p.dc == nil || p.dc.ReadyState() != webrtc.DataChannelStateOpen {
return 0, net.ErrClosed
}
@ -110,7 +110,7 @@ func DialPacketConn(pconn net.PacketConn, raddr string) (net.PacketConn, error)
return nil, err
}
p := &packetConn{
p := &RTCPacketConn{
pc: pc,
localAddr: pconn.LocalAddr(),
ctx: ctx,

View File

@ -15,8 +15,8 @@ var (
ErrInvalidAddress = errors.New("invalid address")
)
// conn implements net.Conn over WebRTC
type conn struct {
// RTCConn implements net.Conn over WebRTC
type RTCConn struct {
dc *webrtc.DataChannel
pc *webrtc.PeerConnection
localAddr net.Addr
@ -28,8 +28,8 @@ type conn struct {
cancel context.CancelFunc
}
// packetConn implements net.PacketConn over WebRTC
type packetConn struct {
// RTCPacketConn implements net.PacketConn over WebRTC
type RTCPacketConn struct {
pc *webrtc.PeerConnection
dc *webrtc.DataChannel
localAddr net.Addr
@ -42,8 +42,8 @@ type packetConn struct {
writeDeadline chan time.Time
}
// listener implements net.Listener over WebRTC
type listener struct {
// RTCListener implements net.Listener over WebRTC
type RTCListener struct {
underlying net.Listener
config *webrtc.Configuration
mu sync.RWMutex