// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package spdy is an incomplete implementation of the SPDY protocol. // // The implementation follows draft 2 of the spec: // https://sites.google.com/a/chromium.org/dev/spdy/spdy-protocol/spdy-protocol-draft2 package spdy import ( "bytes" "compress/zlib" "encoding/binary" "http" "io" "os" "strconv" "strings" "sync" ) // Version is the protocol version number that this package implements. const Version = 2 // ControlFrameType stores the type field in a control frame header. type ControlFrameType uint16 // Control frame type constants const ( TypeSynStream ControlFrameType = 0x0001 TypeSynReply = 0x0002 TypeRstStream = 0x0003 TypeSettings = 0x0004 TypeNoop = 0x0005 TypePing = 0x0006 TypeGoaway = 0x0007 TypeHeaders = 0x0008 TypeWindowUpdate = 0x0009 ) func (t ControlFrameType) String() string { switch t { case TypeSynStream: return "SYN_STREAM" case TypeSynReply: return "SYN_REPLY" case TypeRstStream: return "RST_STREAM" case TypeSettings: return "SETTINGS" case TypeNoop: return "NOOP" case TypePing: return "PING" case TypeGoaway: return "GOAWAY" case TypeHeaders: return "HEADERS" case TypeWindowUpdate: return "WINDOW_UPDATE" } return "Type(" + strconv.Itoa(int(t)) + ")" } type FrameFlags uint8 // Stream frame flags const ( FlagFin FrameFlags = 0x01 FlagUnidirectional = 0x02 ) // SETTINGS frame flags const ( FlagClearPreviouslyPersistedSettings FrameFlags = 0x01 ) // MaxDataLength is the maximum number of bytes that can be stored in one frame. const MaxDataLength = 1<<24 - 1 // A Frame is a framed message as sent between clients and servers. // There are two types of frames: control frames and data frames. type Frame struct { Header [4]byte Flags FrameFlags Data []byte } // ControlFrame creates a control frame with the given information. func ControlFrame(t ControlFrameType, f FrameFlags, data []byte) Frame { return Frame{ Header: [4]byte{ (Version&0xff00)>>8 | 0x80, (Version & 0x00ff), byte((t & 0xff00) >> 8), byte((t & 0x00ff) >> 0), }, Flags: f, Data: data, } } // DataFrame creates a data frame with the given information. func DataFrame(streamId uint32, f FrameFlags, data []byte) Frame { return Frame{ Header: [4]byte{ byte(streamId & 0x7f000000 >> 24), byte(streamId & 0x00ff0000 >> 16), byte(streamId & 0x0000ff00 >> 8), byte(streamId & 0x000000ff >> 0), }, Flags: f, Data: data, } } // ReadFrame reads an entire frame into memory. func ReadFrame(r io.Reader) (f Frame, err os.Error) { _, err = io.ReadFull(r, f.Header[:]) if err != nil { return } err = binary.Read(r, binary.BigEndian, &f.Flags) if err != nil { return } var lengthField [3]byte _, err = io.ReadFull(r, lengthField[:]) if err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF } return } var length uint32 length |= uint32(lengthField[0]) << 16 length |= uint32(lengthField[1]) << 8 length |= uint32(lengthField[2]) << 0 if length > 0 { f.Data = make([]byte, int(length)) _, err = io.ReadFull(r, f.Data) if err == os.EOF { err = io.ErrUnexpectedEOF } } else { f.Data = []byte{} } return } // IsControl returns whether the frame holds a control frame. func (f Frame) IsControl() bool { return f.Header[0]&0x80 != 0 } // Type obtains the type field if the frame is a control frame, otherwise it returns zero. func (f Frame) Type() ControlFrameType { if !f.IsControl() { return 0 } return (ControlFrameType(f.Header[2])<<8 | ControlFrameType(f.Header[3])) } // StreamId returns the stream ID field if the frame is a data frame, otherwise it returns zero. func (f Frame) StreamId() (id uint32) { if f.IsControl() { return 0 } id |= uint32(f.Header[0]) << 24 id |= uint32(f.Header[1]) << 16 id |= uint32(f.Header[2]) << 8 id |= uint32(f.Header[3]) << 0 return } // WriteTo writes the frame in the SPDY format. func (f Frame) WriteTo(w io.Writer) (n int64, err os.Error) { var nn int // Header nn, err = w.Write(f.Header[:]) n += int64(nn) if err != nil { return } // Flags nn, err = w.Write([]byte{byte(f.Flags)}) n += int64(nn) if err != nil { return } // Length nn, err = w.Write([]byte{ byte(len(f.Data) & 0x00ff0000 >> 16), byte(len(f.Data) & 0x0000ff00 >> 8), byte(len(f.Data) & 0x000000ff), }) n += int64(nn) if err != nil { return } // Data if len(f.Data) > 0 { nn, err = w.Write(f.Data) n += int64(nn) } return } // headerDictionary is the dictionary sent to the zlib compressor/decompressor. // Even though the specification states there is no null byte at the end, Chrome sends it. const headerDictionary = "optionsgetheadpostputdeletetrace" + "acceptaccept-charsetaccept-encodingaccept-languageauthorizationexpectfromhost" + "if-modified-sinceif-matchif-none-matchif-rangeif-unmodifiedsince" + "max-forwardsproxy-authorizationrangerefererteuser-agent" + "100101200201202203204205206300301302303304305306307400401402403404405406407408409410411412413414415416417500501502503504505" + "accept-rangesageetaglocationproxy-authenticatepublicretry-after" + "servervarywarningwww-authenticateallowcontent-basecontent-encodingcache-control" + "connectiondatetrailertransfer-encodingupgradeviawarning" + "content-languagecontent-lengthcontent-locationcontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookie" + "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" + "JanFebMarAprMayJunJulAugSepOctNovDec" + "chunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/plainpublicmax-age" + "charset=iso-8859-1utf-8gzipdeflateHTTP/1.1statusversionurl\x00" // hrSource is a reader that passes through reads from another reader. // When the underlying reader reaches EOF, Read will block until another reader is added via change. type hrSource struct { r io.Reader m sync.RWMutex c *sync.Cond } func (src *hrSource) Read(p []byte) (n int, err os.Error) { src.m.RLock() for src.r == nil { src.c.Wait() } n, err = src.r.Read(p) src.m.RUnlock() if err == os.EOF { src.change(nil) err = nil } return } func (src *hrSource) change(r io.Reader) { src.m.Lock() defer src.m.Unlock() src.r = r src.c.Broadcast() } // A HeaderReader reads zlib-compressed headers. type HeaderReader struct { source hrSource decompressor io.ReadCloser } // NewHeaderReader creates a HeaderReader with the initial dictionary. func NewHeaderReader() (hr *HeaderReader) { hr = new(HeaderReader) hr.source.c = sync.NewCond(hr.source.m.RLocker()) return } // ReadHeader reads a set of headers from a reader. func (hr *HeaderReader) ReadHeader(r io.Reader) (h http.Header, err os.Error) { hr.source.change(r) h, err = hr.read() return } // Decode reads a set of headers from a block of bytes. func (hr *HeaderReader) Decode(data []byte) (h http.Header, err os.Error) { hr.source.change(bytes.NewBuffer(data)) h, err = hr.read() return } func (hr *HeaderReader) read() (h http.Header, err os.Error) { var count uint16 if hr.decompressor == nil { hr.decompressor, err = zlib.NewReaderDict(&hr.source, []byte(headerDictionary)) if err != nil { return } } err = binary.Read(hr.decompressor, binary.BigEndian, &count) if err != nil { return } h = make(http.Header, int(count)) for i := 0; i < int(count); i++ { var name, value string name, err = readHeaderString(hr.decompressor) if err != nil { return } value, err = readHeaderString(hr.decompressor) if err != nil { return } valueList := strings.Split(string(value), "\x00", -1) for _, v := range valueList { h.Add(name, v) } } return } func readHeaderString(r io.Reader) (s string, err os.Error) { var length uint16 err = binary.Read(r, binary.BigEndian, &length) if err != nil { return } data := make([]byte, int(length)) _, err = io.ReadFull(r, data) if err != nil { return } return string(data), nil } // HeaderWriter will write zlib-compressed headers on different streams. type HeaderWriter struct { compressor *zlib.Writer buffer *bytes.Buffer } // NewHeaderWriter creates a HeaderWriter ready to compress headers. func NewHeaderWriter(level int) (hw *HeaderWriter) { hw = &HeaderWriter{buffer: new(bytes.Buffer)} hw.compressor, _ = zlib.NewWriterDict(hw.buffer, level, []byte(headerDictionary)) return } // WriteHeader writes a header block directly to an output. func (hw *HeaderWriter) WriteHeader(w io.Writer, h http.Header) (err os.Error) { hw.write(h) _, err = io.Copy(w, hw.buffer) hw.buffer.Reset() return } // Encode returns a compressed header block. func (hw *HeaderWriter) Encode(h http.Header) (data []byte) { hw.write(h) data = make([]byte, hw.buffer.Len()) hw.buffer.Read(data) return } func (hw *HeaderWriter) write(h http.Header) { binary.Write(hw.compressor, binary.BigEndian, uint16(len(h))) for k, vals := range h { k = strings.ToLower(k) binary.Write(hw.compressor, binary.BigEndian, uint16(len(k))) binary.Write(hw.compressor, binary.BigEndian, []byte(k)) v := strings.Join(vals, "\x00") binary.Write(hw.compressor, binary.BigEndian, uint16(len(v))) binary.Write(hw.compressor, binary.BigEndian, []byte(v)) } hw.compressor.Flush() }