Import the files of go implementation
This commit is contained in:
Родитель
805fb733d2
Коммит
34a074d5cb
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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}
|
||||
}
|
Загрузка…
Ссылка в новой задаче