{"record":{"id":"3acc5c553fc0600c","repo":"geektutu/7days-golang","slug":"unexpected-http-response-3acc5c","errorCode":null,"errorMessage":"unexpected HTTP response: ","messagePattern":"unexpected HTTP response: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gee-rpc/day6-load-balance/client.go","lineNumber":295,"sourceCode":"}\n\n// Dial connects to an RPC server at the specified network address\nfunc Dial(network, address string, opts ...*Option) (*Client, error) {\n\treturn dialTimeout(NewClient, network, address, opts...)\n}\n\n// NewHTTPClient new a Client instance via HTTP as transport protocol\nfunc NewHTTPClient(conn net.Conn, opt *Option) (*Client, error) {\n\t_, _ = io.WriteString(conn, fmt.Sprintf(\"CONNECT %s HTTP/1.0\\n\\n\", defaultRPCPath))\n\n\t// Require successful HTTP response\n\t// before switching to RPC protocol.\n\tresp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: \"CONNECT\"})\n\tif err == nil && resp.Status == connected {\n\t\treturn NewClient(conn, opt)\n\t}\n\tif err == nil {\n\t\terr = errors.New(\"unexpected HTTP response: \" + resp.Status)\n\t}\n\treturn nil, err\n}\n\n// DialHTTP connects to an HTTP RPC server at the specified network address\n// listening on the default HTTP RPC path.\nfunc DialHTTP(network, address string, opts ...*Option) (*Client, error) {\n\treturn dialTimeout(NewHTTPClient, network, address, opts...)\n}\n\n// XDial calls different functions to connect to a RPC server\n// according the first parameter rpcAddr.\n// rpcAddr is a general format (protocol@addr) to represent a rpc server\n// eg, http@10.0.0.1:7001, tcp@10.0.0.1:9999, unix@/tmp/geerpc.sock\nfunc XDial(rpcAddr string, opts ...*Option) (*Client, error) {\n\tparts := strings.Split(rpcAddr, \"@\")\n\tif len(parts) != 2 {\n\t\treturn nil, fmt.Errorf(\"rpc client err: wrong format '%s', expect protocol@addr\", rpcAddr)","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-rpc/day6-load-balance/client.go#L277-L313","documentation":"NewHTTPClient performs the xRPC CONNECT handshake: it sends an HTTP CONNECT request and waits for a response whose status equals the magic \"connected\" string before switching to the RPC protocol. If the response is HTTP but not the expected status, it wraps the status in \"unexpected HTTP response: \". This indicates the server did not recognize the HTTP RPC handshake.","triggerScenarios":"DialHTTP/NewHTTPClient against a port that serves plain HTTP (no xRPC handler on DefaultHTTPRPCPath); server not registered via HandleHTTP; wrong path or proxy intercepting CONNECT; connecting a normal (non-HTTP) xRPC server with DialHTTP.","commonSituations":"Pointing DialHTTP at an ordinary web server or wrong port; forgetting gee-rpc's HandleHTTP registration on the server; a reverse proxy returning 404/502 for the RPC path; version mismatch where the server expects a different handshake string.","solutions":["Register the HTTP handshake on the server (HandleHTTP) and serve DefaultHTTPRPCPath via http.ListenAndServe","Verify the address/port points to the xRPC HTTP endpoint, not a plain web server or the raw-RPC port","Use Dial (raw TCP) instead of DialHTTP if the server is not an HTTP-handshake server","Check proxies/load balancers that may intercept or rewrite the CONNECT request"],"exampleFix":"// before\nclient, err := geeprc.DialHTTP(\"tcp\", \"localhost:9999\") // plain TCP rpc server\n\n// after\n// server side:\ngeeprc.HandleHTTP()\nhttp.ListenAndServe(\"localhost:9999\", nil)\n// then client:\nclient, err := geeprc.DialHTTP(\"tcp\", \"localhost:9999\")","handlingStrategy":"validation","validationCode":"// verify the server speaks the xRPC HTTP handshake before dialing\nconn, _ := net.DialTimeout(\"tcp\", addr, 3*time.Second)\nreq, _ := http.NewRequest(\"CONNECT\", \"http://\"+addr+DefaultHTTPRPCPath, nil)\nreq.Write(conn)\nresp, err := http.ReadResponse(bufio.NewReader(conn), req)\nif err != nil || resp.Status != connected {\n    return errors.New(\"endpoint does not support xRPC over HTTP\")\n}","typeGuard":null,"tryCatchPattern":"client, err := geeprc.DialHTTP(\"tcp\", addr)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"unexpected HTTP response: \") {\n        // wrong endpoint/protocol: fall back to raw Dial or fix server registration\n    }\n    return err\n}","preventionTips":["Always call geeprc.HandleHTTP() and serve the DefaultHTTPRPCPath on the server","Keep separate ports for raw xRPC (Dial) and HTTP-handshake xRPC (DialHTTP)","Verify via curl/CONNECT smoke test before deploying client config","Check reverse proxies pass the CONNECT request through untouched"],"tags":["rpc","http","handshake","go","protocol"],"backgroundTag":"http-protocol-handshake-failed","analyzedSha":"cf3644382101dc13e7fd92e8f5c66cabc51bcd3b","analyzedAt":"2026-09-03T18:31:24.087Z","contentChangedAt":"2026-09-03T18:31:24.087Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}