github.com/alejandroesc/spdy@v0.0.0-20200317064415-01a02f0eb389/spdy3/spdy_api.go (about)

     1  package spdy3
     2  
     3  import (
     4  	"errors"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  
     9  	"github.com/SlyMarbo/spdy/common"
    10  	"github.com/SlyMarbo/spdy/spdy3/frames"
    11  )
    12  
    13  // Ping is used by spdy.PingServer and spdy.PingClient to send
    14  // SPDY PINGs.
    15  func (c *Conn) Ping() (<-chan bool, error) {
    16  	if c.Closed() {
    17  		return nil, errors.New("Error: Conn has been closed.")
    18  	}
    19  
    20  	ping := new(frames.PING)
    21  	c.nextPingIDLock.Lock()
    22  	pid := c.nextPingID
    23  	if pid+2 < pid {
    24  		if pid&1 == 0 {
    25  			c.nextPingID = 2
    26  		} else {
    27  			c.nextPingID = 1
    28  		}
    29  	} else {
    30  		c.nextPingID += 2
    31  	}
    32  	c.nextPingIDLock.Unlock()
    33  
    34  	ping.PingID = pid
    35  	c.output[0] <- ping
    36  	ch := make(chan bool, 1)
    37  	c.pingsLock.Lock()
    38  	c.pings[pid] = ch
    39  	c.pingsLock.Unlock()
    40  
    41  	return ch, nil
    42  }
    43  
    44  // Push is used to issue a server push to the client. Note that this cannot be performed
    45  // by clients.
    46  func (c *Conn) Push(resource string, origin common.Stream) (common.PushStream, error) {
    47  	c.goawayLock.Lock()
    48  	goaway := c.goawayReceived || c.goawaySent
    49  	c.goawayLock.Unlock()
    50  	if goaway {
    51  		return nil, common.ErrGoaway
    52  	}
    53  
    54  	if c.server == nil {
    55  		return nil, errors.New("Error: Only servers can send pushes.")
    56  	}
    57  
    58  	// Parse and check URL.
    59  	url, err := url.Parse(resource)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	if url.Scheme == "" || url.Host == "" {
    64  		return nil, errors.New("Error: Incomplete path provided to resource.")
    65  	}
    66  	resource = url.String()
    67  
    68  	// Ensure the resource hasn't been pushed on the given stream already.
    69  	if c.pushedResources[origin] == nil {
    70  		c.pushedResources[origin] = map[string]struct{}{
    71  			resource: struct{}{},
    72  		}
    73  	} else if _, ok := c.pushedResources[origin][url.String()]; !ok {
    74  		c.pushedResources[origin][resource] = struct{}{}
    75  	} else {
    76  		return nil, errors.New("Error: Resource already pushed to this stream.")
    77  	}
    78  
    79  	// Check stream limit would allow the new stream.
    80  	if !c.pushStreamLimit.Add() {
    81  		return nil, errors.New("Error: Max concurrent streams limit exceeded.")
    82  	}
    83  
    84  	// Verify that path is prefixed with / as required by spec.
    85  	path := url.Path
    86  	if !strings.HasPrefix(path, "/") {
    87  		path = "/" + path
    88  	}
    89  
    90  	// Prepare the SYN_STREAM.
    91  	push := new(frames.SYN_STREAM)
    92  	push.Flags = common.FLAG_UNIDIRECTIONAL
    93  	push.AssocStreamID = origin.StreamID()
    94  	push.Priority = 7
    95  	push.Header = make(http.Header)
    96  	push.Header.Set(":scheme", url.Scheme)
    97  	push.Header.Set(":host", url.Host)
    98  	push.Header.Set(":path", path)
    99  	push.Header.Set(":version", "HTTP/1.1")
   100  
   101  	// Send.
   102  	c.streamCreation.Lock()
   103  	defer c.streamCreation.Unlock()
   104  
   105  	c.lastPushStreamIDLock.Lock()
   106  	c.lastPushStreamID += 2
   107  	newID := c.lastPushStreamID
   108  	c.lastPushStreamIDLock.Unlock()
   109  	if newID > common.MAX_STREAM_ID {
   110  		return nil, errors.New("Error: All server streams exhausted.")
   111  	}
   112  	push.StreamID = newID
   113  	c.output[0] <- push
   114  
   115  	// Create the pushStream.
   116  	out := NewPushStream(c, newID, origin, c.output[7])
   117  	out.AddFlowControl(c.flowControl)
   118  
   119  	// Store in the connection map.
   120  	c.streamsLock.Lock()
   121  	c.streams[newID] = out
   122  	c.streamsLock.Unlock()
   123  
   124  	return out, nil
   125  }
   126  
   127  func (c *Conn) SetFlowControl(f common.FlowControl) {
   128  	c.flowControlLock.Lock()
   129  	c.flowControl = f
   130  	c.flowControlLock.Unlock()
   131  }