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