~gsthnz/satellite

ref: 08c1a5a59b68a33880f45218cdfad85b6e1b7c65 satellite/gemini.go -rw-r--r-- 2.3 KiB
08c1a5a5Paper Set minimum TLS version to TLS 1.2 1 year, 7 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package main

import (
	"fmt"
	"io"
	"mime"
	"net"
	"net/url"
	"os"
	"path"
)

type Status int

const (
	Input                     = 10
	Success                   = 20
	Redirect                  = 30
	RedirectPermanent         = 31
	TemporaryFailure          = 40
	PermanentFailure          = 50
	NotFound                  = 51
	ProxyRequestRefused       = 53
	BadRequest                = 59
	ClientCertificateRequired = 60
)

const ResponseEnd = "\r\n"
const MaxURLSize = 1024
const IndexFile = "index.gmi"
const GeminiMIME = "text/gemini"

func handleRequest(c net.Conn, di int, parsedURL *url.URL) {
	if parsedURL.Path == "" {
		parsedURL.Path = "/"
		redirectPermanent(c, parsedURL.String())
		return
	} else if parsedURL.Path != path.Clean(parsedURL.Path) {
		sendError(c, BadRequest, "Path error")
		return
	}

	if parsedURL.Path == "/" || parsedURL.Path == "." {
		serve(c, di, IndexFile)
	} else {
		serve(c, di, parsedURL.Path)
	}
}

func serve(c net.Conn, di int, filepath string) {
	fullPath := path.Join(config.Domain[di].Root, filepath)

	pathInfo, err := os.Stat(fullPath)
	if err != nil {
		sendError(c, NotFound, "Not found")
		return
	}

	if pathInfo.IsDir() {
		subDirIndex := path.Join(fullPath, IndexFile)
		if _, err := os.Stat(subDirIndex); os.IsNotExist(err) {
			sendError(c, NotFound, "Not found")
			return
		}

		fullPath = subDirIndex
	}

	mimeType := getMimeType(fullPath)

	if mimeType == "" {
		sendError(c, NotFound, "")
		return
	}

	file, err := os.Open(fullPath)
	if err != nil {
		sendError(c, NotFound, "")
		return
	}
	defer file.Close()

	success(c, file, mimeType)
}

func redirectPermanent(c net.Conn, url string) {
	c.Write(getResponseBytes(RedirectPermanent, url))
}

func sendError(c net.Conn, code int, message string) {
	c.Write(getResponseBytes(code, message))
}

func success(c net.Conn, file *os.File, mimeType string) {
	c.Write(getResponseBytes(Success, mimeType))
	io.Copy(c, file)
}

func getResponseBytes(code int, message string) []byte {
	if message == "" {
		return []byte(fmt.Sprintf("%d%s", code, ResponseEnd))
	}
	return []byte(fmt.Sprintf("%d %s%s", code, message, ResponseEnd))
}

func getMimeType(fullPath string) string {
	ext := path.Ext(fullPath)
	if ext == ".gmi" || ext == ".gemini" {
		return GeminiMIME
	}
	return mime.TypeByExtension(ext)
}