From bb8a233a4024e6264eba1d4fdb5a9057742e97f3 Mon Sep 17 00:00:00 2001 From: fghwett <1058178245@qq.com> Date: Wed, 23 Feb 2022 10:58:31 +0800 Subject: [PATCH] first versiom --- .idea/.gitignore | 8 ++ .idea/gh-proxy.iml | 9 ++ .idea/inspectionProfiles/Project_Default.xml | 8 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 + .idea/watcherTasks.xml | 29 ++++ go.mod | 3 + main.go | 136 +++++++++++++++++++ regexp_test.go | 49 +++++++ server.crt | 21 +++ server.key | 27 ++++ 11 files changed, 304 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/gh-proxy.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/watcherTasks.xml create mode 100644 go.mod create mode 100644 main.go create mode 100644 regexp_test.go create mode 100644 server.crt create mode 100644 server.key diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/gh-proxy.iml b/.idea/gh-proxy.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/gh-proxy.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..a225e0f --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2bebf93 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 0000000..97ad6d2 --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ef74818 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gh-proxy + +go 1.17 diff --git a/main.go b/main.go new file mode 100644 index 0000000..d242d1d --- /dev/null +++ b/main.go @@ -0,0 +1,136 @@ +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", 8091), nil) + if err != nil { + fmt.Println("run server error: ", err) + return + } +} diff --git a/regexp_test.go b/regexp_test.go new file mode 100644 index 0000000..a5cbc9d --- /dev/null +++ b/regexp_test.go @@ -0,0 +1,49 @@ +package main + +import "testing" + +func TestExp(t *testing.T) { + arr := []struct { + Url string + Match bool + }{ + { + Url: "https://github.com/hunshcn/project/archive/master.zip", + Match: true, + }, + { + Url: "https://github.com/hunshcn/project/archive/v0.1.0.tar.gz", + Match: true, + }, + { + Url: "https://github.com/hunshcn/project/releases/download/v0.1.0/example.zip", + Match: true, + }, + { + Url: "https://github.com/hunshcn/project/blob/master/filename", + Match: true, + }, + { + Url: "https://github.com/hunshcn/project/blob/1111111111111111111111111111/filename", + Match: true, + }, + { + Url: "https://raw.githubusercontent.com/stilleshan/ServerStatus/master/Dockerfile", + Match: true, + }, + { + Url: "https://baidu.com", + Match: false, + }, + { + Url: "https://git.aweoo.com/fghwett/notepad.git", + Match: false, + }, + } + + for _, v := range arr { + if v.Match != checkUrl(v.Url) { + t.Errorf("%s is not match, except: %t", v.Url, v.Match) + } + } +} diff --git a/server.crt b/server.crt new file mode 100644 index 0000000..4272a4d --- /dev/null +++ b/server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDeDCCAmACCQCf38ooVlcvWjANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJD +TjEPMA0GA1UECAwGRnVqaWFuMQ8wDQYDVQQHDAZYaWFtZW4xDTALBgNVBAoMBFRl +c3QxDTALBgNVBAsMBFRlc3QxFzAVBgNVBAMMDjEyNy4wLjAuMTo4MDkxMRYwFAYJ +KoZIhvcNAQkBFgdpQGNjLmJpMB4XDTIyMDIyMzAyNTMwNloXDTIzMDIyMzAyNTMw +NlowfjELMAkGA1UEBhMCQ04xDzANBgNVBAgMBkZ1amlhbjEPMA0GA1UEBwwGWGlh +bWVuMQ0wCwYDVQQKDARUZXN0MQ0wCwYDVQQLDARUZXN0MRcwFQYDVQQDDA4xMjcu +MC4wLjE6ODA5MTEWMBQGCSqGSIb3DQEJARYHaUBjYy5iaTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANpfoO0XN6fA2ktMjhWlQme8qc2feFWAEiTbc9Pg +L8Sp8n1Y68AIjxM2hq/ALzHbDCfppwP6mQd527AgAUyLt12KIXVRhI/cOvlIJu7s +1PnbqRR6lo7p6DFdt2Vxbm4FM5ZLLSnno8iD9aupG8U079UPXROPJFcHqrbxyPHX +7uS6WIzVzTJjvle7qQg1qTiR0DWm7o7Ue8+e877vfhcigNoP0f8jk4vWVtY4WK92 +LwcEQteDrF/jNjxKAZ12B8FeY1t5ZIc9A2UCkb6JSSfPYb4JW7YWWV+Fhy9vcQNc +VSeIamWzmYNQJs9OyhlvJEiKPFIcjvTOLQEieVFcW2bwRX0CAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEAoRkyHYWLDCuliwFE+CKCcoIk6j9NEB1NSohEk4cgYuKSVK7L ++Szc7Ske4u4aDVp9M8UT6tPZTnLsVJLc6XlQL1QdURCVCF4KHdTkYx7fD9eKskp8 +kM4aWGlLaER6psCTXj938MIOYlQxjAeeUove+grgk6MuNagXHk2DDR/gkykmAALT +Vf/gIy1HwzOhQ5+BW3e1XpYLwv9YkkyW1O/YLTe3cTmQSULfl88heLCleSTyz+Zb +cxSwn+BSpbWh0vlMarSd/UdI6g2zhk4K2/OnEhLrcjMdaV0a+wuXCtV/EoEuSsPI +omIqSb/yqNz0Y1Svj/FTmx60nOv1F32f7eHhhw== +-----END CERTIFICATE----- diff --git a/server.key b/server.key new file mode 100644 index 0000000..001b2b8 --- /dev/null +++ b/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA2l+g7Rc3p8DaS0yOFaVCZ7ypzZ94VYASJNtz0+AvxKnyfVjr +wAiPEzaGr8AvMdsMJ+mnA/qZB3nbsCABTIu3XYohdVGEj9w6+Ugm7uzU+dupFHqW +junoMV23ZXFubgUzlkstKeejyIP1q6kbxTTv1Q9dE48kVweqtvHI8dfu5LpYjNXN +MmO+V7upCDWpOJHQNabujtR7z57zvu9+FyKA2g/R/yOTi9ZW1jhYr3YvBwRC14Os +X+M2PEoBnXYHwV5jW3lkhz0DZQKRvolJJ89hvglbthZZX4WHL29xA1xVJ4hqZbOZ +g1Amz07KGW8kSIo8UhyO9M4tASJ5UVxbZvBFfQIDAQABAoIBAQC9fAskj2fgrUv9 +2LDVgW+smh1xafW9owKbbr0D1V09tgXO1HDEUejsT/zw1b9d8GeM3obowfM1dHy3 +SB44cGesx+YeaZywQ/X8RqpZ2bE4GW2Ocozf94FeDKbNZG7hEqxftl27Jd+NZ0uu +J3AMX9HXu5wVE7kBt4pfck/d4nPNjXY4ke0ewEVIKMKBgxPSFWGMA/oHuVaktYl3 +EzuktL/mQubO/516D/yCd+XgrfEhJpbskoc6h2mJJIsvgFK1zEKO2N04hsukZPSA +mHmPvrsHzF+P1Tq+5QWL+QaLzArcoYhke069MX8Oy52R2qlSZVaPIFyL7qgpR6T+ +hvfS2ZTVAoGBAPNBYxU2WAwhDrmdKaJE9zd76Atl3j6MU6dCARDv8bREMexzjevR +wUXJydmtDJtczyT5HcQK3legDJb5b9rtb/Dy2MPRUKdKbDJSbo0iPfn2dtT3VHwi +Yi7pX4o/KV7Jz6MwxNsmRYSkUgqnHi0LRVj9+u7KPDX3H/4zgi24Q0RHAoGBAOXQ +hMbKzdHXP2Q+8gTnDoEcuQ6xb0oLBrNateY+1iGtVvdHLKL6oEQ+pYhiSV9OCAJM +peDITjT91kxktHjjed5S4K/m+DZK/w+c8+ygPwZS3h4o9D64AUBPnLoOVd/1PByQ +ZUg/zmUIeDAtQ16oSSozqkuSqtUHX194f9KWCl4bAoGAVIMmGmuMAYLGq+QXX+IY +BbXNs5ALu61jLtv2pNzIG7oJoj2vU/vG6yklMLQ3ig6fhRfrqH2iK81WMmms2+Hc +H1kvyDCDlet8Vatf7zazU5G2TV00hAAqHmREJjK1e9IfinHaIs1UH7Y7LqpLCJk+ +2aE7uhSXAKS//8ADfUDkVecCgYBKs7AlSlEC79vcQxW6gDzXvTVuUEDjqZfg/xB3 +ql9CjSzirlEVHTCxXkfCgGhnQV8bGD0nRxUc5pIdPPLpBNBdc0U0CHkQdOpZ8ePc +O+6gqJko5pkWNgu6EubYF8bUSMvkYQ97H7qAXvNqfZwTjdJSlH67mA5NttrKlsm9 +8yWllwKBgQCZGYcuY+GA6wzj7+glFqxPDl4l4Kk48dENi2mF4VhgGhCpk78n7sN6 ++xF8bRGCnLVM1pmvSR43Lj83Z/7ltVmft7r91WSqLLwBPpAk27yflPUa8Ja5E0iV +3pMwDVnzFIIwWRfTWYzQQxh0Pi67ilC3aF5nm39sH3ZZ2uLeFIngbg== +-----END RSA PRIVATE KEY-----