h2conn

package module
v0.0.0-...-3997dee Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 4, 2023 License: Apache-2.0 Imports: 6 Imported by: 14

README

h2conn

h2conn provides HTTP2 client-server full-duplex communication connection.

Build Status codecov GoDoc Go Report Card

Motivation

Go has a wonderful HTTP2 support that is integrated seamlessly into the HTTP1.1 implementation. There is a nice demo on https://http2.golang.org in which you can see it in action. The code for the demo is available here.

I became interested how HTTP2 can work with full-duplex communication, and saw the echo handler implementation, and a suggested client side implementation for this handler in this Github issue.

This library provides a simpler API for the same sort of "advanced usage" / "low level" / "hard core" implementation.

Examples

Check out the example directory.

Server

On the server side, in an implementation of http.Handler, the h2conn.Accept function can be used to get a full-duplex connection to the client.

type handler struct{}

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	conn, err := h2conn.Accept(w, r)
	if err != nil {
		log.Printf("Failed creating connection from %s: %s", r.RemoteAddr, err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	defer conn.Close() 
	
	// [ Use conn ... ]
	// The connection will be left open until this function will return.
	// If there is a need to wait to the client to close the connection,
	// we can wait on the request context: `<-r.Context().Done()`.
}
Client

On the client side, the h2conn.Connect function can be used in order to connect to an HTTP2 server with full-duplex communication.

func main() {
    conn, resp, err := h2conn.Connect(ctx, url, nil)
	if err != nil {
		log.Fatalf("Initiate conn: %s", err)
	}
	defer conn.Close()

	// Check server status code
	if resp.StatusCode != http.StatusOK {
		log.Fatalf("Bad status code: %d", resp.StatusCode)
	}

	// [ Use conn ... ]
}
Using the Connection

The server and the client need to decide on message format. Here are few examples that demonstrate how the client and server can communicate over the created pipe.

1. JSON

Sending and receiving JSON format is a very common thing to do.

import "encoding/json"

func main() {
	// [ Create a connection ... ]
	
	// Create an encoder and decoder from the connection
	var in, out = json.NewDecoder(conn), json.NewEncoder(conn)
	
	// Sending data into the connection using the out encoder.	
	// Any type can be sent - the important thing is that the other side will read with a
	// variable of the same type.
	// In this case, we just use a simple string.
	err = out.Encode("hello")
	// [ handle err ... ]
	
	// Receiving data from the connection using the in decoder and a variable.
	// Any type can be received - the important thing is that the other side will write data
	// to the connection of the same type.
	// In this case we assume that the other side sent us a string.
	var resp string
	err = in.Decode(&resp)	
	// [ handle err, use resp ... ]
}
2. GOB

GOB is more efficient message format, but requires both client and server to be written in Go. The example is exactly the same as in the json encoding, just switch json with gob.

import "encoding/gob"

func main() {
	// [ Create a connection ... ]
	
	var in, out = gob.NewDecoder(conn), gob.NewEncoder(conn)
	
	// Sending data into the connection using the out encoder.	
	// Any type can be sent - the important thing is that the other side will read with a
	// variable of the same type.
	// In this case, we just use a simple string.
	err = out.Encode("hello")
	// [ handle err ... ]
	
	// Receiving data from the connection using the in decoder and a variable.
	// Any type can be received - the important thing is that the other side will write data
	// to the connection of the same type.
	// In this case we assume that the other side sent us a string.
	var resp string
	err = in.Decode(&resp)	
	// [ handle err, use resp ... ]
}
3. Constant Buffer Size
// Create constant size buffer
const bufSize = 10

func main () {
	// [ Create a connection ... ]
	
	buf := make([]byte, bufSize)

	// Write to the connection:
	// [ Write something to buf... ]
	_, err = conn.Write(buf)

	// Read from the connection:
	_, err = conn.Read(buf)
	// [ Use buf... ]
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrHTTP2NotSupported = fmt.Errorf("HTTP2 not supported")

ErrHTTP2NotSupported is returned by Accept if the client connection does not support HTTP2 connection. The server than can response to the client with an HTTP1.1 as he wishes.

Functions

This section is empty.

Types

type Client

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 http2.Transport as it's transport.
	Client *http.Client
}

Client provides HTTP2 client side connection with special arguments

func (*Client) Connect

func (c *Client) Connect(ctx context.Context, urlStr string) (*Conn, *http.Response, error)

Connect establishes a full duplex communication with an HTTP2 server with custom client. See h2conn.Connect documentation for more info.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

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.

func Accept

func Accept(w http.ResponseWriter, r *http.Request) (*Conn, error)

Accept is used on a server http.Handler to extract a full-duplex communication object with the client. The server connection will be closed when the http handler function will return. If the client does not support HTTP2, an ErrHTTP2NotSupported is returned.

Usage:

     func (w http.ResponseWriter, r *http.Request) {
         conn, err := h2conn.Accept(w, r)
         if err != nil {
		        log.Printf("Failed creating http2 connection: %s", err)
		        http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		        return
	        }
         // use conn
     }

func Connect

func Connect(ctx context.Context, urlStr string) (*Conn, *http.Response, error)

Connect establishes a full duplex communication with an HTTP2 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 (*Conn) Close

func (c *Conn) Close() error

Close closes the connection

func (*Conn) Read

func (c *Conn) Read(data []byte) (int, error)

Read reads data from the connection

func (*Conn) Write

func (c *Conn) Write(data []byte) (int, error)

Write writes data to the connection

type Server

type Server struct {
	StatusCode int
}

Server can "accept" an http2 connection to obtain a read/write object for full duplex communication with a client.

func (*Server) Accept

func (u *Server) Accept(w http.ResponseWriter, r *http.Request) (*Conn, error)

Accept is used on a server http.Handler to extract a full-duplex communication object with the client. See h2conn.Accept documentation for more info.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL