-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp321.go
More file actions
135 lines (117 loc) · 3.61 KB
/
http321.go
File metadata and controls
135 lines (117 loc) · 3.61 KB
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
package http321
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"sync"
"time"
"github.com/quic-go/quic-go"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
// QuicNetListener implements a net.Listener on top of a quic.Connection
type QuicNetListener struct {
quic.Connection
}
var _ net.Listener = &QuicNetListener{}
func (l *QuicNetListener) Accept() (net.Conn, error) {
stream, err := l.Connection.AcceptStream(context.Background())
if err != nil {
return nil, fmt.Errorf("quicListener accept: %w", err)
}
// return &ReadWriteConn{Reader: stream, Writer: stream, Closer: stream}, nil
return &ReadWriteConn{Reader: &EchoReader{stream}, Writer: &EchoWriter{stream}, Closer: stream}, nil
}
func (l *QuicNetListener) Close() error {
return l.Connection.CloseWithError(quic.ApplicationErrorCode(quic.NoError), "")
}
func (l *QuicNetListener) Addr() net.Addr {
return l.Connection.LocalAddr()
}
// ReadWriteConn implement net.Conn for a quic.Stream
type ReadWriteConn struct {
io.Reader
io.Writer
io.Closer
}
func (c *ReadWriteConn) LocalAddr() net.Addr { return &net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0} }
func (c *ReadWriteConn) RemoteAddr() net.Addr { return &net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0} }
func (c *ReadWriteConn) SetDeadline(t time.Time) error { return nil }
func (c *ReadWriteConn) SetReadDeadline(t time.Time) error { return nil }
func (c *ReadWriteConn) SetWriteDeadline(t time.Time) error { return nil }
func QuicConnDial(conn quic.Connection) (net.Conn, error) {
stream, err := conn.OpenStreamSync(context.Background())
if err != nil {
return nil, fmt.Errorf("quicConnDial openStreamSync: %w", err)
}
return &ReadWriteConn{Reader: stream, Writer: stream, Closer: stream}, nil
}
type flushWriter struct {
w io.Writer
}
func (fw flushWriter) Write(p []byte) (n int, err error) {
n, err = fw.w.Write(p)
if f, ok := fw.w.(http.Flusher); ok {
f.Flush()
}
return
}
type HTTP2OverQuicListener struct {
once sync.Once
listener *QuicNetListener
conns chan net.Conn
server *http.Server
}
var _ net.Listener = &HTTP2OverQuicListener{}
func (l *HTTP2OverQuicListener) Accept() (net.Conn, error) {
l.once.Do(func() {
l.conns = make(chan net.Conn, 1)
handler := h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
pReader, pWriter := io.Pipe()
l.conns <- &ReadWriteConn{Reader: r.Body, Writer: pWriter, Closer: r.Body}
_, _ = io.Copy(flushWriter{w}, pReader)
}), &http2.Server{})
http2Server := &http.Server{Handler: handler}
go func() { _ = http2Server.Serve(l.listener) }()
})
return <-l.conns, nil
}
func (l *HTTP2OverQuicListener) Close() error {
if l.server != nil {
return l.server.Close()
}
return nil
}
func (l *HTTP2OverQuicListener) Addr() net.Addr {
return l.listener.Addr()
}
func HTTP2OverQuicDial(conn quic.Connection) (net.Conn, error) {
client := &http.Client{
Transport: &http2.Transport{
AllowHTTP: true, // Enable h2c support
DialTLSContext: func(ctx context.Context,
network, addr string, cfg *tls.Config) (net.Conn, error) {
return QuicConnDial(conn)
},
},
}
inReader, inWriter := io.Pipe()
outReader, outWriter := io.Pipe()
req, err := http.NewRequest(http.MethodPost, "http://whatever", io.NopCloser(inReader))
if err != nil {
return nil, err
}
go func() {
resp, err := client.Do(req)
if err != nil {
fmt.Println("HTTP2OverQuicDial error", err)
}
_, _ = io.Copy(outWriter, resp.Body)
}()
time.Sleep(1 * time.Millisecond) // :(
return &ReadWriteConn{Reader: outReader, Writer: inWriter, Closer: outReader}, nil
}