Import the files of go implementation

This commit is contained in:
Moto Ishizawa 2014-12-02 23:57:48 +09:00
Родитель 805fb733d2
Коммит 34a074d5cb
7 изменённых файлов: 533 добавлений и 0 удалений

42
3_5.go Normal file
Просмотреть файл

@ -0,0 +1,42 @@
package h2spec
import (
"fmt"
"io"
"time"
)
func TestHttp2ConnectionPreface(ctx *Context) {
PrintHeader("3.5. HTTP/2 Connection Preface", 0)
func(ctx *Context) {
desc := "Sends invalid connection preface"
msg := "The endpoint MUST terminate the TCP connection."
result := false
tcpConn := CreateTcpConn(ctx)
defer tcpConn.conn.Close()
fmt.Fprintf(tcpConn.conn, "INVALID CONNECTION PREFACE")
timeCh := time.After(3 * time.Second)
loop:
for {
select {
case <-tcpConn.dataCh:
break
case err := <-tcpConn.errCh:
if err == io.EOF {
result = true
break loop
}
case <-timeCh:
break loop
}
}
PrintResult(result, desc, msg, 0)
}(ctx)
PrintFooter()
}

78
4_2.go Normal file
Просмотреть файл

@ -0,0 +1,78 @@
package h2spec
import (
"fmt"
"github.com/bradfitz/http2"
"time"
)
func TestFrameSize(ctx *Context) {
PrintHeader("4.2. Frame Size", 0)
msg := "The endpoint MUST send a FRAME_SIZE_ERROR error."
func(ctx *Context) {
desc := "Sends too small size frame"
result := false
http2Conn := CreateHttp2Conn(ctx, true)
defer http2Conn.conn.Close()
fmt.Fprintf(http2Conn.conn, "\x00\x00\x00\x08\x00\x00\x00\x00\x00")
timeCh := time.After(3 * time.Second)
loop:
for {
select {
case f := <-http2Conn.dataCh:
gf := f.(*http2.GoAwayFrame)
if gf != nil {
if gf.ErrCode == http2.ErrCodeFrameSize {
result = true
break loop
}
}
break
case <-http2Conn.errCh:
break loop
case <-timeCh:
break loop
}
}
PrintResult(result, desc, msg, 0)
}(ctx)
func(ctx *Context) {
desc := "Sends too large size frame"
result := false
http2Conn := CreateHttp2Conn(ctx, true)
defer http2Conn.conn.Close()
fmt.Fprintf(http2Conn.conn, "\x00\x00\x0f\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
timeCh := time.After(3 * time.Second)
loop:
for {
select {
case f := <-http2Conn.dataCh:
gf := f.(*http2.GoAwayFrame)
if gf != nil {
if gf.ErrCode == http2.ErrCodeFrameSize {
result = true
break loop
}
}
break
case <-http2Conn.errCh:
break loop
case <-timeCh:
break loop
}
}
PrintResult(result, desc, msg, 0)
}(ctx)
PrintFooter()
}

49
4_3.go Normal file
Просмотреть файл

@ -0,0 +1,49 @@
package h2spec
import (
"fmt"
"github.com/bradfitz/http2"
"time"
)
func TestHeaderCompressionAndDecompression(ctx *Context) {
PrintHeader("4.3. Header Compression and Decompression", 0)
func(ctx *Context) {
desc := "Sends invalid header block fragment"
msg := "The endpoint MUST terminate the connection with a connection error of type COMPRESSION_ERROR."
rfResult := false
gfResult := false
http2Conn := CreateHttp2Conn(ctx, true)
defer http2Conn.conn.Close()
fmt.Fprintf(http2Conn.conn, "\x00\x00\x14\x01\x05\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
timeCh := time.After(3 * time.Second)
loop:
for {
select {
case f := <-http2Conn.dataCh:
switch frame := f.(type) {
case *http2.RSTStreamFrame:
if frame.ErrCode == http2.ErrCodeCompression {
rfResult = true
}
case *http2.GoAwayFrame:
if frame.ErrCode == http2.ErrCodeCompression {
gfResult = true
}
}
case <-http2Conn.errCh:
break loop
case <-timeCh:
break loop
}
}
PrintResult(rfResult && gfResult, desc, msg, 0)
}(ctx)
PrintFooter()
}

122
5_1.go Normal file
Просмотреть файл

@ -0,0 +1,122 @@
package h2spec
import (
"bytes"
"github.com/bradfitz/http2"
"github.com/bradfitz/http2/hpack"
"time"
)
func TestStreamStates(ctx *Context) {
PrintHeader("5.1. Stream States", 0)
TestStreamIdentifiers(ctx)
PrintFooter()
}
func TestStreamIdentifiers(ctx *Context) {
PrintHeader("5.1.1. Stream Identifiers", 1)
msg := "The endpoint MUST respond with a connection error of type PROTOCOL_ERROR."
func(ctx *Context) {
desc := "Sends even-numbered stream identifier"
result := false
http2Conn := CreateHttp2Conn(ctx, true)
defer http2Conn.conn.Close()
var buf bytes.Buffer
hdrs := []hpack.HeaderField{
pair(":method", "GET"),
pair(":scheme", "http"),
pair(":path", "/"),
pair(":authority", ctx.Authority()),
}
enc := hpack.NewEncoder(&buf)
for _, hf := range hdrs {
_ = enc.WriteField(hf)
}
var hp http2.HeadersFrameParam
hp.StreamID = 2
hp.EndStream = true
hp.EndHeaders = true
hp.BlockFragment = buf.Bytes()
http2Conn.fr.WriteHeaders(hp)
timeCh := time.After(3 * time.Second)
loop:
for {
select {
case f := <-http2Conn.dataCh:
gf, ok := f.(*http2.GoAwayFrame)
if ok {
if gf.ErrCode == http2.ErrCodeProtocol {
result = true
}
}
case <-http2Conn.errCh:
break loop
case <-timeCh:
break loop
}
}
PrintResult(result, desc, msg, 1)
}(ctx)
func(ctx *Context) {
desc := "Sends stream identifier that is numerically smaller than previous"
result := false
http2Conn := CreateHttp2Conn(ctx, true)
defer http2Conn.conn.Close()
var buf bytes.Buffer
hdrs := []hpack.HeaderField{
pair(":method", "GET"),
pair(":scheme", "http"),
pair(":path", "/"),
pair(":authority", ctx.Authority()),
}
enc := hpack.NewEncoder(&buf)
for _, hf := range hdrs {
_ = enc.WriteField(hf)
}
var hp1 http2.HeadersFrameParam
hp1.StreamID = 5
hp1.EndStream = true
hp1.EndHeaders = true
hp1.BlockFragment = buf.Bytes()
http2Conn.fr.WriteHeaders(hp1)
var hp2 http2.HeadersFrameParam
hp2.StreamID = 3
hp2.EndStream = true
hp2.EndHeaders = true
hp2.BlockFragment = buf.Bytes()
http2Conn.fr.WriteHeaders(hp2)
timeCh := time.After(3 * time.Second)
loop:
for {
select {
case f := <-http2Conn.dataCh:
gf, ok := f.(*http2.GoAwayFrame)
if ok {
if gf.ErrCode == http2.ErrCodeProtocol {
result = true
}
}
case <-http2Conn.errCh:
break loop
case <-timeCh:
break loop
}
}
PrintResult(result, desc, msg, 1)
}(ctx)
}

52
5_4.go Normal file
Просмотреть файл

@ -0,0 +1,52 @@
package h2spec
import (
"github.com/bradfitz/http2"
"io"
"time"
)
func TestErrorHandling(ctx *Context) {
PrintHeader("5.4. Error Handling", 0)
TestConnectionErrorHandling(ctx)
PrintFooter()
}
func TestConnectionErrorHandling(ctx *Context) {
PrintHeader("5.4.1. Connection Error Handling", 1)
func(ctx *Context) {
desc := "Receives a GOAWAY frame"
msg := "After sending the GOAWAY frame, the endpoint MUST close the TCP connection."
gfResult := false
closeResult := false
http2Conn := CreateHttp2Conn(ctx, true)
defer http2Conn.conn.Close()
http2Conn.fr.WriteData(1, true, []byte("test"))
timeCh := time.After(3 * time.Second)
loop:
for {
select {
case f := <-http2Conn.dataCh:
gf, ok := f.(*http2.GoAwayFrame)
if ok {
if gf.ErrCode == http2.ErrCodeProtocol {
gfResult = false
}
}
case err := <-http2Conn.errCh:
if err == io.EOF {
closeResult = true
}
break loop
case <-timeCh:
break loop
}
}
PrintResult(gfResult && closeResult, desc, msg, 1)
}(ctx)
}

30
cmd/h2spec.go Normal file
Просмотреть файл

@ -0,0 +1,30 @@
package main
import (
"flag"
"fmt"
"github.com/summerwind/h2spec"
"os"
)
func main() {
port := flag.Int("p", 80, "Target port")
host := flag.String("h", "127.0.0.1", "Target host")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage %s: [OPTIONS]\n\n", os.Args[0])
fmt.Println("Options:")
fmt.Println(" -p: Target port. (Default: 80)")
fmt.Println(" -h: Target host. (Default: 127.0.0.1)")
fmt.Println(" --help: Display this help and exit.")
os.Exit(1)
}
flag.Parse()
var ctx h2spec.Context
ctx.Port = *port
ctx.Host = *host
h2spec.Run(&ctx)
}

160
h2spec.go Normal file
Просмотреть файл

@ -0,0 +1,160 @@
package h2spec
import (
"fmt"
"github.com/bradfitz/http2"
"github.com/bradfitz/http2/hpack"
"net"
"os"
"strings"
"time"
)
type TcpConn struct {
conn net.Conn
dataCh chan []byte
errCh chan error
}
type Http2Conn struct {
conn net.Conn
fr *http2.Framer
dataCh chan http2.Frame
errCh chan error
}
type Context struct {
Port int
Host string
}
func (ctx *Context) Authority() (authority string) {
return fmt.Sprintf("%s:%d", ctx.Host, ctx.Port)
}
func Run(ctx *Context) {
TestHttp2ConnectionPreface(ctx)
TestFrameSize(ctx)
TestHeaderCompressionAndDecompression(ctx)
TestStreamStates(ctx)
TestErrorHandling(ctx)
}
func CreateTcpConn(ctx *Context) *TcpConn {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ctx.Host, ctx.Port))
if err != nil {
fmt.Println("Unable to connect to the target server.")
os.Exit(1)
}
dataCh := make(chan []byte)
errCh := make(chan error, 1)
tcpConn := &TcpConn{
conn: conn,
dataCh: dataCh,
errCh: errCh,
}
go func() {
for {
buf := make([]byte, 512)
_, err := conn.Read(buf)
dataCh <- buf
if err != nil {
errCh <- err
return
}
}
}()
return tcpConn
}
func CreateHttp2Conn(ctx *Context, sn bool) *Http2Conn {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ctx.Host, ctx.Port))
if err != nil {
fmt.Println("Unable to connect to the target server.")
os.Exit(1)
}
fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
if sn {
done := false
fr := http2.NewFramer(conn, conn)
fr.WriteSettings()
for {
f, _ := fr.ReadFrame()
switch f := f.(type) {
case *http2.SettingsFrame:
if f.IsAck() {
done = true
} else {
fr.WriteSettingsAck()
}
default:
done = true
}
if done {
break
}
}
}
fr := http2.NewFramer(conn, conn)
dataCh := make(chan http2.Frame)
errCh := make(chan error, 1)
http2Conn := &Http2Conn{
conn: conn,
fr: fr,
dataCh: dataCh,
errCh: errCh,
}
go func() {
for {
f, err := fr.ReadFrame()
dataCh <- f
if err != nil {
errCh <- err
return
}
}
}()
return http2Conn
}
func SetReadTimer(conn net.Conn, sec time.Duration) {
now := time.Now()
conn.SetReadDeadline(now.Add(time.Second * sec))
}
func PrintHeader(title string, i int) {
fmt.Printf("%s%s\n", strings.Repeat(" ", i), title)
}
func PrintFooter() {
fmt.Println("")
}
func PrintResult(result bool, desc string, msg string, i int) {
var mark string
indent := strings.Repeat(" ", i+1)
if result {
mark = "✓"
fmt.Printf("%s\x1b[32m%s\x1b[0m \x1b[90m%s\x1b[0m\n", indent, mark, desc)
} else {
mark = "×"
fmt.Printf("%s\x1b[31m%s %s\x1b[0m\n", indent, mark, desc)
fmt.Printf("%s\x1b[31m - %s\x1b[0m\n", indent, msg)
}
}
func pair(name, value string) hpack.HeaderField {
return hpack.HeaderField{Name: name, Value: value}
}