diff --git a/cmd/gocaddyway/readme.md b/cmd/gocaddyway/readme.md new file mode 100644 index 0000000..96eb1c5 --- /dev/null +++ b/cmd/gocaddyway/readme.md @@ -0,0 +1,23 @@ +## Use goflyway in Caddy [beta] + +1. Follow the [official guide](https://github.com/mholt/caddy#build) to run a working caddy server from source +2. In `caddy/caddymain/run.go`, add `_ "github.com/coyove/goflyway/cmd/gocaddyway"` +2. In `caddyhttp/httpserver/plugin.go`, add `goflyway` to `directives` at about line 600. (Don't add to the bottom, it should be in front of `locale`, the order is important) +2. Build caddy +2. Prepare a Caddyfile and start the server, e.g.: + ``` + http://:8100 { + goflyway password + proxy / http://example.com + } + ``` +2. At local: + ``` + ./goflyway -up xxx:8100 -k password + ``` +2. Done! + +## Note + +1. TCP multiplexer is not supported +2. Caddyfile hot reload is not supported, stop all and restart all \ No newline at end of file diff --git a/cmd/gocaddyway/server.go b/cmd/gocaddyway/server.go new file mode 100644 index 0000000..16fefd2 --- /dev/null +++ b/cmd/gocaddyway/server.go @@ -0,0 +1,58 @@ +package gocaddyway + +import ( + "net/http" + + "github.com/coyove/common/logg" + "github.com/coyove/goflyway/proxy" + "github.com/mholt/caddy" + "github.com/mholt/caddy/caddyhttp/httpserver" +) + +type gofwHandler struct { + Next httpserver.Handler + gofw *proxy.ProxyServer +} + +func init() { + caddy.RegisterPlugin("goflyway", caddy.Plugin{ + ServerType: "http", + Action: setup, + }) +} + +func setup(c *caddy.Controller) error { + + c.Next() + if !c.NextArg() { + return c.ArgErr() + } + + cipher := proxy.NewCipher(c.Val(), proxy.FullCipher) + cipher.IO.StartPurgeConns(20) + cipher.IO.Logger = logg.NewLogger("off") + sc := &proxy.ServerConfig{ + Cipher: cipher, + LBindTimeout: 10, + LBindCap: 256, + Logger: cipher.IO.Logger, + } + + cfg := httpserver.GetConfig(c) + mid := func(next httpserver.Handler) httpserver.Handler { + server, _ := proxy.NewServer("0.0.0.0:0", sc) + return gofwHandler{ + Next: next, + gofw: server, + } + } + cfg.AddMiddleware(mid) + return nil +} + +func (h gofwHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { + if h.gofw.ServeHTTPImpl(w, r) { + return 0, nil + } + return h.Next.ServeHTTP(w, r) +} diff --git a/proxy/server.go b/proxy/server.go index 2ee1d1a..599ed97 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -148,27 +148,13 @@ func (proxy *ProxyServer) replyGood(downstreamConn net.Conn, cr *clientRequest, downstreamConn.Write(p.Bytes()) } -func (proxy *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - replySomething := func() { - if proxy.rp == nil { - w.WriteHeader(404) - w.Write([]byte(` -404 Not Found - -

404 Not Found

