diff options
| author | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 16:12:43 +0200 |
|---|---|---|
| committer | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 16:12:43 +0200 |
| commit | ac770231436f8a17d348a6a0ab934429df3c57d0 (patch) | |
| tree | 50e14597a6b20a464882b123275f76b906616af7 /internal/port/gemini.go | |
| parent | WIP: Refactoring (diff) | |
| download | gopherproxy-ac770231436f8a17d348a6a0ab934429df3c57d0.tar.gz gopherproxy-ac770231436f8a17d348a6a0ab934429df3c57d0.tar.bz2 gopherproxy-ac770231436f8a17d348a6a0ab934429df3c57d0.zip | |
WIP: Refactoring
Diffstat (limited to 'internal/port/gemini.go')
| -rw-r--r-- | internal/port/gemini.go | 176 |
1 files changed, 64 insertions, 112 deletions
diff --git a/internal/port/gemini.go b/internal/port/gemini.go index b10da7d..0d8292c 100644 --- a/internal/port/gemini.go +++ b/internal/port/gemini.go | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | package port | 1 | package port |
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "bufio" | ||
| 5 | "bytes" | 4 | "bytes" |
| 6 | "fmt" | 5 | "fmt" |
| 7 | "html/template" | 6 | "html/template" |
| @@ -10,7 +9,6 @@ import ( | |||
| 10 | "mime" | 9 | "mime" |
| 11 | "net/http" | 10 | "net/http" |
| 12 | "net/url" | 11 | "net/url" |
| 13 | "regexp" | ||
| 14 | "strings" | 12 | "strings" |
| 15 | 13 | ||
| 16 | "golang.org/x/net/html/charset" | 14 | "golang.org/x/net/html/charset" |
| @@ -21,129 +19,83 @@ import ( | |||
| 21 | "github.com/temoto/robotstxt" | 19 | "github.com/temoto/robotstxt" |
| 22 | ) | 20 | ) |
| 23 | 21 | ||
| 24 | type SectionType byte | 22 | type GeminiTemplateVariables struct { |
| 25 | |||
| 26 | const ( | ||
| 27 | RAW_TEXT = SectionType(0) | ||
| 28 | REFLOW_TEXT = SectionType(1) | ||
| 29 | LINK = SectionType(2) | ||
| 30 | ) | ||
| 31 | |||
| 32 | type Section struct { | ||
| 33 | Type SectionType | ||
| 34 | Text string | ||
| 35 | URL template.URL | ||
| 36 | } | ||
| 37 | |||
| 38 | type templateVariables struct { | ||
| 39 | Title string | 23 | Title string |
| 40 | URI string | 24 | URL string |
| 41 | Assets AssetList | 25 | Assets AssetList |
| 42 | Sections []Section | 26 | Sections []GeminiSection |
| 27 | Nav []GeminiNavItem | ||
| 43 | } | 28 | } |
| 44 | 29 | ||
| 45 | var ( | 30 | type GeminiNavItem struct { |
| 46 | TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") | 31 | Label string |
| 47 | ) | 32 | URL string |
| 33 | } | ||
| 48 | 34 | ||
| 49 | func resolveURI(uri string, baseURL *url.URL) (resolvedURI string) { | 35 | type GeminiSection struct { |
| 36 | Type libgemini.GeminiDocSectionType | ||
| 37 | Text string | ||
| 38 | URL template.URL | ||
| 39 | Items []string | ||
| 40 | } | ||
| 41 | |||
| 42 | func resolveURL(uri string, baseURL *url.URL) (resolvedURL string) { | ||
| 50 | if strings.HasPrefix(uri, "//") { | 43 | if strings.HasPrefix(uri, "//") { |
| 51 | resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "//") | 44 | resolvedURL = "/gemini/" + strings.TrimPrefix(uri, "//") |
| 52 | } else if strings.HasPrefix(uri, "gemini://") { | 45 | } else if strings.HasPrefix(uri, "gemini://") { |
| 53 | resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "gemini://") | 46 | resolvedURL = "/gemini/" + strings.TrimPrefix(uri, "gemini://") |
| 54 | } else if strings.HasPrefix(uri, "gopher://") { | 47 | } else if strings.HasPrefix(uri, "gopher://") { |
| 55 | resolvedURI = "/gopher/" + strings.TrimPrefix(uri, "gopher://") | 48 | resolvedURL = "/gopher/" + strings.TrimPrefix(uri, "gopher://") |
| 56 | } else { | 49 | } else { |
| 57 | url, err := url.Parse(uri) | 50 | url, err := url.Parse(uri) |
| 58 | if err != nil { | 51 | if err != nil { |
| 59 | return "" | 52 | return "" |
| 60 | } | 53 | } |
| 61 | adjustedURI := baseURL.ResolveReference(url) | 54 | adjustedURL := baseURL.ResolveReference(url) |
| 62 | path := adjustedURI.Path | 55 | path := adjustedURL.Path |
| 63 | if !strings.HasPrefix(path, "/") { | 56 | if !strings.HasPrefix(path, "/") { |
| 64 | path = "/" + path | 57 | path = "/" + path |
| 65 | } | 58 | } |
| 66 | if adjustedURI.Scheme == "gemini" { | 59 | if adjustedURL.Scheme == "gemini" { |
| 67 | resolvedURI = "/gemini/" + adjustedURI.Host + path | 60 | resolvedURL = "/gemini/" + adjustedURL.Host + path |
| 68 | } else if adjustedURI.Scheme == "gopher" { | 61 | } else if adjustedURL.Scheme == "gopher" { |
| 69 | resolvedURI = "/gopher/" + adjustedURI.Host + path | 62 | resolvedURL = "/gopher/" + adjustedURL.Host + path |
| 70 | } else { | 63 | } else { |
| 71 | resolvedURI = adjustedURI.String() | 64 | resolvedURL = adjustedURL.String() |
| 72 | } | 65 | } |
| 73 | } | 66 | } |
| 74 | 67 | ||
| 75 | return | 68 | return |
| 76 | } | 69 | } |
| 77 | 70 | ||
| 78 | func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (sections []Section) { | 71 | func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (sections []GeminiSection) { |
| 79 | baseURL, err := url.Parse(fmt.Sprintf( | 72 | baseURL, err := url.Parse(fmt.Sprintf( |
| 80 | "gemini://%s/%s", | 73 | "gemini://%s/%s", |
| 81 | hostport, | 74 | hostport, |
| 82 | uri, | 75 | uri, |
| 83 | )) | 76 | )) |
| 84 | if err != nil { | 77 | if err != nil { |
| 85 | return []Section{} | 78 | return |
| 86 | } | ||
| 87 | |||
| 88 | skipSection := true | ||
| 89 | |||
| 90 | section := Section{ | ||
| 91 | Type: RAW_TEXT, | ||
| 92 | } | 79 | } |
| 93 | 80 | ||
| 94 | scanner := bufio.NewScanner(body) | 81 | unpreppedSections := libgemini.ParseGeminiDocument(body) |
| 95 | 82 | ||
| 96 | for scanner.Scan() { | 83 | for _, section := range unpreppedSections { |
| 97 | line := strings.Trim(scanner.Text(), "\r\n") | 84 | if section.Type != libgemini.LINK { |
| 98 | line = TermEscapeSGRPattern.ReplaceAllString(line, "") | 85 | sections = append(sections, GeminiSection{ |
| 99 | 86 | Type: section.Type, | |
| 100 | linkMatch := libgemini.LinkPattern.FindStringSubmatch(line) | 87 | Text: section.Text, |
| 101 | if len(linkMatch) != 0 && linkMatch[0] != "" { | 88 | URL: template.URL(section.URL), |
| 102 | curType := section.Type | 89 | Items: section.Items, |
| 103 | |||
| 104 | if !skipSection { | ||
| 105 | sections = append(sections, section) | ||
| 106 | } | ||
| 107 | |||
| 108 | label := linkMatch[2] | ||
| 109 | if label == "" { | ||
| 110 | label = linkMatch[1] | ||
| 111 | } | ||
| 112 | |||
| 113 | sections = append(sections, Section{ | ||
| 114 | Type: LINK, | ||
| 115 | Text: label, | ||
| 116 | URL: template.URL(resolveURI(linkMatch[1], baseURL)), | ||
| 117 | }) | 90 | }) |
| 118 | |||
| 119 | skipSection = false | ||
| 120 | section = Section{ | ||
| 121 | Type: curType, | ||
| 122 | } | ||
| 123 | } else { | ||
| 124 | reflowModeMatch := libgemini.ReflowModePattern.FindStringSubmatch(line) | ||
| 125 | if len(reflowModeMatch) != 0 { | ||
| 126 | newType := RAW_TEXT | ||
| 127 | if section.Type == RAW_TEXT { | ||
| 128 | newType = REFLOW_TEXT | ||
| 129 | } | ||
| 130 | |||
| 131 | if !skipSection { | ||
| 132 | sections = append(sections, section) | ||
| 133 | } | ||
| 134 | |||
| 135 | skipSection = false | ||
| 136 | section = Section{ | ||
| 137 | Type: newType, | ||
| 138 | } | ||
| 139 | } else { | ||
| 140 | section.Text = section.Text + "\n" + line | ||
| 141 | } | ||
| 142 | } | 91 | } |
| 143 | } | ||
| 144 | 92 | ||
| 145 | if !skipSection { | 93 | sections = append(sections, GeminiSection{ |
| 146 | sections = append(sections, section) | 94 | Type: section.Type, |
| 95 | Text: section.Text, | ||
| 96 | URL: template.URL(resolveURL(section.URL, baseURL)), | ||
| 97 | Items: section.Items, | ||
| 98 | }) | ||
| 147 | } | 99 | } |
| 148 | 100 | ||
| 149 | return | 101 | return |
| @@ -176,12 +128,12 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 176 | 128 | ||
| 177 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | 129 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) |
| 178 | if err != nil { | 130 | if err != nil { |
| 179 | if e := tpl.Execute(w, templateVariables{ | 131 | if e := tpl.Execute(w, GeminiTemplateVariables{ |
| 180 | Title: title, | 132 | Title: title, |
| 181 | URI: hostport, | 133 | URL: hostport, |
| 182 | Assets: assetList, | 134 | Assets: assetList, |
| 183 | Sections: []Section{{ | 135 | Sections: []GeminiSection{{ |
| 184 | Type: RAW_TEXT, | 136 | Type: libgemini.RAW_TEXT, |
| 185 | Text: fmt.Sprintf("Error: %s", err), | 137 | Text: fmt.Sprintf("Error: %s", err), |
| 186 | }}, | 138 | }}, |
| 187 | }); e != nil { | 139 | }); e != nil { |
| @@ -205,12 +157,12 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 205 | ) | 157 | ) |
| 206 | 158 | ||
| 207 | if err != nil { | 159 | if err != nil { |
| 208 | if e := tpl.Execute(w, templateVariables{ | 160 | if e := tpl.Execute(w, GeminiTemplateVariables{ |
| 209 | Title: title, | 161 | Title: title, |
| 210 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 162 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
| 211 | Assets: assetList, | 163 | Assets: assetList, |
| 212 | Sections: []Section{{ | 164 | Sections: []GeminiSection{{ |
| 213 | Type: RAW_TEXT, | 165 | Type: libgemini.RAW_TEXT, |
| 214 | Text: fmt.Sprintf("Error: %s", err), | 166 | Text: fmt.Sprintf("Error: %s", err), |
| 215 | }}, | 167 | }}, |
| 216 | }); e != nil { | 168 | }); e != nil { |
| @@ -227,12 +179,12 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 227 | uri, | 179 | uri, |
| 228 | )) | 180 | )) |
| 229 | if err != nil { | 181 | if err != nil { |
| 230 | if e := tpl.Execute(w, templateVariables{ | 182 | if e := tpl.Execute(w, GeminiTemplateVariables{ |
| 231 | Title: title, | 183 | Title: title, |
| 232 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 184 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
| 233 | Assets: assetList, | 185 | Assets: assetList, |
| 234 | Sections: []Section{{ | 186 | Sections: []GeminiSection{{ |
| 235 | Type: RAW_TEXT, | 187 | Type: libgemini.RAW_TEXT, |
| 236 | Text: fmt.Sprintf("Error: %s", err), | 188 | Text: fmt.Sprintf("Error: %s", err), |
| 237 | }}, | 189 | }}, |
| 238 | }); e != nil { | 190 | }); e != nil { |
| @@ -242,17 +194,17 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 242 | return | 194 | return |
| 243 | } | 195 | } |
| 244 | 196 | ||
| 245 | http.Redirect(w, req, resolveURI(res.Header.Meta, baseURL), http.StatusFound) | 197 | http.Redirect(w, req, resolveURL(res.Header.Meta, baseURL), http.StatusFound) |
| 246 | return | 198 | return |
| 247 | } | 199 | } |
| 248 | 200 | ||
| 249 | if int(res.Header.Status/10) != 2 { | 201 | if int(res.Header.Status/10) != 2 { |
| 250 | if err := tpl.Execute(w, templateVariables{ | 202 | if err := tpl.Execute(w, GeminiTemplateVariables{ |
| 251 | Title: title, | 203 | Title: title, |
| 252 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 204 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
| 253 | Assets: assetList, | 205 | Assets: assetList, |
| 254 | Sections: []Section{{ | 206 | Sections: []GeminiSection{{ |
| 255 | Type: RAW_TEXT, | 207 | Type: libgemini.RAW_TEXT, |
| 256 | Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), | 208 | Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), |
| 257 | }}, | 209 | }}, |
| 258 | }); err != nil { | 210 | }); err != nil { |
| @@ -277,20 +229,20 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 277 | writer.Close() | 229 | writer.Close() |
| 278 | } | 230 | } |
| 279 | 231 | ||
| 280 | var sections []Section | 232 | var sections []GeminiSection |
| 281 | 233 | ||
| 282 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { | 234 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { |
| 283 | sections = parseGeminiDocument(buf, uri, hostport) | 235 | sections = parseGeminiDocument(buf, uri, hostport) |
| 284 | } else { | 236 | } else { |
| 285 | sections = append(sections, Section{ | 237 | sections = append(sections, GeminiSection{ |
| 286 | Type: RAW_TEXT, | 238 | Type: libgemini.RAW_TEXT, |
| 287 | Text: buf.String(), | 239 | Text: buf.String(), |
| 288 | }) | 240 | }) |
| 289 | } | 241 | } |
| 290 | 242 | ||
| 291 | if err := tpl.Execute(w, templateVariables{ | 243 | if err := tpl.Execute(w, GeminiTemplateVariables{ |
| 292 | Title: title, | 244 | Title: title, |
| 293 | URI: fmt.Sprintf("%s/%s", hostport, uri), | 245 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
| 294 | Assets: assetList, | 246 | Assets: assetList, |
| 295 | Sections: sections, | 247 | Sections: sections, |
| 296 | }); err != nil { | 248 | }); err != nil { |
