diff options
| author | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 12:12:43 +0200 |
|---|---|---|
| committer | Feuerfuchs <git@feuerfuchs.dev> | 2020-05-18 12:12:43 +0200 |
| commit | 4bf44b16562335b3d09b6df0150521bb5b5f776f (patch) | |
| tree | 576723e6dc9f9db48d0892f7ec354a11b973aef4 /pkg/libgemini | |
| parent | Added 2 more glyphs (diff) | |
| download | gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.gz gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.bz2 gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.zip | |
WIP: Refactoring
Diffstat (limited to 'pkg/libgemini')
| -rw-r--r-- | pkg/libgemini/libgemini.go | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/pkg/libgemini/libgemini.go b/pkg/libgemini/libgemini.go new file mode 100644 index 0000000..303490c --- /dev/null +++ b/pkg/libgemini/libgemini.go | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | package libgemini | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bufio" | ||
| 5 | "crypto/tls" | ||
| 6 | "errors" | ||
| 7 | "fmt" | ||
| 8 | "io" | ||
| 9 | "mime" | ||
| 10 | "net" | ||
| 11 | "net/url" | ||
| 12 | "regexp" | ||
| 13 | "strconv" | ||
| 14 | "strings" | ||
| 15 | ) | ||
| 16 | |||
| 17 | const ( | ||
| 18 | CRLF = "\r\n" | ||
| 19 | ) | ||
| 20 | |||
| 21 | const ( | ||
| 22 | STATUS_INPUT = 10 | ||
| 23 | STATUS_SUCCESS = 20 | ||
| 24 | STATUS_SUCCESS_CERT = 21 | ||
| 25 | STATUS_REDIRECT_TEMP = 30 | ||
| 26 | STATUS_REDIRECT_PERM = 31 | ||
| 27 | STATUS_TEMP_FAILURE = 40 | ||
| 28 | STATUS_SERVER_UNAVAILABLE = 41 | ||
| 29 | STATUS_CGI_ERROR = 42 | ||
| 30 | STATUS_PROXY_ERROR = 43 | ||
| 31 | STATUS_SLOW_DOWN = 44 | ||
| 32 | STATUS_PERM_FAILURE = 50 | ||
| 33 | STATUS_NOT_FOUND = 51 | ||
| 34 | STATUS_GONE = 52 | ||
| 35 | STATUS_PROXY_REFUSED = 53 | ||
| 36 | STATUS_BAD_REQUEST = 59 | ||
| 37 | STATUS_CLIENT_CERT_EXPIRED = 60 | ||
| 38 | STATUS_TRANSIENT_CERT_REQUEST = 61 | ||
| 39 | STATUS_AUTH_CERT_REQUIRED = 62 | ||
| 40 | STATUS_CERT_REJECTED = 63 | ||
| 41 | STATUS_FUTURE_CERT_REJECTED = 64 | ||
| 42 | STATUS_EXPIRED_CERT_REJECTED = 65 | ||
| 43 | ) | ||
| 44 | |||
| 45 | const ( | ||
| 46 | MIME_GEMINI = "text/gemini" | ||
| 47 | DEFAULT_MIME = MIME_GEMINI | ||
| 48 | DEFAULT_CHARSET = "utf-8" | ||
| 49 | ) | ||
| 50 | |||
| 51 | var ( | ||
| 52 | HeaderPattern = regexp.MustCompile("^(\\d\\d)[ \\t]+(.*)$") | ||
| 53 | LinkPattern = regexp.MustCompile("^=>[ \\t]*([^ \\t]+)(?:[ \\t]+(.*))?$") | ||
| 54 | ) | ||
| 55 | |||
| 56 | type Header struct { | ||
| 57 | Status int | ||
| 58 | Meta string | ||
| 59 | } | ||
| 60 | |||
| 61 | type Response struct { | ||
| 62 | Header *Header | ||
| 63 | Body io.Reader | ||
| 64 | } | ||
| 65 | |||
| 66 | func Get(uri string) (*Response, error) { | ||
| 67 | u, err := url.Parse(uri) | ||
| 68 | if err != nil { | ||
| 69 | return nil, err | ||
| 70 | } | ||
| 71 | |||
| 72 | if u.Scheme != "gemini" { | ||
| 73 | return nil, errors.New("invalid scheme for uri") | ||
| 74 | } | ||
| 75 | |||
| 76 | host := u.Hostname() | ||
| 77 | port := u.Port() | ||
| 78 | |||
| 79 | if port == "" { | ||
| 80 | port = "1965" | ||
| 81 | } | ||
| 82 | |||
| 83 | conn, err := tls.Dial("tcp", net.JoinHostPort(host, port), &tls.Config{ | ||
| 84 | MinVersion: tls.VersionTLS12, | ||
| 85 | InsecureSkipVerify: true, | ||
| 86 | }) | ||
| 87 | if err != nil { | ||
| 88 | return nil, err | ||
| 89 | } | ||
| 90 | |||
| 91 | _, err = conn.Write([]byte(u.String() + CRLF)) | ||
| 92 | if err != nil { | ||
| 93 | conn.Close() | ||
| 94 | return nil, err | ||
| 95 | } | ||
| 96 | |||
| 97 | reader := bufio.NewReader(conn) | ||
| 98 | |||
| 99 | line, _, err := reader.ReadLine() | ||
| 100 | if err != nil { | ||
| 101 | conn.Close() | ||
| 102 | return nil, err | ||
| 103 | } | ||
| 104 | |||
| 105 | header, err := ParseHeader(string(line)) | ||
| 106 | if err != nil { | ||
| 107 | conn.Close() | ||
| 108 | return nil, err | ||
| 109 | } | ||
| 110 | |||
| 111 | return &Response{ | ||
| 112 | Header: header, | ||
| 113 | Body: reader, | ||
| 114 | }, nil | ||
| 115 | } | ||
| 116 | |||
| 117 | func ParseHeader(line string) (header *Header, err error) { | ||
| 118 | matches := HeaderPattern.FindStringSubmatch(line) | ||
| 119 | |||
| 120 | status, err := strconv.Atoi(matches[1]) | ||
| 121 | if err != nil { | ||
| 122 | return nil, err | ||
| 123 | } | ||
| 124 | |||
| 125 | meta := matches[2] | ||
| 126 | |||
| 127 | if int(status/10) == 2 { | ||
| 128 | mediaType, params, err := mime.ParseMediaType(meta) | ||
| 129 | |||
| 130 | if err != nil { | ||
| 131 | meta = fmt.Sprintf("%s;charset=%s", DEFAULT_MIME, DEFAULT_CHARSET) | ||
| 132 | } else if strings.HasPrefix(mediaType, "text/") { | ||
| 133 | if _, ok := params["charset"]; !ok { | ||
| 134 | meta += ";charset=" + DEFAULT_CHARSET | ||
| 135 | } | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | header = &Header{ | ||
| 140 | Status: status, | ||
| 141 | Meta: meta, | ||
| 142 | } | ||
| 143 | |||
| 144 | return | ||
| 145 | } | ||
