Skip to content

Commit

Permalink
*: initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
igolaizola committed Aug 3, 2018
1 parent b1e2e2e commit 6af5993
Show file tree
Hide file tree
Showing 197 changed files with 159,803 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor/** -diff
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,21 @@
*.dll
*.so
*.dylib
bin/
main

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Debug
debug
debug.test

# Others
.idea/
.reports/
.temp/
.vscode/
74 changes: 74 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true

[[constraint]]
name = "github.com/spf13/cobra"
version = "0.0.3"

[[constraint]]
name = "golang.org/x/net"
branch = "master"

[prune]
go-tests = true
unused-packages = true
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
# h2-stream

HTTP2 client and server implementation in GO that holds a persistent data stream
- Client takes data from standard input and forwards it to the server
- Client forwards server responses to standard output
- Server responds echoing the received data

TLS and non TLS options are both available

## Usage

### without TLS

launch server:
```
go run cmd/h2-stream/main.go serve --addr=localhost:8080 --tls=false
```

launch client:
```
go run cmd/h2-stream/main.go cli --addr=http://localhost:8080 --method=POST
```

### with TLS
launch server:
```
go run cmd/h2-stream/main.go serve --addr=localhost:8080 --tls=true --cert=certs/cert.pem --key=certs/key.pem
```

launch client:
```
go run cmd/h2-stream/main.go cli --addr=https://localhost:8080 --method=POST --insecure
```
19 changes: 19 additions & 0 deletions certs/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDADCCAeigAwIBAgIRAMlZFfrjDjpriu1r+XIr1kwwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xODA4MDIxMTI0MTlaFw0xOTA4MDIxMTI0
MTlaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDHYGCQkL4xc4djNNtjWcuPAGLmiRLI+uompmccJ7f9vUZgu/gO9oVS
nQlVRNX4LS0TnZjyQMso+9ZNt9sdyDohkMVmS0O27kD9gz2Pz+otYg0w4TVX0pJp
c3jwvSoXdqNxrj+Fk9aptIFsfipN2cE7uFA40+rZSlyND+lSB/VvNKILSrp6Ugmo
CpRRFJ0O8VjYV+qU7RZh9HFIvtW6w9uLeN2jD+k7VGVt6hADpdoSzQiAerZ5+8ee
IcmAj/G5COGbGAnbuy73/Bmo9b728UXo6b+7GdyXYij/pev/0OcIoT7WKFQJJyVz
owc+yyEHhKpuKqCy9KNzPQqm7je//BptAgMBAAGjUTBPMA4GA1UdDwEB/wQEAwIF
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBoGA1UdEQQTMBGC
CWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAlDF2c4ktrz1BJcQL
PhyynqOmLCJiPw/A9vSCOuaH2RduHufiO80RKW9KRiLsAAvSToAsFrTNlTL3Jdjp
UnWjal+gMh3fU+Fw3lGlq/UeYxMjZsTATazy2D2dJWwv0PUWo7dE0w/Thh1SdhEU
cNpoIDTsrnfa4P300XK+ej5A6gVYa++adAh3QdjLAzOfDxIInMwinMIQy9kACPvd
XNZ4AfD+wsH0dHTFPr5k12ZJbPMljCFe/rmbDoEpxOwimBcnRohEgOIbKjwEUXRi
B+q7AnJ0Q1rK/J7ikSDFBBGlg8wHWz+FCINmyyv62qClErI4aA/WN6+ilINJV/gG
qgNGqQ==
-----END CERTIFICATE-----
27 changes: 27 additions & 0 deletions certs/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAx2BgkJC+MXOHYzTbY1nLjwBi5okSyPrqJqZnHCe3/b1GYLv4
DvaFUp0JVUTV+C0tE52Y8kDLKPvWTbfbHcg6IZDFZktDtu5A/YM9j8/qLWINMOE1
V9KSaXN48L0qF3ajca4/hZPWqbSBbH4qTdnBO7hQONPq2UpcjQ/pUgf1bzSiC0q6
elIJqAqUURSdDvFY2FfqlO0WYfRxSL7VusPbi3jdow/pO1RlbeoQA6XaEs0IgHq2
efvHniHJgI/xuQjhmxgJ27su9/wZqPW+9vFF6Om/uxncl2Io/6Xr/9DnCKE+1ihU
CSclc6MHPsshB4SqbiqgsvSjcz0Kpu43v/wabQIDAQABAoIBADbktjGXaIY9BL2v
w+eqxXzt4k0O2Hk1fFp/3kvGM8ZM4p+noTidbz+7tOIhPbhC1/Japc2tQUJbdDmZ
sV6VzkuHjJIJju9C0en6xGxgFl3AbVlT6FfxxhX6kQXXT0t+gqm+DAc/GQ9If4nb
gtJEbgt/R7cdwb9p1emQw/Ct+ElRe+xTZ36Vw1wgyLUmJJB3IAj5JTqeBwqFQOvt
fV3zuS/zzzfXuhShwJMpsdHVJJzULeZPU1nhxAeTKGSF8XwBOL0hSL4ikS0s/U0P
RoTL4flKsC3YwFOVq3Cn8bZW0xI5h/UmISJhkyj8th6PV72NNCBJ5ogLAr24TqH4
Emvj14ECgYEA1uLZXRue7vScYbpEUud+crM6ejBNOhib968J/Au1yf6aS9dgALVP
MaZXg2GKRJWLHr3om4vJOUP14970NkJKlCkqJD8uUjKtGchcaNELPQjwM7HjmBAW
VHANjHFJTEIGG/v9wwVE/ZUf9ljqE7jFA+DkJ5GrSfomwU1eKEthmQkCgYEA7YXc
9zD/dmHg4LpbBP7R8syC4Ijl+ux/huuBh6GIbyLKCagtL3TFSNInDPO6rT1YdgLZ
7WZSXaQ98aq2Q0vNRiXMccrsx6nPj20arRaZROZgz7s5W62Eexbz/b5rUrAuXzJF
CVF6raZUxUKlF1b2ybc93ScqqjfWfoyZebE8w0UCgYEApxz+O+maHW2AHIR2VB8R
+HOoG5Rqyq6OxP2Mf0ZAFxn4ttiFIaffMdaSImt90z6VVdANEMKSOAXBOXiPZY8C
XtzwmAXGqUgd1Ho8W4uO+OV1oE5MmFqScxI9hyYnAbYq+CJtw/faIneRxsx5JeNA
3HZOGPOxSTPQZe4cNqwA97kCgYBfVvYk+rPwDsW3LtZOIQKg1NpLymeV2swtmeZ6
TKp5AZvbWHgarmJqIoCuQD7UPuV9KRPUqNey4rRChuV2Cb0xxQZVPsDgPBcmWQL2
KzYGY/rEJ0CUvgeJaOMzHPXzUOisKX9wiBYYEcXBEEk4Hx4cRcM9O/VyMcuVLFaG
dFARiQKBgQCHKrb0SzYVnaEWFR+GP+sJMfxrhq/N8m+WcCpoQ/UIvguMWmFKtVtC
WVTd3XNizIpuNpDgGI4qvIwmEs7UhAzemxasYoP3y3FO2dT0QGC+T1SX/BsW6AiO
fi06KUiLh/4rJtf2wph2wN8SPAY4yQkopFlDYTJNmhhYsKTGIhrpww==
-----END RSA PRIVATE KEY-----
15 changes: 15 additions & 0 deletions cmd/h2-stream/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"log"

"github.com/igarciaolaizola/h2-stream/internal/cli"
)

func main() {
cmd := cli.NewCommand()

if err := cmd.Execute(); err != nil {
log.Fatal(err)
}
}
63 changes: 63 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cli

import (
"github.com/igarciaolaizola/h2-stream/internal/client"
"github.com/igarciaolaizola/h2-stream/internal/server"
"github.com/spf13/cobra"
)

// NewCommand create and returns the root cli command
func NewCommand() *cobra.Command {
tunnelCmd := &cobra.Command{
Use: "tunnel",
Short: "CLI tool aimed to handle http2 streams",
Long: `CLI tool aimed to handle http2 streams: client or server side.`,
}

tunnelCmd.AddCommand(newServerCommand())
tunnelCmd.AddCommand(newClientCommand())
return tunnelCmd
}

func newServerCommand() *cobra.Command {
var addr, cert, key string
var tls bool

cmd := &cobra.Command{
Use: "serve",
Short: "Launches the http2 stream echo server",
Long: `Launches the http2 stream server that echoes any incoming data.`,
RunE: func(c *cobra.Command, args []string) error {
return server.Run(addr, tls, cert, key)
},
}

cmd.Flags().StringVar(&addr, "addr", "localhost:8080", "http listening address")
cmd.Flags().BoolVar(&tls, "tls", true, "enable tls (https)")
cmd.Flags().StringVar(&cert, "cert", "certs/cert.pem", "tls certificate file")
cmd.Flags().StringVar(&key, "key", "certs/key.pem", "tls key file")
return cmd
}

func newClientCommand() *cobra.Command {
var cfg client.Config

cmd := &cobra.Command{
Use: "cli",
Short: "Launches the http2 stream client",
Long: `Launches the http2 stream client that sends any writen to stdin.`,
RunE: func(c *cobra.Command, args []string) error {
client, err := client.New(cfg)
if err != nil {
return err
}
return client.Run()
},
}

cmd.Flags().StringVar(&cfg.Addr, "addr", "https://localhost:8080", "http server address")
cmd.Flags().StringVar(&cfg.Method, "method", "POST", "http method (GET, POST, PUT, DELETE...)")
cmd.Flags().BoolVar(&cfg.Insecure, "insecure", false, "skip tls validation")
cmd.Flags().StringArrayVar(&cfg.Headers, "header", nil, "custom headers, ex.: \"Content-Type: application/json\"")
return cmd
}
98 changes: 98 additions & 0 deletions internal/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package client

import (
"crypto/tls"
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"os"
"strings"

"golang.org/x/net/http2"
)

// Config of the http2 client
type Config struct {
Addr string
Method string
Insecure bool
Headers []string
}

// Client is a http2 client
type Client struct {
Config
client http.Client
}

// New returns a new client
func New(cfg Config) (*Client, error) {
// Parse url
url, err := url.Parse(cfg.Addr)
if err != nil {
return nil, err
}

var tlsConfig *tls.Config
tlsEnabled := url.Scheme == "https"
if tlsEnabled {
tlsConfig = &tls.Config{InsecureSkipVerify: cfg.Insecure}
}

// Create http2 client
client := http.Client{
Transport: &http2.Transport{
AllowHTTP: !tlsEnabled,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
if tlsEnabled {
return tls.Dial(network, addr, cfg)
}
return net.Dial(network, addr)
},
TLSClientConfig: tlsConfig,
},
}

return &Client{
client: client,
Config: cfg,
}, nil
}

// Run launches a client
func (c *Client) Run() error {
// Establish http connection
req, err := http.NewRequest(c.Method, c.Addr, os.Stdin)
if c.Headers != nil {
for _, h := range c.Headers {
kv := strings.Split(h, ":")
if len(kv) != 2 {
return errors.New("client: error parsing header")
}
k := strings.Trim(kv[0], " ")
v := strings.Trim(kv[1], " ")
fmt.Println(k, " ", v)
req.Header.Add(k, v)
}
}
if err != nil {
return err
}
resp, err := c.client.Do(req)
if err != nil {
return err
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Println(err)
}
}()

log.Println(fmt.Sprintf("Connected to %s", c.Addr))
io.Copy(os.Stdout, resp.Body)
return nil
}
Loading

0 comments on commit 6af5993

Please sign in to comment.