mirror of
https://github.com/golang/go
synced 2024-11-22 03:44:39 -07:00
http/spdy: improve error handling.
Create a new spdy.Error type that includes the enumerated error type and the associated stream id (0 if not associated with a specific stream). This will let users handle errors differently (RST_STREAM vs GOAWAY). R=bradfitz, rsc, rogpeppe CC=golang-dev https://golang.org/cl/4532131
This commit is contained in:
parent
400d825ea0
commit
5af8e53a14
@ -80,7 +80,7 @@ func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) os.Error {
|
|||||||
func newControlFrame(frameType ControlFrameType) (controlFrame, os.Error) {
|
func newControlFrame(frameType ControlFrameType) (controlFrame, os.Error) {
|
||||||
ctor, ok := cframeCtor[frameType]
|
ctor, ok := cframeCtor[frameType]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, InvalidControlFrame
|
return nil, &Error{Err: InvalidControlFrame}
|
||||||
}
|
}
|
||||||
return ctor(), nil
|
return ctor(), nil
|
||||||
}
|
}
|
||||||
@ -97,30 +97,12 @@ var cframeCtor = map[ControlFrameType]func() controlFrame{
|
|||||||
// TODO(willchan): Add TypeWindowUpdate
|
// TODO(willchan): Add TypeWindowUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
type corkedReader struct {
|
func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) os.Error {
|
||||||
r io.Reader
|
|
||||||
ch chan int
|
|
||||||
n int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr *corkedReader) Read(p []byte) (int, os.Error) {
|
|
||||||
if cr.n == 0 {
|
|
||||||
cr.n = <-cr.ch
|
|
||||||
}
|
|
||||||
if len(p) > cr.n {
|
|
||||||
p = p[:cr.n]
|
|
||||||
}
|
|
||||||
n, err := cr.r.Read(p)
|
|
||||||
cr.n -= n
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Framer) uncorkHeaderDecompressor(payloadSize int) os.Error {
|
|
||||||
if f.headerDecompressor != nil {
|
if f.headerDecompressor != nil {
|
||||||
f.headerReader.ch <- payloadSize
|
f.headerReader.N = payloadSize
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
f.headerReader = corkedReader{r: f.r, ch: make(chan int, 1), n: payloadSize}
|
f.headerReader = io.LimitedReader{R: f.r, N: payloadSize}
|
||||||
decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(HeaderDictionary))
|
decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(HeaderDictionary))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -161,11 +143,12 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (
|
|||||||
return cframe, nil
|
return cframe, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHeaderValueBlock(r io.Reader) (http.Header, os.Error) {
|
func parseHeaderValueBlock(r io.Reader, streamId uint32) (http.Header, os.Error) {
|
||||||
var numHeaders uint16
|
var numHeaders uint16
|
||||||
if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil {
|
if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var e os.Error
|
||||||
h := make(http.Header, int(numHeaders))
|
h := make(http.Header, int(numHeaders))
|
||||||
for i := 0; i < int(numHeaders); i++ {
|
for i := 0; i < int(numHeaders); i++ {
|
||||||
var length uint16
|
var length uint16
|
||||||
@ -178,10 +161,11 @@ func parseHeaderValueBlock(r io.Reader) (http.Header, os.Error) {
|
|||||||
}
|
}
|
||||||
name := string(nameBytes)
|
name := string(nameBytes)
|
||||||
if name != strings.ToLower(name) {
|
if name != strings.ToLower(name) {
|
||||||
return nil, UnlowercasedHeaderName
|
e = &Error{UnlowercasedHeaderName, streamId}
|
||||||
|
name = strings.ToLower(name)
|
||||||
}
|
}
|
||||||
if h[name] != nil {
|
if h[name] != nil {
|
||||||
return nil, DuplicateHeaders
|
e = &Error{DuplicateHeaders, streamId}
|
||||||
}
|
}
|
||||||
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
|
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -195,6 +179,9 @@ func parseHeaderValueBlock(r io.Reader) (http.Header, os.Error) {
|
|||||||
h.Add(name, v)
|
h.Add(name, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if e != nil {
|
||||||
|
return h, e
|
||||||
|
}
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,14 +201,25 @@ func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame)
|
|||||||
|
|
||||||
reader := f.r
|
reader := f.r
|
||||||
if !f.headerCompressionDisabled {
|
if !f.headerCompressionDisabled {
|
||||||
f.uncorkHeaderDecompressor(int(h.length - 10))
|
f.uncorkHeaderDecompressor(int64(h.length - 10))
|
||||||
reader = f.headerDecompressor
|
reader = f.headerDecompressor
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.Headers, err = parseHeaderValueBlock(reader)
|
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
|
||||||
|
if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) {
|
||||||
|
err = &Error{WrongCompressedPayloadSize, 0}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Remove this condition when we bump Version to 3.
|
||||||
|
if Version >= 3 {
|
||||||
|
for h, _ := range frame.Headers {
|
||||||
|
if invalidReqHeaders[h] {
|
||||||
|
return &Error{InvalidHeaderPresent, frame.StreamId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,13 +235,24 @@ func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) o
|
|||||||
}
|
}
|
||||||
reader := f.r
|
reader := f.r
|
||||||
if !f.headerCompressionDisabled {
|
if !f.headerCompressionDisabled {
|
||||||
f.uncorkHeaderDecompressor(int(h.length - 6))
|
f.uncorkHeaderDecompressor(int64(h.length - 6))
|
||||||
reader = f.headerDecompressor
|
reader = f.headerDecompressor
|
||||||
}
|
}
|
||||||
frame.Headers, err = parseHeaderValueBlock(reader)
|
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
|
||||||
|
if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) {
|
||||||
|
err = &Error{WrongCompressedPayloadSize, 0}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Remove this condition when we bump Version to 3.
|
||||||
|
if Version >= 3 {
|
||||||
|
for h, _ := range frame.Headers {
|
||||||
|
if invalidRespHeaders[h] {
|
||||||
|
return &Error{InvalidHeaderPresent, frame.StreamId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,13 +268,31 @@ func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) os.
|
|||||||
}
|
}
|
||||||
reader := f.r
|
reader := f.r
|
||||||
if !f.headerCompressionDisabled {
|
if !f.headerCompressionDisabled {
|
||||||
f.uncorkHeaderDecompressor(int(h.length - 6))
|
f.uncorkHeaderDecompressor(int64(h.length - 6))
|
||||||
reader = f.headerDecompressor
|
reader = f.headerDecompressor
|
||||||
}
|
}
|
||||||
frame.Headers, err = parseHeaderValueBlock(reader)
|
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
|
||||||
|
if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) {
|
||||||
|
err = &Error{WrongCompressedPayloadSize, 0}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove this condition when we bump Version to 3.
|
||||||
|
if Version >= 3 {
|
||||||
|
var invalidHeaders map[string]bool
|
||||||
|
if frame.StreamId%2 == 0 {
|
||||||
|
invalidHeaders = invalidReqHeaders
|
||||||
|
} else {
|
||||||
|
invalidHeaders = invalidRespHeaders
|
||||||
|
}
|
||||||
|
for h, _ := range frame.Headers {
|
||||||
|
if invalidHeaders[h] {
|
||||||
|
return &Error{InvalidHeaderPresent, frame.StreamId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +306,6 @@ func (f *Framer) parseDataFrame(streamId uint32) (*DataFrame, os.Error) {
|
|||||||
frame.Flags = DataFlags(length >> 24)
|
frame.Flags = DataFlags(length >> 24)
|
||||||
length &= 0xffffff
|
length &= 0xffffff
|
||||||
frame.Data = make([]byte, length)
|
frame.Data = make([]byte, length)
|
||||||
// TODO(willchan): Support compressed data frames.
|
|
||||||
if _, err := io.ReadFull(f.r, frame.Data); err != nil {
|
if _, err := io.ReadFull(f.r, frame.Data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ func TestHeaderParsing(t *testing.T) {
|
|||||||
var headerValueBlockBuf bytes.Buffer
|
var headerValueBlockBuf bytes.Buffer
|
||||||
writeHeaderValueBlock(&headerValueBlockBuf, headers)
|
writeHeaderValueBlock(&headerValueBlockBuf, headers)
|
||||||
|
|
||||||
newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf)
|
const bogusStreamId = 1
|
||||||
|
newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("parseHeaderValueBlock:", err)
|
t.Fatal("parseHeaderValueBlock:", err)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"http"
|
"http"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Data Frame Format
|
// Data Frame Format
|
||||||
@ -302,33 +301,41 @@ const HeaderDictionary = "optionsgetheadpostputdeletetrace" +
|
|||||||
"chunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/plainpublicmax-age" +
|
"chunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/plainpublicmax-age" +
|
||||||
"charset=iso-8859-1utf-8gzipdeflateHTTP/1.1statusversionurl\x00"
|
"charset=iso-8859-1utf-8gzipdeflateHTTP/1.1statusversionurl\x00"
|
||||||
|
|
||||||
type FramerError int
|
// A SPDY specific error.
|
||||||
|
type ErrorCode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Internal FramerError = iota
|
UnlowercasedHeaderName ErrorCode = "header was not lowercased"
|
||||||
InvalidControlFrame
|
DuplicateHeaders ErrorCode = "multiple headers with same name"
|
||||||
UnlowercasedHeaderName
|
WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect"
|
||||||
DuplicateHeaders
|
UnknownFrameType ErrorCode = "unknown frame type"
|
||||||
UnknownFrameType
|
InvalidControlFrame ErrorCode = "invalid control frame"
|
||||||
InvalidDataFrame
|
InvalidDataFrame ErrorCode = "invalid data frame"
|
||||||
|
InvalidHeaderPresent ErrorCode = "frame contained invalid header"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e FramerError) String() string {
|
// Error contains both the type of error and additional values. StreamId is 0
|
||||||
switch e {
|
// if Error is not associated with a stream.
|
||||||
case Internal:
|
type Error struct {
|
||||||
return "Internal"
|
Err ErrorCode
|
||||||
case InvalidControlFrame:
|
StreamId uint32
|
||||||
return "InvalidControlFrame"
|
|
||||||
case UnlowercasedHeaderName:
|
|
||||||
return "UnlowercasedHeaderName"
|
|
||||||
case DuplicateHeaders:
|
|
||||||
return "DuplicateHeaders"
|
|
||||||
case UnknownFrameType:
|
|
||||||
return "UnknownFrameType"
|
|
||||||
case InvalidDataFrame:
|
|
||||||
return "InvalidDataFrame"
|
|
||||||
}
|
}
|
||||||
return "Error(" + strconv.Itoa(int(e)) + ")"
|
|
||||||
|
func (e *Error) String() string {
|
||||||
|
return string(e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidReqHeaders = map[string]bool{
|
||||||
|
"Connection": true,
|
||||||
|
"Keep-Alive": true,
|
||||||
|
"Proxy-Connection": true,
|
||||||
|
"Transfer-Encoding": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidRespHeaders = map[string]bool{
|
||||||
|
"Connection": true,
|
||||||
|
"Keep-Alive": true,
|
||||||
|
"Transfer-Encoding": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Framer handles serializing/deserializing SPDY frames, including compressing/
|
// Framer handles serializing/deserializing SPDY frames, including compressing/
|
||||||
@ -339,7 +346,7 @@ type Framer struct {
|
|||||||
headerBuf *bytes.Buffer
|
headerBuf *bytes.Buffer
|
||||||
headerCompressor *zlib.Writer
|
headerCompressor *zlib.Writer
|
||||||
r io.Reader
|
r io.Reader
|
||||||
headerReader corkedReader
|
headerReader io.LimitedReader
|
||||||
headerDecompressor io.ReadCloser
|
headerDecompressor io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,10 +267,9 @@ func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err os.Error) {
|
|||||||
func (f *Framer) writeDataFrame(frame *DataFrame) (err os.Error) {
|
func (f *Framer) writeDataFrame(frame *DataFrame) (err os.Error) {
|
||||||
// Validate DataFrame
|
// Validate DataFrame
|
||||||
if frame.StreamId&0x80000000 != 0 || len(frame.Data) >= 0x0f000000 {
|
if frame.StreamId&0x80000000 != 0 || len(frame.Data) >= 0x0f000000 {
|
||||||
return InvalidDataFrame
|
return &Error{InvalidDataFrame, frame.StreamId}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(willchan): Support data compression.
|
|
||||||
// Serialize frame to Writer
|
// Serialize frame to Writer
|
||||||
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user