package main import ( "fmt" "net/http" "net/url" "regexp" "strings" ) const ( PREFIX = "https://gh.ncc.cx/" exp1 = `^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:releases|archive)\/.*$` exp2 = `^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:blob|raw)\/.*$` exp3 = `^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:info|git-).*$` exp4 = `^(?:https?:\/\/)?raw\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+?\/.+$` exp5 = `^(?:https?:\/\/)?gist\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+$` exp6 = `^(?:https?:\/\/)?github\.com\/.+?\/.+?\/tags.*$` ) func checkUrl(u string) bool { for _, exp := range []string{exp1, exp2, exp3, exp4, exp5, exp6} { if ok, e := regexp.MatchString(exp, u); ok { return true } else if e != nil { fmt.Println(e) } } return false } func fetchHandler(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if !strings.Contains(path, "/") { w.WriteHeader(http.StatusNotFound) return } path = strings.TrimPrefix(path, "/") // 获取到的URL path = strings.Replace(path, "https:/", "https://", 1) u, _ := url.Parse(path) match1, _ := regexp.MatchString(exp1, u.String()) match2, _ := regexp.MatchString(exp2, u.String()) match3, _ := regexp.MatchString(exp3, u.String()) match4, _ := regexp.MatchString(exp4, u.String()) match5, _ := regexp.MatchString(exp5, u.String()) match6, _ := regexp.MatchString(exp6, u.String()) if match1 || match5 || match6 || match3 || match4 { httpHandler(w, r, path) return } else if match2 { path = strings.ReplaceAll(u.Path, "/blob/", "/raw/") httpHandler(w, r, path) return } http.Error(w, fmt.Sprintf("%s is not support", path), http.StatusOK) } func httpHandler(w http.ResponseWriter, r *http.Request, pathname string) { reqHdrRaw := r.Header if r.Method == http.MethodOptions && reqHdrRaw.Get("access-control-request-headers") != "" { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, TRACE, DELETE, HEAD, OPTIONS") w.Header().Set("Access-Control-Max-Age", "1728000") w.WriteHeader(http.StatusNoContent) return } if strings.HasPrefix(pathname, "github") { pathname = "https://" + pathname } client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } proxy(w, r, client, pathname) } func proxy(w http.ResponseWriter, r *http.Request, client *http.Client, pathname string) { r.URL, _ = url.Parse(pathname) req, _ := http.NewRequest(r.Method, r.URL.String(), r.Body) for k, v := range r.Header { req.Header.Set(k, v[0]) } for _, value := range r.Cookies() { w.Header().Add(value.Name, value.Value) } res, e := client.Do(req) if e != nil { http.Error(w, e.Error(), http.StatusOK) return } if res.Header.Get("location") != "" { location := res.Header.Get("location") if checkUrl(location) { res.Header.Set("location", PREFIX+location) } else { newReq, _ := http.NewRequest(r.Method, location, res.Body) proxy(w, newReq, http.DefaultClient, location) return } } res.Header.Set("Access-Control-Expose-Headers", "*") res.Header.Set("Access-Control-Allow-Origin", "*") res.Header.Del("Content-Security-Policy") res.Header.Del("Content-Security-Policy-Report-Only") res.Header.Del("Clear-Site-Data") w.WriteHeader(res.StatusCode) for k, v := range res.Header { w.Header().Set(k, strings.Join(v, ",")) } _ = res.Write(w) } func main() { http.HandleFunc("/", fetchHandler) err := http.ListenAndServe(fmt.Sprintf(":%d", 80), nil) if err != nil { fmt.Println("run server error: ", err) return } }