]> Sergey Matveev's repositories - tofuproxy.git/blob - rounds/redirectHTML.go
d139eb28ae41157c5e5bfc50ad4a784d81b10a73
[tofuproxy.git] / rounds / redirectHTML.go
1 /*
2 tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU
3              manager, WARC/geminispace browser
4 Copyright (C) 2021-2023 Sergey Matveev <stargrave@stargrave.org>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, version 3 of the License.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package rounds
20
21 import (
22         "fmt"
23         "net/http"
24         "path/filepath"
25         "strings"
26
27         "go.stargrave.org/tofuproxy/fifos"
28 )
29
30 var imageExts = map[string]struct{}{
31         ".apng": {},
32         ".avif": {},
33         ".gif":  {},
34         ".heic": {},
35         ".jp2":  {},
36         ".jpeg": {},
37         ".jpg":  {},
38         ".jxl":  {},
39         ".mng":  {},
40         ".png":  {},
41         ".svg":  {},
42         ".tif":  {},
43         ".tiff": {},
44         ".webp": {},
45 }
46
47 func isFeeder(req *http.Request) bool {
48         if strings.Contains(req.Header.Get("User-Agent"), "newsboat/") {
49                 return true
50         }
51         if strings.Contains(req.Header.Get("User-Agent"), "stargrave.org-feeder/") {
52                 return true
53         }
54         return false
55 }
56
57 func RoundRedirectHTML(
58         host string,
59         resp *http.Response,
60         w http.ResponseWriter,
61         req *http.Request,
62 ) (bool, error) {
63         if req.Method != http.MethodGet {
64                 return true, nil
65         }
66         var redirType string
67         switch resp.StatusCode {
68         case http.StatusMovedPermanently, http.StatusPermanentRedirect:
69                 redirType = "permanent"
70         case http.StatusFound, http.StatusSeeOther, http.StatusTemporaryRedirect:
71                 if isFeeder(req) {
72                         return true, nil
73                 }
74                 if _, ok := imageExts[filepath.Ext(req.URL.Path)]; ok {
75                         return true, nil
76                 }
77                 redirType = "temporary"
78         default:
79                 return true, nil
80         }
81         resp.Body.Close()
82         w.Header().Add("Content-Type", "text/html")
83         w.WriteHeader(http.StatusOK)
84         location := resp.Header.Get("Location")
85         fmt.Fprintf(
86                 w, `<!DOCTYPE html>
87 <html><head><title>%d %s: %s redirection</title></head>
88 <body><a href="%s">%s</a></body></html>`,
89                 resp.StatusCode, http.StatusText(resp.StatusCode),
90                 redirType, location, location,
91         )
92         fifos.LogRedir <- fmt.Sprintf(
93                 "%s %s\t%s\t%s", req.Method, req.URL, resp.Status, location,
94         )
95         return false, nil
96 }