github.com/alejandroesc/spdy@v0.0.0-20200317064415-01a02f0eb389/spdy3/requests.go (about) 1 package spdy3 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net/http" 8 "strings" 9 10 "github.com/SlyMarbo/spdy/common" 11 "github.com/SlyMarbo/spdy/spdy3/frames" 12 ) 13 14 // Request is used to make a client request. 15 func (c *Conn) Request(request *http.Request, receiver common.Receiver, priority common.Priority) (common.Stream, error) { 16 if c.Closed() { 17 return nil, common.ErrConnClosed 18 } 19 c.goawayLock.Lock() 20 goaway := c.goawayReceived || c.goawaySent 21 c.goawayLock.Unlock() 22 if goaway { 23 return nil, common.ErrGoaway 24 } 25 26 if c.server != nil { 27 return nil, errors.New("Error: Only clients can send requests.") 28 } 29 30 // Check stream limit would allow the new stream. 31 if !c.requestStreamLimit.Add() { 32 return nil, errors.New("Error: Max concurrent streams limit exceeded.") 33 } 34 35 if !priority.Valid(3) { 36 return nil, errors.New("Error: Priority must be in the range 0 - 7.") 37 } 38 39 url := request.URL 40 if url == nil || url.Scheme == "" || url.Host == "" { 41 return nil, errors.New("Error: Incomplete path provided to resource.") 42 } 43 44 // Prepare the SYN_STREAM. 45 path := url.Path 46 if url.RawQuery != "" { 47 path += "?" + url.RawQuery 48 } 49 if url.Fragment != "" { 50 path += "#" + url.Fragment 51 } 52 if !strings.HasPrefix(path, "/") { 53 path = "/" + path 54 } 55 56 host := url.Host 57 if len(request.Host) > 0 { 58 host = request.Host 59 } 60 syn := new(frames.SYN_STREAM) 61 syn.Priority = priority 62 syn.Header = request.Header 63 syn.Header.Set(":method", request.Method) 64 syn.Header.Set(":path", path) 65 syn.Header.Set(":version", "HTTP/1.1") 66 syn.Header.Set(":host", host) 67 syn.Header.Set(":scheme", url.Scheme) 68 69 // Prepare the request body, if any. 70 body := make([]*frames.DATA, 0, 1) 71 if request.Body != nil { 72 buf := make([]byte, 32*1024) 73 n, err := request.Body.Read(buf) 74 if err != nil && err != io.EOF { 75 return nil, err 76 } 77 total := n 78 for n > 0 { 79 data := new(frames.DATA) 80 data.Data = make([]byte, n) 81 copy(data.Data, buf[:n]) 82 body = append(body, data) 83 n, err = request.Body.Read(buf) 84 if err != nil && err != io.EOF { 85 return nil, err 86 } 87 total += n 88 } 89 90 // Half-close the stream. 91 if len(body) == 0 { 92 syn.Flags = common.FLAG_FIN 93 } else { 94 syn.Header.Set("Content-Length", fmt.Sprint(total)) 95 body[len(body)-1].Flags = common.FLAG_FIN 96 } 97 request.Body.Close() 98 } else { 99 syn.Flags = common.FLAG_FIN 100 } 101 102 // Send. 103 c.streamCreation.Lock() 104 defer c.streamCreation.Unlock() 105 106 c.lastRequestStreamIDLock.Lock() 107 if c.lastRequestStreamID == 0 { 108 c.lastRequestStreamID = 1 109 } else { 110 c.lastRequestStreamID += 2 111 } 112 syn.StreamID = c.lastRequestStreamID 113 c.lastRequestStreamIDLock.Unlock() 114 if syn.StreamID > common.MAX_STREAM_ID { 115 return nil, errors.New("Error: All client streams exhausted.") 116 } 117 c.output[0] <- syn 118 for _, frame := range body { 119 frame.StreamID = syn.StreamID 120 c.output[0] <- frame 121 } 122 123 // Create the request stream. 124 out := NewRequestStream(c, syn.StreamID, c.output[0]) 125 out.Request = request 126 out.Receiver = receiver 127 out.AddFlowControl(c.flowControl) 128 c.streamsLock.Lock() 129 c.streams[syn.StreamID] = out // Store in the connection map. 130 c.streamsLock.Unlock() 131 132 return out, nil 133 } 134 135 func (c *Conn) RequestResponse(request *http.Request, receiver common.Receiver, priority common.Priority) (*http.Response, error) { 136 res := common.NewResponse(request, receiver) 137 138 // Send the request. 139 stream, err := c.Request(request, res, priority) 140 if err != nil { 141 return nil, err 142 } 143 144 // Let the request run its course. 145 stream.Run() 146 147 return res.Response(), c.shutdownError 148 }