-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
449 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package h3conn | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/quic-go/quic-go/http3" | ||
) | ||
|
||
// Client provides HTTP3 client side connection with special arguments | ||
type Client struct { | ||
// Method sets the HTTP method for the dial | ||
// The default method, if not set, is HTTP POST. | ||
Method string | ||
// Header enables sending custom headers to the server | ||
Header http.Header | ||
// Client is a custom HTTP client to be used for the connection. | ||
// The client must have an http3.Transport as it's transport. | ||
Client *http.Client | ||
} | ||
|
||
// Connect establishes a full duplex communication with an HTTP3 server with custom client. | ||
// See h2conn.Connect documentation for more info. | ||
func (c *Client) Connect(ctx context.Context, urlStr string) (*Conn, *http.Response, error) { | ||
reader, writer := io.Pipe() | ||
|
||
// Create a request object to send to the server | ||
req, err := http.NewRequest(c.Method, urlStr, reader) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
// Apply custom headers | ||
if c.Header != nil { | ||
req.Header = c.Header | ||
} | ||
|
||
// Apply given context to the sent request | ||
req = req.WithContext(ctx) | ||
|
||
// If an http client was not defined, use the default http client | ||
httpClient := c.Client | ||
if httpClient == nil { | ||
httpClient = defaultClient.Client | ||
} | ||
|
||
// Perform the request | ||
resp, err := httpClient.Do(req) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
// Create a connection | ||
conn, ctx := newConn(req.Context(), resp.Body, writer) | ||
|
||
// Apply the connection context on the request context | ||
resp.Request = req.WithContext(ctx) | ||
|
||
return conn, resp, nil | ||
} | ||
|
||
var defaultClient = Client{ | ||
Method: http.MethodPost, | ||
Client: &http.Client{Transport: &http3.RoundTripper{}}, | ||
} | ||
|
||
// Connect establishes a full duplex communication with an HTTP3 server. | ||
// | ||
// Usage: | ||
// | ||
// conn, resp, err := h2conn.Connect(ctx, url) | ||
// if err != nil { | ||
// log.Fatalf("Initiate client: %s", err) | ||
// } | ||
// if resp.StatusCode != http.StatusOK { | ||
// log.Fatalf("Bad status code: %d", resp.StatusCode) | ||
// } | ||
// defer conn.Close() | ||
// | ||
// // use conn | ||
func Connect(ctx context.Context, urlStr string) (*Conn, *http.Response, error) { | ||
return defaultClient.Connect(ctx, urlStr) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package h3conn | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"sync" | ||
) | ||
|
||
// Conn is client/server symmetric connection. | ||
// It implements the io.Reader/io.Writer/io.Closer to read/write or close the connection to the other side. | ||
// It also has a Send/Recv function to use channels to communicate with the other side. | ||
type Conn struct { | ||
r io.Reader | ||
wc io.WriteCloser | ||
|
||
cancel context.CancelFunc | ||
|
||
wLock sync.Mutex | ||
rLock sync.Mutex | ||
} | ||
|
||
func newConn(ctx context.Context, r io.Reader, wc io.WriteCloser) (*Conn, context.Context) { | ||
ctx, cancel := context.WithCancel(ctx) | ||
return &Conn{ | ||
r: r, | ||
wc: wc, | ||
cancel: cancel, | ||
}, ctx | ||
} | ||
|
||
// Write writes data to the connection | ||
func (c *Conn) Write(data []byte) (int, error) { | ||
c.wLock.Lock() | ||
defer c.wLock.Unlock() | ||
return c.wc.Write(data) | ||
} | ||
|
||
// Read reads data from the connection | ||
func (c *Conn) Read(data []byte) (int, error) { | ||
c.rLock.Lock() | ||
defer c.rLock.Unlock() | ||
return c.r.Read(data) | ||
} | ||
|
||
// Close closes the connection | ||
func (c *Conn) Close() error { | ||
c.cancel() | ||
return c.wc.Close() | ||
} |
Oops, something went wrong.