mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-26 22:31:35 +00:00
160 lines
4.7 KiB
Go
160 lines
4.7 KiB
Go
package httputil
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// CopyHeaders copies all headers from src to dst.
|
|
func CopyHeaders(dst, src http.Header) {
|
|
for k, vv := range src {
|
|
for _, v := range vv {
|
|
dst.Add(k, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
// CleanHopByHopHeaders removes hop-by-hop headers that should not be forwarded.
|
|
func CleanHopByHopHeaders(headers http.Header) {
|
|
if headers == nil {
|
|
return
|
|
}
|
|
|
|
if connectionHeaders := headers.Get("Connection"); connectionHeaders != "" {
|
|
for _, token := range strings.Split(connectionHeaders, ",") {
|
|
if t := strings.TrimSpace(token); t != "" {
|
|
headers.Del(http.CanonicalHeaderKey(t))
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, key := range []string{
|
|
"Connection",
|
|
"Keep-Alive",
|
|
"Proxy-Authenticate",
|
|
"Proxy-Authorization",
|
|
"Te",
|
|
"Trailer",
|
|
"Transfer-Encoding",
|
|
"Proxy-Connection",
|
|
} {
|
|
headers.Del(key)
|
|
}
|
|
}
|
|
|
|
// WriteProxyError writes an HTTP error response to the writer.
|
|
func WriteProxyError(w io.Writer, code int, msg string) {
|
|
body := msg
|
|
resp := &http.Response{
|
|
StatusCode: code,
|
|
Status: fmt.Sprintf("%d %s", code, http.StatusText(code)),
|
|
Proto: "HTTP/1.1",
|
|
ProtoMajor: 1,
|
|
ProtoMinor: 1,
|
|
Header: make(http.Header),
|
|
Body: io.NopCloser(strings.NewReader(body)),
|
|
ContentLength: int64(len(body)),
|
|
Close: true,
|
|
}
|
|
resp.Header.Set("Content-Type", "text/plain; charset=utf-8")
|
|
resp.Header.Set("Content-Length", fmt.Sprintf("%d", len(body)))
|
|
_ = resp.Write(w)
|
|
_ = resp.Body.Close()
|
|
}
|
|
|
|
func WriteLocalServiceUnavailable(w io.Writer, localPort int) {
|
|
html := fmt.Sprintf(`<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>502 - Local Service Unavailable</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: #fff;
|
|
color: #24292f;
|
|
line-height: 1.6;
|
|
}
|
|
.container { max-width: 720px; margin: 0 auto; padding: 48px 24px; }
|
|
header { margin-bottom: 48px; }
|
|
h1 { font-size: 28px; font-weight: 600; margin-bottom: 8px; }
|
|
h1 span { margin-right: 8px; }
|
|
.desc { color: #57606a; font-size: 16px; }
|
|
p { margin-bottom: 16px; }
|
|
.info-box {
|
|
background: #f6f8fa;
|
|
border: 1px solid #d0d7de;
|
|
border-radius: 6px;
|
|
padding: 16px;
|
|
margin: 24px 0;
|
|
}
|
|
.info-box ul {
|
|
margin: 12px 0 0 20px;
|
|
color: #57606a;
|
|
}
|
|
.info-box li { margin-bottom: 8px; }
|
|
code {
|
|
background: #f6f8fa;
|
|
border: 1px solid #d0d7de;
|
|
border-radius: 4px;
|
|
padding: 2px 6px;
|
|
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
font-size: 14px;
|
|
}
|
|
footer { margin-top: 48px; padding-top: 24px; border-top: 1px solid #d0d7de; }
|
|
footer a { color: #57606a; text-decoration: none; font-size: 14px; }
|
|
footer a:hover { color: #0969da; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<h1><span>⚠️</span>Local Service Unavailable</h1>
|
|
<p class="desc">The tunnel is active, but the local service is not responding.</p>
|
|
</header>
|
|
|
|
<div class="info-box">
|
|
<p>This could happen because:</p>
|
|
<ul>
|
|
<li>No service is running on port <code>%d</code></li>
|
|
<li>The local service has crashed or stopped</li>
|
|
<li>The service is still starting up</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<p>Please ensure your local service is running on port <code>%d</code> and try again.</p>
|
|
|
|
<footer>
|
|
<a href="https://github.com/Gouryella/drip" target="_blank">GitHub</a>
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html>`, localPort, localPort)
|
|
|
|
resp := &http.Response{
|
|
StatusCode: http.StatusBadGateway,
|
|
Status: "502 Bad Gateway",
|
|
Proto: "HTTP/1.1",
|
|
ProtoMajor: 1,
|
|
ProtoMinor: 1,
|
|
Header: make(http.Header),
|
|
Body: io.NopCloser(strings.NewReader(html)),
|
|
ContentLength: int64(len(html)),
|
|
Close: true,
|
|
}
|
|
resp.Header.Set("Content-Type", "text/html; charset=utf-8")
|
|
resp.Header.Set("Content-Length", fmt.Sprintf("%d", len(html)))
|
|
_ = resp.Write(w)
|
|
_ = resp.Body.Close()
|
|
}
|
|
|
|
// IsWebSocketUpgrade checks if the request is a WebSocket upgrade request.
|
|
func IsWebSocketUpgrade(req *http.Request) bool {
|
|
return strings.EqualFold(req.Header.Get("Upgrade"), "websocket") &&
|
|
strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade")
|
|
}
|