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-----