-
Notifications
You must be signed in to change notification settings - Fork 2
/
connection.go
200 lines (176 loc) · 5.39 KB
/
connection.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package quiche
/*
#include <stdlib.h>
#include <sys/types.h>
#include "quiche.h"
*/
import "C"
import (
"fmt"
"time"
"unsafe"
)
// Connection is a QUIC connection.
type Connection C.quiche_conn
// Accept creates a new server-side connection.
func Accept(scid []byte, odcid []byte, config *Config) *Connection {
// odcid is optional
var odcidp *C.uint8_t
if odcid != nil {
odcidp = cbytes(odcid)
}
conn := C.quiche_accept(cbytes(scid), clen(scid),
odcidp, clen(odcid),
(*C.quiche_config)(config))
return (*Connection)(conn)
}
// Connect creates a new server-side connection.
func Connect(serverName string, scid []byte, config *Config) *Connection {
// serverName is optional
var snp *C.char
if serverName != "" {
snp = C.CString(serverName)
}
conn := C.quiche_connect(snp,
cbytes(scid), clen(scid),
(*C.quiche_config)(config))
if snp != nil {
C.free(unsafe.Pointer(snp))
}
return (*Connection)(conn)
}
// Recv processes QUIC packets received from the peer.
func (c *Connection) Recv(b []byte) (int, error) {
n := C.quiche_conn_recv((*C.quiche_conn)(c),
cbytes(b), clen(b))
if n < 0 {
return 0, Error(n)
}
return int(n), nil
}
// Send writes a single QUIC packet to be sent to the peer.
func (c *Connection) Send(b []byte) (int, error) {
n := C.quiche_conn_send((*C.quiche_conn)(c),
cbytes(b), clen(b))
if n < 0 {
return 0, Error(n)
}
return int(n), nil
}
// StreamRecv reads contiguous data from a stream.
func (c *Connection) StreamRecv(streamID uint64, b []byte) (int, bool, error) {
var fin C.bool
n := C.quiche_conn_stream_recv((*C.quiche_conn)(c),
C.uint64_t(streamID),
cbytes(b), clen(b),
&fin)
if n < 0 {
return 0, false, Error(n)
}
return int(n), bool(fin), nil
}
// StreamSend writes data to a stream.
func (c *Connection) StreamSend(streamID uint64, b []byte, fin bool) (int, error) {
n := C.quiche_conn_stream_send((*C.quiche_conn)(c),
C.uint64_t(streamID),
cbytes(b), clen(b),
C.bool(fin))
if n < 0 {
return 0, Error(n)
}
return int(n), nil
}
// Shutdown is the stream's side to shutdown.
type Shutdown int
const (
ShutdownRead = Shutdown(C.QUICHE_SHUTDOWN_READ) // Stop receiving stream data.
ShutdownWrite = Shutdown(C.QUICHE_SHUTDOWN_WRITE) // Stop sending stream data.
)
// StreamShutdown shuts down reading or writing from/to the specified stream.
func (c *Connection) StreamShutdown(streamID uint64, direction Shutdown, err uint64) error {
n := C.quiche_conn_stream_shutdown((*C.quiche_conn)(c),
C.uint64_t(streamID),
C.enum_quiche_shutdown(direction),
C.uint64_t(err))
if n < 0 {
return Error(n)
}
return nil
}
// StreamFinished returns true if all the data has been read from the specified stream.
func (c *Connection) StreamFinished(streamID uint64) bool {
return bool(C.quiche_conn_stream_finished((*C.quiche_conn)(c), C.uint64_t(streamID)))
}
// ReadableNext fetches the next stream that has outstanding data to read. Returns false if
// there are no readable streams.
func (c *Connection) ReadableNext() (uint64, bool) {
var streamID C.uint64_t
next := C.quiche_readable_next((*C.quiche_conn)(c), &streamID)
if next {
return uint64(streamID), true
}
return 0, false
}
// Timeout returns the amount of time until the next timeout event, as nanoseconds.
func (c *Connection) Timeout() time.Duration {
return time.Duration(C.quiche_conn_timeout_as_nanos((*C.quiche_conn)(c))) * time.Nanosecond
}
// OnTimeout processes a timeout event.
func (c *Connection) OnTimeout() {
C.quiche_conn_on_timeout((*C.quiche_conn)(c))
}
// Close closes the connection with the given error and reason.
func (c *Connection) Close(app bool, errCode uint16, reason []byte) error {
n := C.quiche_conn_close((*C.quiche_conn)(c),
C.bool(app),
C.uint16_t(errCode),
cbytes(reason), clen(reason))
if n < 0 {
return Error(n)
}
return nil
}
// ApplicationProto returns the negotiated ALPN protocol.
func (c *Connection) ApplicationProto() []byte {
var out *C.uint8_t
var outLen C.size_t
C.quiche_conn_application_proto((*C.quiche_conn)(c), &out, &outLen)
if outLen <= 0 {
return nil
}
return C.GoBytes(unsafe.Pointer(out), C.int(outLen))
}
// IsEstablished returns true if the connection handshake is complete.
func (c *Connection) IsEstablished() bool {
return bool(C.quiche_conn_is_established((*C.quiche_conn)(c)))
}
// IsClosed returns true if the connection is closed.
func (c *Connection) IsClosed() bool {
return bool(C.quiche_conn_is_closed((*C.quiche_conn)(c)))
}
// Stats collects and returns statistics about the connection.
func (c *Connection) Stats(stats *Stats) {
var s C.quiche_stats
C.quiche_conn_stats((*C.quiche_conn)(c), &s)
stats.Recv = uint64(s.recv)
stats.Sent = uint64(s.sent)
stats.Lost = uint64(s.lost)
stats.RTT = time.Duration(s.rtt) * time.Nanosecond
stats.CWnd = uint64(s.cwnd)
}
// Free frees the connection object.
func (c *Connection) Free() {
C.quiche_conn_free((*C.quiche_conn)(c))
}
// Stats is statistics about the connection.
type Stats struct {
Recv uint64 // The number of QUIC packets received on this connection.
Sent uint64 // The number of QUIC packets sent on this connection.
Lost uint64 // The number of QUIC packets that were lost.
RTT time.Duration // The estimated round-trip time of the connection.
CWnd uint64 // The size in bytes of the connection's congestion window.
}
func (s *Stats) String() string {
return fmt.Sprintf("recv=%d sent=%d lost=%d rtt=%s",
s.Recv, s.Sent, s.Lost, s.RTT)
}