-
Notifications
You must be signed in to change notification settings - Fork 205
/
server.factor
119 lines (99 loc) · 3.35 KB
/
server.factor
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
113
114
115
116
117
118
119
! Copyright (C) 2021 John Benediktsson
! See https://factorcode.org/license.txt for BSD license
USING: accessors calendar combinators combinators.short-circuit
command-line formatting io io.directories io.encodings.binary
io.encodings.string io.encodings.utf8 io.files io.files.info
io.files.types io.pathnames io.servers io.sockets.secure kernel
math mime.types namespaces sequences sorting splitting strings
urls urls.encoding ;
IN: gemini.server
TUPLE: gemini-server < threaded-server
{ serving-directory string } ;
<PRIVATE
: send-file ( path -- )
binary [ [ write ] each-block ] with-file-reader ;
: write-utf8 ( string -- )
utf8 encode write ;
: send-status ( path file-info -- )
type>> {
{ +directory+ [ drop "text/gemini" ] }
{ +regular-file+ [ mime-type ] }
[ 2drop f ]
} case "application/octet-stream" or
"20 %s\r\n" sprintf write-utf8 ;
: file-modified ( entry -- string )
modified>> "%Y-%b-%d %H:%M" strftime ;
: file-size ( entry -- string )
dup directory? [
drop "- "
] [
size>> {
{ [ dup 40 2^ >= ] [ 40 2^ /f "TB" ] }
{ [ dup 30 2^ >= ] [ 30 2^ /f "GB" ] }
{ [ dup 20 2^ >= ] [ 20 2^ /f "MB" ] }
[ 10 2^ /f "KB" ]
} cond "%.1f %s" sprintf
] if ;
:: list-directory ( server path -- )
path server serving-directory>> ?head drop [
"# [%s]\r\n\r\n" sprintf write-utf8
] [
dup "/" = [ drop ] [
parent-directory ".."
"=> %s %-69s\r\n" sprintf
write-utf8
] if
] bi
path [
[ name>> "." head? ] reject
[ { [ directory? ] [ regular-file? ] } 1|| ] filter
[ name>> ] sort-by
[
[ name>> ] [ directory? [ "/" append ] when ] bi
[
url-encode
] [
dup file-info [ file-modified ] [ file-size ] bi
"%-40s %s %10s" sprintf
] bi
"=> %s %s\r\n" sprintf
write-utf8
] each
] with-directory-entries ;
: send-directory ( server path -- )
dup ".geminimap" append-path [
send-file 2drop
] [
drop dup ".geminihead" append-path
[ send-file ] when-file-exists
list-directory
] if-file-exists ;
: read-gemini-path ( -- path )
readln utf8 decode "\r" ?tail drop >url path>> ;
M: gemini-server handle-client*
dup serving-directory>> read-gemini-path append
dup file-info [ send-status ] 2keep type>> {
{ +directory+ [ send-directory ] }
{ +regular-file+ [ nip send-file ] }
[ 3drop ]
} case flush ;
PRIVATE>
: <gemini-secure-config> ( -- secure-config )
<secure-config>
"key-file" get absolute-path >>key-file
"dh-file" get absolute-path >>dh-file
"key-password" get >>password ;
: <gemini-server> ( directory port -- server )
utf8 gemini-server new-threaded-server
<gemini-secure-config> >>secure-config
f >>insecure
swap >>secure
swap resolve-symlinks >>serving-directory
"gemini.server" >>name
binary >>encoding
5 minutes >>timeout ;
: gemini-server-main ( -- )
command-line get ?first "." or
1965 <gemini-server> start-server wait-for-server ;
MAIN: gemini-server-main
! ./factor -key-file=cert.pem -dh-file=dh2048.pem -key-password=password -run=gemini.server