first versiom
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -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/
|
||||||
9
.idea/gh-proxy.iml
generated
Normal file
9
.idea/gh-proxy.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="GoCommentLeadingSpace" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="GoCommentStart" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="HttpUrlsUsage" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/gh-proxy.iml" filepath="$PROJECT_DIR$/.idea/gh-proxy.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
29
.idea/watcherTasks.xml
generated
Normal file
29
.idea/watcherTasks.xml
generated
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectTasksOptions">
|
||||||
|
<TaskOptions isEnabled="true">
|
||||||
|
<option name="arguments" value="fmt $FilePath$" />
|
||||||
|
<option name="checkSyntaxErrors" value="true" />
|
||||||
|
<option name="description" />
|
||||||
|
<option name="exitCodeBehavior" value="ERROR" />
|
||||||
|
<option name="fileExtension" value="go" />
|
||||||
|
<option name="immediateSync" value="false" />
|
||||||
|
<option name="name" value="go fmt" />
|
||||||
|
<option name="output" value="$FilePath$" />
|
||||||
|
<option name="outputFilters">
|
||||||
|
<array />
|
||||||
|
</option>
|
||||||
|
<option name="outputFromStdout" value="false" />
|
||||||
|
<option name="program" value="$GoExecPath$" />
|
||||||
|
<option name="runOnExternalChanges" value="false" />
|
||||||
|
<option name="scopeName" value="Project Files" />
|
||||||
|
<option name="trackOnlyRoot" value="true" />
|
||||||
|
<option name="workingDir" value="$ProjectFileDir$" />
|
||||||
|
<envs>
|
||||||
|
<env name="GOROOT" value="$GOROOT$" />
|
||||||
|
<env name="GOPATH" value="$GOPATH$" />
|
||||||
|
<env name="PATH" value="$GoBinDirs$" />
|
||||||
|
</envs>
|
||||||
|
</TaskOptions>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
136
main.go
Normal file
136
main.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
49
regexp_test.go
Normal file
49
regexp_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
server.crt
Normal file
21
server.crt
Normal file
@@ -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-----
|
||||||
27
server.key
Normal file
27
server.key
Normal file
@@ -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-----
|
||||||
Reference in New Issue
Block a user