-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathurl.go
154 lines (133 loc) · 3.75 KB
/
url.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package main
import (
"errors"
"net/http"
"net/url"
"strings"
)
var (
errDupRefSpec = errors.New("duplicate ref spec")
errNoRefSpec = errors.New("no ref spec")
)
// getParamRefTypeName looks at the query parameters in an HTTP request and
// returns its ref name and type, if any.
func getParamRefTypeName(request *http.Request) (retRefType, retRefName string, err error) {
rawQuery := request.URL.RawQuery
queryValues, err := url.ParseQuery(rawQuery)
if err != nil {
return
}
done := false
for _, refType := range []string{"commit", "branch", "tag"} {
refName, ok := queryValues[refType]
if ok {
if done {
err = errDupRefSpec
return
}
done = true
if len(refName) != 1 {
err = errDupRefSpec
return
}
retRefName = refName[0]
retRefType = refType
}
}
if !done {
err = errNoRefSpec
}
return
}
// parseReqURI parses an HTTP request URL, and returns a slice of path segments
// and the query parameters. It handles %2F correctly.
func parseReqURI(requestURI string) (segments []string, params url.Values, err error) {
path, paramsStr, _ := strings.Cut(requestURI, "?")
segments, err = pathToSegments(path)
if err != nil {
return
}
params, err = url.ParseQuery(paramsStr)
return
}
func pathToSegments(path string) (segments []string, err error) {
segments = strings.Split(strings.TrimPrefix(path, "/"), "/")
for i, segment := range segments {
segments[i], err = url.PathUnescape(segment)
if err != nil {
return
}
}
return
}
// redirectDir returns true and redirects the user to a version of the URL with
// a trailing slash, if and only if the request URL does not already have a
// trailing slash.
func redirectDir(writer http.ResponseWriter, request *http.Request) bool {
requestURI := request.RequestURI
pathEnd := strings.IndexAny(requestURI, "?#")
var path, rest string
if pathEnd == -1 {
path = requestURI
} else {
path = requestURI[:pathEnd]
rest = requestURI[pathEnd:]
}
if !strings.HasSuffix(path, "/") {
http.Redirect(writer, request, path+"/"+rest, http.StatusSeeOther)
return true
}
return false
}
// redirectNoDir returns true and redirects the user to a version of the URL
// without a trailing slash, if and only if the request URL has a trailing
// slash.
func redirectNoDir(writer http.ResponseWriter, request *http.Request) bool {
requestURI := request.RequestURI
pathEnd := strings.IndexAny(requestURI, "?#")
var path, rest string
if pathEnd == -1 {
path = requestURI
} else {
path = requestURI[:pathEnd]
rest = requestURI[pathEnd:]
}
if strings.HasSuffix(path, "/") {
http.Redirect(writer, request, strings.TrimSuffix(path, "/")+rest, http.StatusSeeOther)
return true
}
return false
}
// redirectUnconditionally unconditionally redirects the user back to the
// current page while preserving query parameters.
func redirectUnconditionally(writer http.ResponseWriter, request *http.Request) {
requestURI := request.RequestURI
pathEnd := strings.IndexAny(requestURI, "?#")
var path, rest string
if pathEnd == -1 {
path = requestURI
} else {
path = requestURI[:pathEnd]
rest = requestURI[pathEnd:]
}
http.Redirect(writer, request, path+rest, http.StatusSeeOther)
}
// segmentsToURL joins URL segments to the path component of a URL.
// Each segment is escaped properly first.
func segmentsToURL(segments []string) string {
for i, segment := range segments {
segments[i] = url.PathEscape(segment)
}
return strings.Join(segments, "/")
}
// anyContain returns true if and only if ss contains a string that contains c.
func anyContain(ss []string, c string) bool {
for _, s := range ss {
if strings.Contains(s, c) {
return true
}
}
return false
}