github.com/simonmittag/ws@v1.1.0-rc.5.0.20210419231947-82b846128245/server.go (about) 1 package ws 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "net" 9 "net/http" 10 "strings" 11 "time" 12 13 "github.com/gobwas/httphead" 14 "github.com/gobwas/pool/pbufio" 15 ) 16 17 // Constants used by ConnUpgrader. 18 const ( 19 DefaultServerReadBufferSize = 4096 20 DefaultServerWriteBufferSize = 512 21 ) 22 23 // Errors used by both client and server when preparing WebSocket handshake. 24 var ( 25 ErrHandshakeBadProtocol = RejectConnectionError( 26 RejectionStatus(http.StatusHTTPVersionNotSupported), 27 RejectionReason(fmt.Sprintf("handshake error: bad HTTP protocol version")), 28 ) 29 ErrHandshakeBadMethod = RejectConnectionError( 30 RejectionStatus(http.StatusMethodNotAllowed), 31 RejectionReason(fmt.Sprintf("handshake error: bad HTTP request method")), 32 ) 33 ErrHandshakeBadHost = RejectConnectionError( 34 RejectionStatus(http.StatusBadRequest), 35 RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerHost)), 36 ) 37 ErrHandshakeBadUpgrade = RejectConnectionError( 38 RejectionStatus(http.StatusBadRequest), 39 RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerUpgrade)), 40 ) 41 ErrHandshakeBadConnection = RejectConnectionError( 42 RejectionStatus(http.StatusBadRequest), 43 RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerConnection)), 44 ) 45 ErrHandshakeBadSecAccept = RejectConnectionError( 46 RejectionStatus(http.StatusBadRequest), 47 RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecAccept)), 48 ) 49 ErrHandshakeBadSecKey = RejectConnectionError( 50 RejectionStatus(http.StatusBadRequest), 51 RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecKey)), 52 ) 53 ErrHandshakeBadSecVersion = RejectConnectionError( 54 RejectionStatus(http.StatusBadRequest), 55 RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)), 56 ) 57 ) 58 59 // ErrMalformedResponse is returned by Dialer to indicate that server response 60 // can not be parsed. 61 var ErrMalformedResponse = fmt.Errorf("malformed HTTP response") 62 63 // ErrMalformedRequest is returned when HTTP request can not be parsed. 64 var ErrMalformedRequest = RejectConnectionError( 65 RejectionStatus(http.StatusBadRequest), 66 RejectionReason("malformed HTTP request"), 67 ) 68 69 // ErrHandshakeUpgradeRequired is returned by Upgrader to indicate that 70 // connection is rejected because given WebSocket version is malformed. 71 // 72 // According to RFC6455: 73 // If this version does not match a version understood by the server, the 74 // server MUST abort the WebSocket handshake described in this section and 75 // instead send an appropriate HTTP error code (such as 426 Upgrade Required) 76 // and a |Sec-WebSocket-Version| header field indicating the version(s) the 77 // server is capable of understanding. 78 var ErrHandshakeUpgradeRequired = RejectConnectionError( 79 RejectionStatus(http.StatusUpgradeRequired), 80 RejectionHeader(HandshakeHeaderString(headerSecVersion+": 13\r\n")), 81 RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)), 82 ) 83 84 // ErrNotHijacker is an error returned when http.ResponseWriter does not 85 // implement http.Hijacker interface. 86 var ErrNotHijacker = RejectConnectionError( 87 RejectionStatus(http.StatusInternalServerError), 88 RejectionReason("given http.ResponseWriter is not a http.Hijacker"), 89 ) 90 91 // DefaultHTTPUpgrader is an HTTPUpgrader that holds no options and is used by 92 // UpgradeHTTP function. 93 var DefaultHTTPUpgrader HTTPUpgrader 94 95 // UpgradeHTTP is like HTTPUpgrader{}.Upgrade(). 96 func UpgradeHTTP(r *http.Request, w http.ResponseWriter) (net.Conn, *bufio.ReadWriter, Handshake, error) { 97 return DefaultHTTPUpgrader.Upgrade(r, w) 98 } 99 100 // DefaultUpgrader is an Upgrader that holds no options and is used by Upgrade 101 // function. 102 var DefaultUpgrader Upgrader 103 104 // Upgrade is like Upgrader{}.Upgrade(). 105 func Upgrade(conn io.ReadWriter) (Handshake, error) { 106 return DefaultUpgrader.Upgrade(conn) 107 } 108 109 // HTTPUpgrader contains options for upgrading connection to websocket from 110 // net/http Handler arguments. 111 type HTTPUpgrader struct { 112 // Timeout is the maximum amount of time an Upgrade() will spent while 113 // writing handshake response. 114 // 115 // The default is no timeout. 116 Timeout time.Duration 117 118 // Header is an optional http.Header mapping that could be used to 119 // write additional headers to the handshake response. 120 // 121 // Note that if present, it will be written in any result of handshake. 122 Header http.Header 123 124 // Protocol is the select function that is used to select subprotocol from 125 // list requested by client. If this field is set, then the first matched 126 // protocol is sent to a client as negotiated. 127 Protocol func(string) bool 128 129 // Extension is the select function that is used to select extensions from 130 // list requested by client. If this field is set, then the all matched 131 // extensions are sent to a client as negotiated. 132 // 133 // DEPRECATED. Use Negotiate instead. 134 Extension func(httphead.Option) bool 135 136 // Negotiate is the callback that is used to negotiate extensions from 137 // the client's offer. If this field is set, then the returned non-zero 138 // extensions are sent to the client as accepted extensions in the 139 // response. 140 // 141 // The argument is only valid until the Negotiate callback returns. 142 // 143 // If returned error is non-nil then connection is rejected and response is 144 // sent with appropriate HTTP error code and body set to error message. 145 // 146 // RejectConnectionError could be used to get more control on response. 147 Negotiate func(httphead.Option) (httphead.Option, error) 148 } 149 150 // Upgrade upgrades http connection to the websocket connection. 151 // 152 // It hijacks net.Conn from w and returns received net.Conn and 153 // bufio.ReadWriter. On successful handshake it returns Handshake struct 154 // describing handshake info. 155 func (u HTTPUpgrader) Upgrade(r *http.Request, w http.ResponseWriter) (conn net.Conn, rw *bufio.ReadWriter, hs Handshake, err error) { 156 // Hijack connection first to get the ability to write rejection errors the 157 // same way as in Upgrader. 158 hj, ok := w.(http.Hijacker) 159 if ok { 160 conn, rw, err = hj.Hijack() 161 } else { 162 err = ErrNotHijacker 163 } 164 if err != nil { 165 httpError(w, err.Error(), http.StatusInternalServerError) 166 return 167 } 168 169 // See https://tools.ietf.org/html/rfc6455#section-4.1 170 // The method of the request MUST be GET, and the HTTP version MUST be at least 1.1. 171 var nonce string 172 if r.Method != http.MethodGet { 173 err = ErrHandshakeBadMethod 174 } else if r.ProtoMajor < 1 || (r.ProtoMajor == 1 && r.ProtoMinor < 1) { 175 err = ErrHandshakeBadProtocol 176 } else if r.Host == "" { 177 err = ErrHandshakeBadHost 178 } else if u := httpGetHeader(r.Header, headerUpgradeCanonical); u != "websocket" && !strings.EqualFold(u, "websocket") { 179 err = ErrHandshakeBadUpgrade 180 } else if c := httpGetHeader(r.Header, headerConnectionCanonical); c != "Upgrade" && !strHasToken(c, "upgrade") { 181 err = ErrHandshakeBadConnection 182 } else if nonce = httpGetHeader(r.Header, headerSecKeyCanonical); len(nonce) != nonceSize { 183 err = ErrHandshakeBadSecKey 184 } else if v := httpGetHeader(r.Header, headerSecVersionCanonical); v != "13" { 185 // According to RFC6455: 186 // 187 // If this version does not match a version understood by the server, 188 // the server MUST abort the WebSocket handshake described in this 189 // section and instead send an appropriate HTTP error code (such as 426 190 // Upgrade Required) and a |Sec-WebSocket-Version| header field 191 // indicating the version(s) the server is capable of understanding. 192 // 193 // So we branching here cause empty or not present version does not 194 // meet the ABNF rules of RFC6455: 195 // 196 // version = DIGIT | (NZDIGIT DIGIT) | 197 // ("1" DIGIT DIGIT) | ("2" DIGIT DIGIT) 198 // ; Limited to 0-255 range, with no leading zeros 199 // 200 // That is, if version is really invalid – we sent 426 status, if it 201 // not present or empty – it is 400. 202 if v != "" { 203 err = ErrHandshakeUpgradeRequired 204 } else { 205 err = ErrHandshakeBadSecVersion 206 } 207 } 208 if check := u.Protocol; err == nil && check != nil { 209 ps := r.Header[headerSecProtocolCanonical] 210 for i := 0; i < len(ps) && err == nil && hs.Protocol == ""; i++ { 211 var ok bool 212 hs.Protocol, ok = strSelectProtocol(ps[i], check) 213 if !ok { 214 err = ErrMalformedRequest 215 } 216 } 217 } 218 if f := u.Negotiate; err == nil && f != nil { 219 for _, h := range r.Header[headerSecExtensionsCanonical] { 220 hs.Extensions, err = negotiateExtensions(strToBytes(h), hs.Extensions, f) 221 if err != nil { 222 break 223 } 224 } 225 } 226 // DEPRECATED path. 227 if check := u.Extension; err == nil && check != nil && u.Negotiate == nil { 228 xs := r.Header[headerSecExtensionsCanonical] 229 for i := 0; i < len(xs) && err == nil; i++ { 230 var ok bool 231 hs.Extensions, ok = btsSelectExtensions(strToBytes(xs[i]), hs.Extensions, check) 232 if !ok { 233 err = ErrMalformedRequest 234 } 235 } 236 } 237 238 // Clear deadlines set by server. 239 conn.SetDeadline(noDeadline) 240 if t := u.Timeout; t != 0 { 241 conn.SetWriteDeadline(time.Now().Add(t)) 242 defer conn.SetWriteDeadline(noDeadline) 243 } 244 245 var header handshakeHeader 246 if h := u.Header; h != nil { 247 header[0] = HandshakeHeaderHTTP(h) 248 } 249 if err == nil { 250 httpWriteResponseUpgrade(rw.Writer, strToBytes(nonce), hs, header.WriteTo) 251 err = rw.Writer.Flush() 252 } else { 253 var code int 254 if rej, ok := err.(*RejectConnectionErrorType); ok { 255 code = rej.code 256 header[1] = rej.header 257 } 258 if code == 0 { 259 code = http.StatusInternalServerError 260 } 261 httpWriteResponseError(rw.Writer, err, code, header.WriteTo) 262 // Do not store Flush() error to not override already existing one. 263 rw.Writer.Flush() 264 } 265 return 266 } 267 268 // Upgrader contains options for upgrading connection to websocket. 269 type Upgrader struct { 270 // ReadBufferSize and WriteBufferSize is an I/O buffer sizes. 271 // They used to read and write http data while upgrading to WebSocket. 272 // Allocated buffers are pooled with sync.Pool to avoid extra allocations. 273 // 274 // If a size is zero then default value is used. 275 // 276 // Usually it is useful to set read buffer size bigger than write buffer 277 // size because incoming request could contain long header values, such as 278 // Cookie. Response, in other way, could be big only if user write multiple 279 // custom headers. Usually response takes less than 256 bytes. 280 ReadBufferSize, WriteBufferSize int 281 282 // Protocol is a select function that is used to select subprotocol 283 // from list requested by client. If this field is set, then the first matched 284 // protocol is sent to a client as negotiated. 285 // 286 // The argument is only valid until the callback returns. 287 Protocol func([]byte) bool 288 289 // ProtocolCustrom allow user to parse Sec-WebSocket-Protocol header manually. 290 // Note that returned bytes must be valid until Upgrade returns. 291 // If ProtocolCustom is set, it used instead of Protocol function. 292 ProtocolCustom func([]byte) (string, bool) 293 294 // Extension is a select function that is used to select extensions 295 // from list requested by client. If this field is set, then the all matched 296 // extensions are sent to a client as negotiated. 297 // 298 // Note that Extension may be called multiple times and implementations 299 // must track uniqueness of accepted extensions manually. 300 // 301 // The argument is only valid until the callback returns. 302 // 303 // According to the RFC6455 order of extensions passed by a client is 304 // significant. That is, returning true from this function means that no 305 // other extension with the same name should be checked because server 306 // accepted the most preferable extension right now: 307 // "Note that the order of extensions is significant. Any interactions between 308 // multiple extensions MAY be defined in the documents defining the extensions. 309 // In the absence of such definitions, the interpretation is that the header 310 // fields listed by the client in its request represent a preference of the 311 // header fields it wishes to use, with the first options listed being most 312 // preferable." 313 // 314 // DEPRECATED. Use Negotiate instead. 315 Extension func(httphead.Option) bool 316 317 // ExtensionCustom allow user to parse Sec-WebSocket-Extensions header 318 // manually. 319 // 320 // If ExtensionCustom() decides to accept received extension, it must 321 // append appropriate option to the given slice of httphead.Option. 322 // It returns results of append() to the given slice and a flag that 323 // reports whether given header value is wellformed or not. 324 // 325 // Note that ExtensionCustom may be called multiple times and 326 // implementations must track uniqueness of accepted extensions manually. 327 // 328 // Note that returned options should be valid until Upgrade returns. 329 // If ExtensionCustom is set, it used instead of Extension function. 330 ExtensionCustom func([]byte, []httphead.Option) ([]httphead.Option, bool) 331 332 // Negotiate is the callback that is used to negotiate extensions from 333 // the client's offer. If this field is set, then the returned non-zero 334 // extensions are sent to the client as accepted extensions in the 335 // response. 336 // 337 // The argument is only valid until the Negotiate callback returns. 338 // 339 // If returned error is non-nil then connection is rejected and response is 340 // sent with appropriate HTTP error code and body set to error message. 341 // 342 // RejectConnectionError could be used to get more control on response. 343 Negotiate func(httphead.Option) (httphead.Option, error) 344 345 // Header is an optional HandshakeHeader instance that could be used to 346 // write additional headers to the handshake response. 347 // 348 // It used instead of any key-value mappings to avoid allocations in user 349 // land. 350 // 351 // Note that if present, it will be written in any result of handshake. 352 Header HandshakeHeader 353 354 // OnRequest is a callback that will be called after request line 355 // successful parsing. 356 // 357 // The arguments are only valid until the callback returns. 358 // 359 // If returned error is non-nil then connection is rejected and response is 360 // sent with appropriate HTTP error code and body set to error message. 361 // 362 // RejectConnectionError could be used to get more control on response. 363 OnRequest func(uri []byte) error 364 365 // OnHost is a callback that will be called after "Host" header successful 366 // parsing. 367 // 368 // It is separated from OnHeader callback because the Host header must be 369 // present in each request since HTTP/1.1. Thus Host header is non-optional 370 // and required for every WebSocket handshake. 371 // 372 // The arguments are only valid until the callback returns. 373 // 374 // If returned error is non-nil then connection is rejected and response is 375 // sent with appropriate HTTP error code and body set to error message. 376 // 377 // RejectConnectionError could be used to get more control on response. 378 OnHost func(host []byte) error 379 380 // OnHeader is a callback that will be called after successful parsing of 381 // header, that is not used during WebSocket handshake procedure. That is, 382 // it will be called with non-websocket headers, which could be relevant 383 // for application-level logic. 384 // 385 // The arguments are only valid until the callback returns. 386 // 387 // If returned error is non-nil then connection is rejected and response is 388 // sent with appropriate HTTP error code and body set to error message. 389 // 390 // RejectConnectionError could be used to get more control on response. 391 OnHeader func(key, value []byte) error 392 393 // OnBeforeUpgrade is a callback that will be called before sending 394 // successful upgrade response. 395 // 396 // Setting OnBeforeUpgrade allows user to make final application-level 397 // checks and decide whether this connection is allowed to successfully 398 // upgrade to WebSocket. 399 // 400 // It must return non-nil either HandshakeHeader or error and never both. 401 // 402 // If returned error is non-nil then connection is rejected and response is 403 // sent with appropriate HTTP error code and body set to error message. 404 // 405 // RejectConnectionError could be used to get more control on response. 406 OnBeforeUpgrade func() (header HandshakeHeader, err error) 407 } 408 409 // Upgrade zero-copy upgrades connection to WebSocket. It interprets given conn 410 // as connection with incoming HTTP Upgrade request. 411 // 412 // It is a caller responsibility to manage i/o timeouts on conn. 413 // 414 // Non-nil error means that request for the WebSocket upgrade is invalid or 415 // malformed and usually connection should be closed. 416 // Even when error is non-nil Upgrade will write appropriate response into 417 // connection in compliance with RFC. 418 func (u Upgrader) Upgrade(conn io.ReadWriter) (hs Handshake, err error) { 419 // headerSeen constants helps to report whether or not some header was seen 420 // during reading request bytes. 421 const ( 422 headerSeenHost = 1 << iota 423 headerSeenUpgrade 424 headerSeenConnection 425 headerSeenSecVersion 426 headerSeenSecKey 427 428 // headerSeenAll is the value that we expect to receive at the end of 429 // headers read/parse loop. 430 headerSeenAll = 0 | 431 headerSeenHost | 432 headerSeenUpgrade | 433 headerSeenConnection | 434 headerSeenSecVersion | 435 headerSeenSecKey 436 ) 437 438 // Prepare I/O buffers. 439 // TODO(gobwas): make it configurable. 440 br := pbufio.GetReader(conn, 441 nonZero(u.ReadBufferSize, DefaultServerReadBufferSize), 442 ) 443 bw := pbufio.GetWriter(conn, 444 nonZero(u.WriteBufferSize, DefaultServerWriteBufferSize), 445 ) 446 defer func() { 447 pbufio.PutReader(br) 448 pbufio.PutWriter(bw) 449 }() 450 451 // Read HTTP request line like "GET /ws HTTP/1.1". 452 rl, err := readLine(br) 453 if err != nil { 454 return 455 } 456 // Parse request line data like HTTP version, uri and method. 457 req, err := httpParseRequestLine(rl) 458 if err != nil { 459 return 460 } 461 462 // Prepare stack-based handshake header list. 463 header := handshakeHeader{ 464 0: u.Header, 465 } 466 467 // Parse and check HTTP request. 468 // As RFC6455 says: 469 // The client's opening handshake consists of the following parts. If the 470 // server, while reading the handshake, finds that the client did not 471 // send a handshake that matches the description below (note that as per 472 // [RFC2616], the order of the header fields is not important), including 473 // but not limited to any violations of the ABNF grammar specified for 474 // the components of the handshake, the server MUST stop processing the 475 // client's handshake and return an HTTP response with an appropriate 476 // error code (such as 400 Bad Request). 477 // 478 // See https://tools.ietf.org/html/rfc6455#section-4.2.1 479 480 // An HTTP/1.1 or higher GET request, including a "Request-URI". 481 // 482 // Even if RFC says "1.1 or higher" without mentioning the part of the 483 // version, we apply it only to minor part. 484 switch { 485 case req.major != 1 || req.minor < 1: 486 // Abort processing the whole request because we do not even know how 487 // to actually parse it. 488 err = ErrHandshakeBadProtocol 489 490 case btsToString(req.method) != http.MethodGet: 491 err = ErrHandshakeBadMethod 492 493 default: 494 if onRequest := u.OnRequest; onRequest != nil { 495 err = onRequest(req.uri) 496 } 497 } 498 // Start headers read/parse loop. 499 var ( 500 // headerSeen reports which header was seen by setting corresponding 501 // bit on. 502 headerSeen byte 503 504 nonce = make([]byte, nonceSize) 505 ) 506 for err == nil { 507 line, e := readLine(br) 508 if e != nil { 509 return hs, e 510 } 511 if len(line) == 0 { 512 // Blank line, no more lines to read. 513 break 514 } 515 516 k, v, ok := httpParseHeaderLine(line) 517 if !ok { 518 err = ErrMalformedRequest 519 break 520 } 521 522 switch btsToString(k) { 523 case headerHostCanonical: 524 headerSeen |= headerSeenHost 525 if onHost := u.OnHost; onHost != nil { 526 err = onHost(v) 527 } 528 529 case headerUpgradeCanonical: 530 headerSeen |= headerSeenUpgrade 531 if !bytes.Equal(v, specHeaderValueUpgrade) && !bytes.EqualFold(v, specHeaderValueUpgrade) { 532 err = ErrHandshakeBadUpgrade 533 } 534 535 case headerConnectionCanonical: 536 headerSeen |= headerSeenConnection 537 if !bytes.Equal(v, specHeaderValueConnection) && !btsHasToken(v, specHeaderValueConnectionLower) { 538 err = ErrHandshakeBadConnection 539 } 540 541 case headerSecVersionCanonical: 542 headerSeen |= headerSeenSecVersion 543 if !bytes.Equal(v, specHeaderValueSecVersion) { 544 err = ErrHandshakeUpgradeRequired 545 } 546 547 case headerSecKeyCanonical: 548 headerSeen |= headerSeenSecKey 549 if len(v) != nonceSize { 550 err = ErrHandshakeBadSecKey 551 } else { 552 copy(nonce[:], v) 553 } 554 555 case headerSecProtocolCanonical: 556 if custom, check := u.ProtocolCustom, u.Protocol; hs.Protocol == "" && (custom != nil || check != nil) { 557 var ok bool 558 if custom != nil { 559 hs.Protocol, ok = custom(v) 560 } else { 561 hs.Protocol, ok = btsSelectProtocol(v, check) 562 } 563 if !ok { 564 err = ErrMalformedRequest 565 } 566 } 567 568 case headerSecExtensionsCanonical: 569 if f := u.Negotiate; err == nil && f != nil { 570 hs.Extensions, err = negotiateExtensions(v, hs.Extensions, f) 571 } 572 // DEPRECATED path. 573 if custom, check := u.ExtensionCustom, u.Extension; u.Negotiate == nil && (custom != nil || check != nil) { 574 var ok bool 575 if custom != nil { 576 hs.Extensions, ok = custom(v, hs.Extensions) 577 } else { 578 hs.Extensions, ok = btsSelectExtensions(v, hs.Extensions, check) 579 } 580 if !ok { 581 err = ErrMalformedRequest 582 } 583 } 584 585 default: 586 if onHeader := u.OnHeader; onHeader != nil { 587 err = onHeader(k, v) 588 } 589 } 590 } 591 switch { 592 case err == nil && headerSeen != headerSeenAll: 593 switch { 594 case headerSeen&headerSeenHost == 0: 595 // As RFC2616 says: 596 // A client MUST include a Host header field in all HTTP/1.1 597 // request messages. If the requested URI does not include an 598 // Internet host name for the service being requested, then the 599 // Host header field MUST be given with an empty value. An 600 // HTTP/1.1 proxy MUST ensure that any request message it 601 // forwards does contain an appropriate Host header field that 602 // identifies the service being requested by the proxy. All 603 // Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad 604 // Request) status code to any HTTP/1.1 request message which 605 // lacks a Host header field. 606 err = ErrHandshakeBadHost 607 case headerSeen&headerSeenUpgrade == 0: 608 err = ErrHandshakeBadUpgrade 609 case headerSeen&headerSeenConnection == 0: 610 err = ErrHandshakeBadConnection 611 case headerSeen&headerSeenSecVersion == 0: 612 // In case of empty or not present version we do not send 426 status, 613 // because it does not meet the ABNF rules of RFC6455: 614 // 615 // version = DIGIT | (NZDIGIT DIGIT) | 616 // ("1" DIGIT DIGIT) | ("2" DIGIT DIGIT) 617 // ; Limited to 0-255 range, with no leading zeros 618 // 619 // That is, if version is really invalid – we sent 426 status as above, if it 620 // not present – it is 400. 621 err = ErrHandshakeBadSecVersion 622 case headerSeen&headerSeenSecKey == 0: 623 err = ErrHandshakeBadSecKey 624 default: 625 panic("unknown headers state") 626 } 627 628 case err == nil && u.OnBeforeUpgrade != nil: 629 header[1], err = u.OnBeforeUpgrade() 630 } 631 if err != nil { 632 var code int 633 if rej, ok := err.(*RejectConnectionErrorType); ok { 634 code = rej.code 635 header[1] = rej.header 636 } 637 if code == 0 { 638 code = http.StatusInternalServerError 639 } 640 httpWriteResponseError(bw, err, code, header.WriteTo) 641 // Do not store Flush() error to not override already existing one. 642 bw.Flush() 643 return 644 } 645 646 httpWriteResponseUpgrade(bw, nonce, hs, header.WriteTo) 647 err = bw.Flush() 648 649 return 650 } 651 652 type handshakeHeader [2]HandshakeHeader 653 654 func (hs handshakeHeader) WriteTo(w io.Writer) (n int64, err error) { 655 for i := 0; i < len(hs) && err == nil; i++ { 656 if h := hs[i]; h != nil { 657 var m int64 658 m, err = h.WriteTo(w) 659 n += m 660 } 661 } 662 return n, err 663 }