-
nginx
- -`)) - } else { - proxy.rp.ServeHTTP(w, r) - } - } - +// ServeHTTPImpl returns true if it successfully handled the goflyway request, false if any error occurred. +// When succeed, ServeHTTPImpl will take over all controls of w and r, caller shall not alter them ever after. +func (proxy *ProxyServer) ServeHTTPImpl(w http.ResponseWriter, r *http.Request) bool { addr, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { proxy.Logger.Warnf("Unknown address: %s", r.RemoteAddr) - replySomething() - return + return false } var rawReq []byte @@ -200,7 +186,7 @@ DE_AGAIN: case resp := <-cb: if resp.err != nil { userConn.Write([]byte("HTTP/1.1 400 Bad Request\r\n\r\nError: " + resp.err.Error())) - return + return true } case <-time.After(time.Duration(proxy.LBindTimeout) * time.Second): proxy.Logger.Errorf("RP client didn't response") @@ -216,7 +202,7 @@ DE_AGAIN: } } proxy.localRP.Unlock() - return + return true } if fu := r.Header.Get(fwdURLHeader); fu != "" { @@ -227,14 +213,13 @@ DE_AGAIN: proxy.Logger.Dbgf("Invalid request from %s: %s", addr, proxy.stripURI(r.RequestURI)) proxy.blacklist.Add(addr, nil) - replySomething() - return + return false } if proxy.Users != nil { if !proxy.auth(cr.Auth) { proxy.Logger.Warnf("User auth failed from %s", addr) - return + return true } } @@ -255,7 +240,7 @@ DE_AGAIN: proxy.Logger.Dbgf("DNS answer of %s: %s", host, ip.String()) w.Header().Add(dnsRespHeader, base64.StdEncoding.EncodeToString([]byte(ip.IP.To4()))) w.WriteHeader(200) - return + return true } if cr.Opt.IsSet(doLocalRP) { @@ -268,7 +253,7 @@ DE_AGAIN: resp, ok := proxy.localRP.waiting[dst] if !ok { proxy.localRP.Unlock() - return + return true } proxy.localRP.Unlock() @@ -276,28 +261,27 @@ DE_AGAIN: proxy.replyGood(downstreamConn, cr, &ioc, r) go proxy.Cipher.IO.Bridge(downstreamConn, resp.req.conn, cr.IV, ioc) resp.req.callback <- resp - return + return true } } if proxy.isBlocked(dst) { w.WriteHeader(http.StatusForbidden) proxy.Logger.Logf("%s is blocked", dst) - return + return true } if cr.Opt.IsSet(doConnect) { host := dst if host == "" { proxy.Logger.Warnf("Valid rkey invalid host from %s", addr) - replySomething() - return + return false } proxy.Logger.Logf("Dial real host: %s", host) downstreamConn := proxy.hijack(w) if downstreamConn == nil { - return + return false } ioc := proxy.getIOConfig(cr) @@ -309,7 +293,7 @@ DE_AGAIN: if proxy.Policy.IsSet(PolicyDisableUDP) { proxy.Logger.Warnf("Client UDP relay request rejected") downstreamConn.Close() - return + return false } uaddr, _ := net.ResolveUDPAddr("udp", host) @@ -329,7 +313,7 @@ DE_AGAIN: if err != nil { proxy.Logger.Errorf("Dial real host failed: %v", err) downstreamConn.Close() - return + return false } proxy.replyGood(downstreamConn, cr, &ioc, r) @@ -341,13 +325,13 @@ DE_AGAIN: } else { go proxy.Cipher.IO.Bridge(downstreamConn, targetSiteConn, cr.IV, ioc) } + return true } else if cr.Opt.IsSet(doHTTPReq) { var err error r.URL, err = url.Parse(dst) if err != nil { - replySomething() - return + return false } r.Host = r.URL.Host @@ -359,7 +343,7 @@ DE_AGAIN: if err != nil { proxy.Logger.Errorf("Round trip %s: %v", r.URL, err) proxy.Write(w, cr.IV, []byte(err.Error()), http.StatusInternalServerError) - return + return true } copyHeaders(w.Header(), resp.Header, proxy.Cipher, true, cr.IV) @@ -375,9 +359,27 @@ DE_AGAIN: } tryClose(resp.Body) + return true } else { proxy.blacklist.Add(addr, nil) - replySomething() + return false + } +} + +func (proxy *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if !proxy.ServeHTTPImpl(w, r) { + if proxy.rp == nil { + w.WriteHeader(404) + w.Write([]byte(` +404 Not Found + +

404 Not Found

+
nginx
+ +`)) + } else { + proxy.rp.ServeHTTP(w, r) + } } }