func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error) { c.Version = ProtocolVersionHixie75 if req.Method != "GET" || req.Proto != "HTTP/1.1" { return http.StatusMethodNotAllowed, ErrBadRequestMethod } if req.Header.Get("Upgrade") != "WebSocket" { return http.StatusBadRequest, ErrNotWebSocket } if req.Header.Get("Connection") != "Upgrade" { return http.StatusBadRequest, ErrNotWebSocket } c.Origin, err = url.ParseRequest(strings.TrimSpace(req.Header.Get("Origin"))) if err != nil { return http.StatusBadRequest, err } var scheme string if req.TLS != nil { scheme = "wss" } else { scheme = "ws" } c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath) if err != nil { return http.StatusBadRequest, err } protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol")) protocols := strings.Split(protocol, ",") for i := 0; i < len(protocols); i++ { c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) } return http.StatusSwitchingProtocols, nil }
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error) { c.Version = ProtocolVersionHybi13 if req.Method != "GET" { return http.StatusMethodNotAllowed, ErrBadRequestMethod } // HTTP version can be safely ignored. if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { return http.StatusBadRequest, ErrNotWebSocket } key := req.Header.Get("Sec-Websocket-Key") if key == "" { return http.StatusBadRequest, ErrChallengeResponse } version := req.Header.Get("Sec-Websocket-Version") var origin string switch version { case "13": c.Version = ProtocolVersionHybi13 origin = req.Header.Get("Origin") case "8": c.Version = ProtocolVersionHybi08 origin = req.Header.Get("Sec-Websocket-Origin") default: return http.StatusBadRequest, ErrBadWebSocketVersion } c.Origin, err = url.ParseRequest(origin) if err != nil { return http.StatusForbidden, err } var scheme string if req.TLS != nil { scheme = "wss" } else { scheme = "ws" } c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath) if err != nil { return http.StatusBadRequest, err } protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) protocols := strings.Split(protocol, ",") for i := 0; i < len(protocols); i++ { c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) } c.accept, err = getNonceAccept([]byte(key)) if err != nil { return http.StatusInternalServerError, err } return http.StatusSwitchingProtocols, nil }
// NewConfig creates a new WebSocket config for client connection. func NewConfig(server, origin string) (config *Config, err error) { config = new(Config) config.Version = ProtocolVersionHybi13 config.Location, err = url.ParseRequest(server) if err != nil { return } config.Origin, err = url.ParseRequest(origin) if err != nil { return } return }
func convert(host string, r *fakeHttpRequest) (*http.Request, os.Error) { header := http.Header{} header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1") header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") theurl, err := url.ParseRequest(r.URL) if err != nil { log.Println("Error in url: ", err) return nil, err } out := &http.Request{ Method: r.Method, URL: theurl, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: header, // Body: byteReadCloser([]byte(r.Body)), ContentLength: int64(len(r.Body)), TransferEncoding: []string{}, Close: true, Host: host, Form: nil, MultipartForm: nil, Trailer: nil, RemoteAddr: "fake:req", TLS: nil, } if len(r.Body) == 0 { out.Body = nil } else { out.Body = byteReadCloser([]byte(r.Body)) } return out, nil }
// ProxyFromEnvironment returns the URL of the proxy to use for a // given request, as indicated by the environment variables // $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy). // Either URL or an error is returned. func ProxyFromEnvironment(req *Request) (*url.URL, os.Error) { proxy := getenvEitherCase("HTTP_PROXY") if proxy == "" { return nil, nil } if !useProxy(canonicalAddr(req.URL)) { return nil, nil } proxyURL, err := url.ParseRequest(proxy) if err != nil { return nil, os.NewError("invalid proxy address") } if proxyURL.Host == "" { proxyURL, err = url.ParseRequest("http://" + proxy) if err != nil { return nil, os.NewError("invalid proxy address") } } return proxyURL, nil }
func TestWithQuery(t *testing.T) { once.Do(startServer) client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } config := newConfig(t, "/echo") config.Location, err = url.ParseRequest(fmt.Sprintf("ws://%s/echo?q=v", serverAddr)) if err != nil { t.Fatal("location url", err) } ws, err := NewClient(config, client) if err != nil { t.Errorf("WebSocket handshake: %v", err) return } ws.Close() }
func (handler *oauthclientHandler) Post(r *fhttp.JsonRequest) fhttp.Response { post := new(oauthclientReq) if err := r.Extract(post); err != nil || post.Redirect == "" || post.Name == "" || post.Email == "" { return fhttp.UserError("invalid json") } emailRegexp := regexp.MustCompile(`^[a-z0-9._%\-+]+@[a-z0-9.\-]+\.[a-z]+$`) if !emailRegexp.MatchString(post.Email) { return fhttp.UserError("invalid email address") } if _, err := url.ParseRequest(post.Redirect); err != nil { return fhttp.UserError("invalid redirect uri") } context := appengine.NewContext((*http.Request)(r)) client := oauth2.NewClient(post.Redirect, post.Name, post.Email) clientKey := datastore.NewKey(context, "OAuthClient", client.Id, 0, nil) if _, err := datastore.Put(context, clientKey, client); err != nil { return fhttp.ServerError(err.String()) } return fhttp.JsonResponse{oauthclientRes{ client.Id, client.Secret, }} }
func TestHybiClientHandshakeHybi08(t *testing.T) { b := bytes.NewBuffer([]byte{}) bw := bufio.NewWriter(b) br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat `)) var err os.Error config := new(Config) config.Location, err = url.ParseRequest("ws://server.example.com/chat") if err != nil { t.Fatal("location url", err) } config.Origin, err = url.ParseRequest("http://example.com") if err != nil { t.Fatal("origin url", err) } config.Protocol = append(config.Protocol, "chat") config.Protocol = append(config.Protocol, "superchat") config.Version = ProtocolVersionHybi08 config.handshakeData = map[string]string{ "key": "dGhlIHNhbXBsZSBub25jZQ==", } err = hybiClientHandshake(config, br, bw) if err != nil { t.Errorf("handshake failed: %v", err) } req, err := http.ReadRequest(bufio.NewReader(b)) if err != nil { t.Fatalf("read request: %v", err) } if req.Method != "GET" { t.Errorf("request method expected GET, but got %q", req.Method) } if req.URL.Path != "/chat" { t.Errorf("request path expected /demo, but got %q", req.URL.Path) } if req.Proto != "HTTP/1.1" { t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) } if req.Host != "server.example.com" { t.Errorf("request Host expected example.com, but got %v", req.Host) } var expectedHeader = map[string]string{ "Connection": "Upgrade", "Upgrade": "websocket", "Sec-Websocket-Key": config.handshakeData["key"], "Sec-Websocket-Origin": config.Origin.String(), "Sec-Websocket-Protocol": "chat, superchat", "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi08), } for k, v := range expectedHeader { if req.Header.Get(k) != v { t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) } } }
// ReadRequest reads and parses a request from b. func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { tp := textproto.NewReader(b) req = new(Request) // First line: GET /index.html HTTP/1.0 var s string if s, err = tp.ReadLine(); err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF } return nil, err } var f []string if f = strings.SplitN(s, " ", 3); len(f) < 3 { return nil, &badStringError{"malformed HTTP request", s} } req.Method, req.RawURL, req.Proto = f[0], f[1], f[2] var ok bool if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok { return nil, &badStringError{"malformed HTTP version", req.Proto} } if req.URL, err = url.ParseRequest(req.RawURL); err != nil { return nil, err } // Subsequent lines: Key: value. mimeHeader, err := tp.ReadMIMEHeader() if err != nil { return nil, err } req.Header = Header(mimeHeader) // RFC2616: Must treat // GET /index.html HTTP/1.1 // Host: www.google.com // and // GET http://www.google.com/index.html HTTP/1.1 // Host: doesntmatter // the same. In the second case, any Host line is ignored. req.Host = req.URL.Host if req.Host == "" { req.Host = req.Header.Get("Host") } req.Header.Del("Host") fixPragmaCacheControl(req.Header) // TODO: Parse specific header values: // Accept // Accept-Encoding // Accept-Language // Authorization // Cache-Control // Connection // Date // Expect // From // If-Match // If-Modified-Since // If-None-Match // If-Range // If-Unmodified-Since // Max-Forwards // Proxy-Authorization // Referer [sic] // TE (transfer-codings) // Trailer // Transfer-Encoding // Upgrade // User-Agent // Via // Warning err = readTransfer(req, b) if err != nil { return nil, err } return req, nil }
func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error) { c.Version = ProtocolVersionHybi00 if req.Method != "GET" { return http.StatusMethodNotAllowed, ErrBadRequestMethod } // HTTP version can be safely ignored. if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || strings.ToLower(req.Header.Get("Connection")) != "upgrade" { return http.StatusBadRequest, ErrNotWebSocket } // TODO(ukai): check Host c.Origin, err = url.ParseRequest(req.Header.Get("Origin")) if err != nil { return http.StatusBadRequest, err } key1 := req.Header.Get("Sec-Websocket-Key1") if key1 == "" { return http.StatusBadRequest, ErrChallengeResponse } key2 := req.Header.Get("Sec-Websocket-Key2") if key2 == "" { return http.StatusBadRequest, ErrChallengeResponse } key3 := make([]byte, 8) if _, err := io.ReadFull(buf, key3); err != nil { return http.StatusBadRequest, ErrChallengeResponse } var scheme string if req.TLS != nil { scheme = "wss" } else { scheme = "ws" } c.Location, err = url.ParseRequest(scheme + "://" + req.Host + req.URL.RawPath) if err != nil { return http.StatusBadRequest, err } // Step 4. get key number in Sec-WebSocket-Key<n> fields. keyNumber1 := getKeyNumber(key1) keyNumber2 := getKeyNumber(key2) // Step 5. get number of spaces in Sec-WebSocket-Key<n> fields. space1 := uint32(strings.Count(key1, " ")) space2 := uint32(strings.Count(key2, " ")) if space1 == 0 || space2 == 0 { return http.StatusBadRequest, ErrChallengeResponse } // Step 6. key number must be an integral multiple of spaces. if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 { return http.StatusBadRequest, ErrChallengeResponse } // Step 7. let part be key number divided by spaces. part1 := keyNumber1 / space1 part2 := keyNumber2 / space2 // Step 8. let challenge be concatenation of part1, part2 and key3. // Step 9. get MD5 fingerprint of challenge. c.challengeResponse, err = getChallengeResponse(part1, part2, key3) if err != nil { return http.StatusInternalServerError, err } protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) protocols := strings.Split(protocol, ",") for i := 0; i < len(protocols); i++ { c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) } return http.StatusSwitchingProtocols, nil }
func TestHixie76ClientHandshake(t *testing.T) { b := bytes.NewBuffer([]byte{}) bw := bufio.NewWriter(b) br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 WebSocket Protocol Handshake Upgrade: WebSocket Connection: Upgrade Sec-WebSocket-Origin: http://example.com Sec-WebSocket-Location: ws://example.com/demo Sec-WebSocket-Protocol: sample 8jKS'y:G*Co,Wxa-`)) var err os.Error config := new(Config) config.Location, err = url.ParseRequest("ws://example.com/demo") if err != nil { t.Fatal("location url", err) } config.Origin, err = url.ParseRequest("http://example.com") if err != nil { t.Fatal("origin url", err) } config.Protocol = append(config.Protocol, "sample") config.Version = ProtocolVersionHixie76 config.handshakeData = map[string]string{ "key1": "4 @1 46546xW%0l 1 5", "number1": "829309203", "key2": "12998 5 Y3 1 .P00", "number2": "259970620", "key3": "^n:ds[4U", } err = hixie76ClientHandshake(config, br, bw) if err != nil { t.Errorf("handshake failed: %v", err) } req, err := http.ReadRequest(bufio.NewReader(b)) if err != nil { t.Fatalf("read request: %v", err) } if req.Method != "GET" { t.Errorf("request method expected GET, but got %q", req.Method) } if req.URL.Path != "/demo" { t.Errorf("request path expected /demo, but got %q", req.URL.Path) } if req.Proto != "HTTP/1.1" { t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) } if req.Host != "example.com" { t.Errorf("request Host expected example.com, but got %v", req.Host) } var expectedHeader = map[string]string{ "Connection": "Upgrade", "Upgrade": "WebSocket", "Origin": "http://example.com", "Sec-Websocket-Key1": config.handshakeData["key1"], "Sec-Websocket-Key2": config.handshakeData["key2"], "Sec-WebSocket-Protocol": config.Protocol[0], } for k, v := range expectedHeader { if req.Header.Get(k) != v { t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) } } }