blob: ad9aa6333559c2ddb6a084c82942f45864f9a3ce [file] [log] [blame]
// 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()
}