{"record":{"id":"fadd8d7e3a84d95a","repo":"ginuerzh/gost","slug":"method-not-allowed","errorCode":null,"errorMessage":"method not allowed","messagePattern":"method not allowed","errorType":"http","errorClass":null,"httpStatus":405,"severity":"error","filePath":"http2.go","lineNumber":787,"sourceCode":"\tif err != nil {\n\t\tlog.Logf(\"[http2] %s - %s %s %s %s: %s\",\n\t\t\tr.RemoteAddr, r.Host, r.Method, r.RequestURI, r.Proto, err)\n\t\treturn\n\t}\n\tselect {\n\tcase l.connChan <- conn:\n\tdefault:\n\t\tconn.Close()\n\t\tlog.Logf(\"[http2] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t}\n\n\t<-conn.closed // NOTE: we need to wait for streaming end, or the connection will be closed\n}\n\nfunc (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*http2Conn, error) {\n\tif l.path == \"\" && r.Method != http.MethodConnect {\n\t\tw.WriteHeader(http.StatusMethodNotAllowed)\n\t\treturn nil, errors.New(\"method not allowed\")\n\t}\n\n\tif l.path != \"\" && r.RequestURI != l.path {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\treturn nil, errors.New(\"bad request\")\n\t}\n\n\tw.WriteHeader(http.StatusOK)\n\tif fw, ok := w.(http.Flusher); ok {\n\t\tfw.Flush() // write header to client\n\t}\n\n\tremoteAddr, _ := net.ResolveTCPAddr(\"tcp\", r.RemoteAddr)\n\tif remoteAddr == nil {\n\t\tremoteAddr = &net.TCPAddr{\n\t\t\tIP:   net.IPv4zero,\n\t\t\tPort: 0,\n\t\t}","sourceCodeStart":769,"sourceCodeEnd":805,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/http2.go#L769-L805","documentation":"h2Listener.upgrade rejects an incoming HTTP/2 request whose method is not CONNECT when the listener was created without a fixed path (l.path == \"\"). In pathless mode the listener only accepts HTTP/2 CONNECT-style tunneling requests; anything else is answered with 405 Method Not Allowed and this error.","triggerScenarios":"Dialing an h2Listener configured with no path using a plain GET/POST (not http2 transport CONNECT), e.g. a normal HTTP request from a browser or curl hitting the server's endpoint while l.path == \"\".","commonSituations":"Testing the h2 endpoint with curl/browser instead of the library's http2 client; client and server configured with mismatched modes (one expects CONNECT tunneling, the other sends ordinary requests); proxying non-CONNECT traffic to an http2 tunnel endpoint.","solutions":["Use the library's HTTP/2 client dialer so the request is sent as an HTTP/2 CONNECT, or upgrade via the expected method.","Configure a path on the listener (l.path) if you want to accept ordinary requests on a specific URI.","Check the server-side listener options: if path is empty, only CONNECT is valid — fix the client to match.","Verify any reverse proxy in front preserves the CONNECT method and does not rewrite it to GET."],"exampleFix":"// before (client sends plain GET)\nresp, _ := http.Get(\"https://host/tunnel\")\n// after (client performs HTTP/2 CONNECT via library dialer)\nconn, _ := (&gost.http2.Client{Host: \"host:443\"}).Dial(ctx, \"tcp\", \"target:80\")","handlingStrategy":"validation","validationCode":"// client side: only CONNECT-style requests are valid when the listener has no path\nif listenerPath == \"\" && req.Method != http.MethodConnect {\n    return fmt.Errorf(\"pathless h2 listener requires CONNECT, got %s\", req.Method)\n}","typeGuard":null,"tryCatchPattern":"conn, err := dialer.Dial(ctx, network, addr)\nif err != nil {\n    var oe *net.OpError\n    if errors.As(err, &oe) && strings.Contains(err.Error(), \"method not allowed\") {\n        return fmt.Errorf(\"server requires CONNECT for this endpoint: %w\", err)\n    }\n    return err\n}","preventionTips":["Keep client request method and server listener path configuration symmetric.","Test tunnel endpoints with the library's own dialer, not curl/browsers.","Ensure reverse proxies preserve the CONNECT method."],"tags":["http2","method-not-allowed","connect-tunnel","server"],"backgroundTag":"http-405-method-not-allowed","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}