initial commit
This commit is contained in:
commit
d9e5170676
11 changed files with 968 additions and 0 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.gitattributes export-ignore
|
||||||
|
.gitignore export-ignore
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
98
Cargo.lock
generated
Normal file
98
Cargo.lock
generated
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.70"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"foreign-types",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"openssl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sheldon_director"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"openssl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "sheldon_director"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Klockenschooster <slartibartfast@klockenschooster.de>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
openssl = { version = "0.10" }
|
||||||
|
|
20
LICENSE.md
Normal file
20
LICENSE.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (c) 2020 Jan Wolff
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied warranty. In
|
||||||
|
no event will the authors be held liable for any damages arising from the use
|
||||||
|
of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose, including
|
||||||
|
commercial applications, and to alter it and redistribute it freely, subject to
|
||||||
|
the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not claim
|
||||||
|
that you wrote the original software. If you use this software in a product,
|
||||||
|
an acknowledgment in the product documentation would be appreciated but is
|
||||||
|
not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Sheldon Director
|
||||||
|
================
|
||||||
|
|
||||||
|
A Gemini Server written in Rust.
|
||||||
|
|
||||||
|
Why "Sheldon Director"?
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Because it is the real name of that villain in _Kim Possible_ who went by the
|
||||||
|
pseudonym "Gemini". (https://kimpossible.fandom.com/wiki/Gemini)
|
32
doc/cert.pem
Normal file
32
doc/cert.pem
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFlTCCA32gAwIBAgIUPqRiFp5Gc1b2Frbab/WcQ4Xdg74wDQYJKoZIhvcNAQEL
|
||||||
|
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||||
|
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MCAX
|
||||||
|
DTIwMDUxNDE4NDI0NVoYDzIxMjAwNDIwMTg0MjQ1WjBZMQswCQYDVQQGEwJBVTET
|
||||||
|
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
|
||||||
|
dHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4IC
|
||||||
|
DwAwggIKAoICAQCqDaso0s8+Rt9fILlF7koLcYJCjs+MjB0kpS7itdJj1YXW6a5l
|
||||||
|
zIRP9bMSSftVmHGR1qFo6suKbVEayCkTrw3PU6w7iW7CMiRK6XL1Ix4B5BYEymKx
|
||||||
|
ihA1geRQZkKL8UHfudnpq7+yTqwKXdOu4KrRUbMdIiOlaIhhK4/DZk8gxmZpiv81
|
||||||
|
xqohtyVsr7Z8yKkSoX7TySPvn3qx83BgeoGSqe/pX4quKVKj8ylkfdyKlaVPLGvS
|
||||||
|
ktTfr2rUYtRz1+xOEEYxUdaZBRZ2/G1QaeELWimwPTAmg9KWCfA0/6TWMDvh2Btq
|
||||||
|
J0qJtcu43QVFf4AxoUQ/S/Mf4Zmc/3gfOcEHrjnNube0xaUfMdjErhwPmXKc0E78
|
||||||
|
hd9jlkJaICdGSEC19DZBt8LinyTMr6+2E/YyD+bl+LUL8uI7m9zFgJYlLXUeZvab
|
||||||
|
PvcxUq72pP6/j+9fGlT2JXSZ6bIDXyDbgz0QmZ5Rh+q5fwSAtfkSE16cx87aOM48
|
||||||
|
iI98c4joN9FXAVwNT53ga/dxwCqQZH4Sd024DEeo2Gk5dWyPTUnENcl4vW1Twa70
|
||||||
|
1gY60YqHw5UgH9DnBN+8yWjFHlbL4aZvbLdTsQOCkHMxoTt5vGP2e5XHh0Etk1oK
|
||||||
|
5E7FtXOFJ125PcPZ9BlyyyaKxR9ykpMYNtHJjScQPTlMhku/8aK3sb3eRwIDAQAB
|
||||||
|
o1MwUTAdBgNVHQ4EFgQUWrJXR3N6gnEkBQHACPj0Db+TtoAwHwYDVR0jBBgwFoAU
|
||||||
|
WrJXR3N6gnEkBQHACPj0Db+TtoAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
|
||||||
|
AQsFAAOCAgEAKLhG4+kNvoVCyByrcSUU9rhUZxn1ezAQJcUO3ETZIE1lU1Z46rMk
|
||||||
|
9he4+sPTDxEEoynL8wseAStn+CVVd8Leer5QZEWqwaM+LkcjicYr+U8CNQ1p28Gy
|
||||||
|
oiSH0rK7RzkhRDwmnMYAx9skpem/nLj21RH4gPBUYaW5mU05O4F3vZvWZfwfGJi8
|
||||||
|
MSvPOVUZVUIJZFqNAECKxGbzneO3iKLETnDdAnw2VgLwTcvLfkbtBxbNwEgTgV8+
|
||||||
|
sWbmgTlPX1ToRdL2Gz5pp0hz6B94H0HoUO4yH2Afzb5O6+sKxLoIBq7S8PBURP0A
|
||||||
|
F+mtnU/VWJBNf52KRVHLWApdtieaqHIrHt9qbWsJ2bx+P3z5SkjOJbLDo06pAp4h
|
||||||
|
uYZuRiykUW+bMfv5Ec+qKUJVidG2J7YKpq3ghzdmebnZ+d/D/5KkEl22EnXw48Mj
|
||||||
|
r+ynJlq+EK5jQc0Y3CRt/ggjrqvmtE5Mqb6ltoVG4/yNa8z0tjza/B17tmYaCC/W
|
||||||
|
L7I/2KY7tKUuOk9JjWbTikTpGccn496QZlcpNWxiiK3qDOeah1iRLBqIm6HQeg5B
|
||||||
|
Vz3YWRir/L2QbkDFy6Lxy/28C0hbEljYV5rvuKjx5a4o8nUUcOPhiDTrpCpGKLZ8
|
||||||
|
uQ/9hYrLWWDYAREeNyuUHyyDyWGObqBr8W/vbQder2t8cSUZBhHR69k=
|
||||||
|
-----END CERTIFICATE-----
|
52
doc/key.pem
Normal file
52
doc/key.pem
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
Qdcfj82isy1JR/mZp2t+sasS1DGxNg==
|
||||||
|
-----END PRIVATE KEY-----
|
649
doc/spec-spec.txt
Normal file
649
doc/spec-spec.txt
Normal file
|
@ -0,0 +1,649 @@
|
||||||
|
-----------------------------
|
||||||
|
Project Gemini
|
||||||
|
"Speculative specification"
|
||||||
|
v0.11.0, March 1st 2020
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
This is an increasingly less rough sketch of an actual spec for
|
||||||
|
Project Gemini. Although not finalised yet, further changes to the
|
||||||
|
specification are likely to be relatively small. You can write code
|
||||||
|
to this pseudo-specification and be confident that it probably won't
|
||||||
|
become totally non-functional due to massive changes next week, but
|
||||||
|
you are still urged to keep an eye on ongoing development of the
|
||||||
|
protocol and make changes as required.
|
||||||
|
|
||||||
|
This is provided mostly so that people can quickly get up to speed on
|
||||||
|
what I'm thinking without having to read lots and lots of old phlog
|
||||||
|
posts and keep notes.
|
||||||
|
|
||||||
|
Feedback on any part of this is extremely welcome, please email
|
||||||
|
solderpunk@sdf.org.
|
||||||
|
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
1. Overview
|
||||||
|
|
||||||
|
Gemini is a client-server protocol featuring request-response
|
||||||
|
transactions, broadly similar to gopher or HTTP. Connections are
|
||||||
|
closed at the end of a single transaction and cannot be reused. When
|
||||||
|
Gemini is served over TCP/IP, servers should listen on port 1965 (the
|
||||||
|
first manned Gemini mission, Gemini 3, flew in March '65). This is an
|
||||||
|
unprivileged port, so it's very easy to run a server as a "nobody"
|
||||||
|
user, even if e.g. the server is written in Go and so can't drop
|
||||||
|
privileges in the traditional fashion.
|
||||||
|
|
||||||
|
1.1 Gemini transactions
|
||||||
|
|
||||||
|
There is one kind of Gemini transaction, roughly equivalent to a
|
||||||
|
gopher request or a HTTP "GET" request. Transactions happen as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
C: Opens connection
|
||||||
|
S: Accepts connection
|
||||||
|
C/S: Complete TLS handshake (see 1.4)
|
||||||
|
C: Validates server certificate (see 1.4.2)
|
||||||
|
C: Sends request (one CRLF terminated line) (see 1.2)
|
||||||
|
S: Sends response header (one CRFL terminated line), closes connection
|
||||||
|
under non-success conditions (see 1.3.1, 1.3.2)
|
||||||
|
S: Sends response body (text or binary data) (see 1.3.3)
|
||||||
|
S: Closes connection
|
||||||
|
C: Handles response (see 1.3.4)
|
||||||
|
|
||||||
|
1.2 Gemini requests
|
||||||
|
|
||||||
|
Gemini requests are a single CRLF-terminated line with the following
|
||||||
|
structure:
|
||||||
|
|
||||||
|
<URL><CR><LF>
|
||||||
|
|
||||||
|
<URL> is a UTF-8 encoded absolute URL, of maximum length 1024 bytes.
|
||||||
|
If the scheme of the URL is not specified, a scheme of gemini:// is
|
||||||
|
implied.
|
||||||
|
|
||||||
|
Sending an absolute URL instead of only a path or selector is
|
||||||
|
effectively equivalent to building in a HTTP "Host" header. It
|
||||||
|
permits virtual hosting of multiple Gemini domains on the same IP
|
||||||
|
address. It also allows servers to optionally act as proxies.
|
||||||
|
Including schemes other than gemini:// in requests allows servers to
|
||||||
|
optionally act as protocol-translating gateways to e.g. fetch gopher
|
||||||
|
resources over Gemini. Proxying is optional and the vast majority of
|
||||||
|
servers are expected to only respond to requests for resources at
|
||||||
|
their own domain(s).
|
||||||
|
|
||||||
|
1.3 Responses
|
||||||
|
|
||||||
|
Gemini response consist of a single CRLF-terminated header line,
|
||||||
|
optionally followed by a response body.
|
||||||
|
|
||||||
|
1.3.1 Response headers
|
||||||
|
|
||||||
|
Gemini response headers look like this:
|
||||||
|
|
||||||
|
<STATUS><whitespace><META><CR><LF>
|
||||||
|
|
||||||
|
<STATUS> is a two-digit numeric status code, as described below in
|
||||||
|
1.3.2 and in Appendix 1.
|
||||||
|
|
||||||
|
<whitespace> is any non-zero number of consecutive spaces or tabs.
|
||||||
|
|
||||||
|
<META> is a UTF-8 encoded string of maximum length 1024, whose meaning
|
||||||
|
is <STATUS> dependent.
|
||||||
|
|
||||||
|
If <STATUS> does not belong to the "SUCCESS" range of codes, then the
|
||||||
|
server MUST close the connection after sending the header and MUST NOT
|
||||||
|
send a response body.
|
||||||
|
|
||||||
|
If a server sends a <STATUS> which is not a two-digit number or a
|
||||||
|
<META> which exceeds 1024, the client SHOULD close the connection and
|
||||||
|
disregard the response header, informing the user of an error.
|
||||||
|
|
||||||
|
1.3.2 Status codes
|
||||||
|
|
||||||
|
Gemini uses two-digit numeric status codes. Related status codes share
|
||||||
|
the same first digit. Importantly, the first digit of Gemini status
|
||||||
|
codes do not group codes into vague categories like "client error" and
|
||||||
|
"server error" as per HTTP. Instead, the first digit alone provides
|
||||||
|
enough information for a client to determine how to handle the
|
||||||
|
response. By design, it is possible to write a simple but feature
|
||||||
|
complete client which only looks at the first digit. The second digit
|
||||||
|
provides more fine-grained information, for unambiguous server logging,
|
||||||
|
to allow writing comfier interactive clients which provide a slightly
|
||||||
|
more streamlined user interface, and to allow writing more robust and
|
||||||
|
intelligent automated clients like content aggregators, search engine
|
||||||
|
crawlers, etc.
|
||||||
|
|
||||||
|
The first digit of a response code unambiguously places the response
|
||||||
|
into one of six categories, which define the semantics of the <META>
|
||||||
|
line.
|
||||||
|
|
||||||
|
1 INPUT
|
||||||
|
|
||||||
|
The requested resource accepts a line of textual user input.
|
||||||
|
The <META> line is a prompt which should be displayed to the
|
||||||
|
user. The same resource should then be requested again with
|
||||||
|
the user's input included as a query component. Queries are
|
||||||
|
included in requests as per the usual generic URL definition
|
||||||
|
in RFC3986, i.e. separated from the path by a ?. There is no
|
||||||
|
response body.
|
||||||
|
|
||||||
|
2 SUCCESS
|
||||||
|
|
||||||
|
The request was handled successfully and a response body will
|
||||||
|
follow the response header. The <META> line is a MIME media
|
||||||
|
type which applies to the response body.
|
||||||
|
|
||||||
|
3 REDIRECT
|
||||||
|
|
||||||
|
The server is redirecting the client to a new location for the
|
||||||
|
requested resource. There is no response body. The header
|
||||||
|
text is a new URL for the requested resource. The URL may be
|
||||||
|
absolute or relative. The redirect should be considered
|
||||||
|
temporary, i.e. clients should continue to request the
|
||||||
|
resource at the original address and should not performance
|
||||||
|
convenience actions like automatically updating bookmarks.
|
||||||
|
There is no response body.
|
||||||
|
|
||||||
|
4 TEMPORARY FAILURE
|
||||||
|
|
||||||
|
The request has failed. There is no response body. The
|
||||||
|
nature of the failure is temporary, i.e. an identical request
|
||||||
|
MAY succeed in the future. The contents of <META> may provide
|
||||||
|
additional information on the failure, and should be displayed
|
||||||
|
to human users.
|
||||||
|
|
||||||
|
5 PERMANENT FAILURE
|
||||||
|
|
||||||
|
The request has failed. There is no response body. The
|
||||||
|
nature of the failure is permanent, i.e. identical future
|
||||||
|
requests will reliably fail for the same reason. The contents
|
||||||
|
of <META> may provide additional information on the failure,
|
||||||
|
and should be displayed to human users. Automatic clients
|
||||||
|
such as aggregators or indexing crawlers should should not
|
||||||
|
repeat this request.
|
||||||
|
|
||||||
|
6 CLIENT CERTIFICATE REQUIRED
|
||||||
|
|
||||||
|
The requested resource requires client-certificate
|
||||||
|
authentication to access. If the request was made without a
|
||||||
|
certificate, it should be repeated with one. If the request
|
||||||
|
was made with a certificate, the server did not accept it and
|
||||||
|
the request should be repeated with a different certificate.
|
||||||
|
The contents of <META> may provide additional information on
|
||||||
|
certificate requirements or the reason a certificate was
|
||||||
|
rejected.
|
||||||
|
|
||||||
|
Note that for basic interactive clients for human use, errors 4 and 5
|
||||||
|
may be effectively handled identically, by simply displaying the
|
||||||
|
contents of <META> under a heading of "ERROR". The
|
||||||
|
temporary/permanent error distinction is primarily relevant to
|
||||||
|
well-behaving automated clients. Basic clients may also choose not to
|
||||||
|
support client-certificate authentication, in which case only four
|
||||||
|
distinct status handling routines are required (for statuses beginning
|
||||||
|
with 1, 2, 3 or a combined 4-or-5).
|
||||||
|
|
||||||
|
The full two-digit system is detailed in Appendix 1. Note that for
|
||||||
|
each of the six valid first digits, a code with a second digit of zero
|
||||||
|
corresponds is a generic status of that kind with no special
|
||||||
|
semantics. This means that basic servers without any advanced
|
||||||
|
functionality need only be able to return codes of 10, 20, 30, 40 or
|
||||||
|
50.
|
||||||
|
|
||||||
|
The Gemini status code system has been carefully designed so that the
|
||||||
|
increased power (and correspondingly increased complexity) of the
|
||||||
|
second digits is entirely "opt-in" on the part of both servers and
|
||||||
|
clients.
|
||||||
|
|
||||||
|
1.3.3 Response bodies
|
||||||
|
|
||||||
|
Response bodies are just raw content, text or binary, ala gopher.
|
||||||
|
There is no support for compression, chunking or any other kind of
|
||||||
|
content or transfer encoding. The server closes the connection after
|
||||||
|
the final byte, there is no "end of response" signal like gopher's
|
||||||
|
lonely dot.
|
||||||
|
|
||||||
|
Response bodies only accompany responses whose header indicates a
|
||||||
|
SUCCESS status (i.e. a status code whose first digit is 2). For such
|
||||||
|
responses, <META> is a MIME media type as defined in RFC 2046.
|
||||||
|
|
||||||
|
If a MIME type begins with "text/" and no charset is explicitly given,
|
||||||
|
the charset should be assumed to be UTF-8. Compliant clients MUST
|
||||||
|
support UTF-8-encoded text/* responses. Clients MAY optionally
|
||||||
|
support other encodings. Clients receiving a response in a charset
|
||||||
|
they cannot decode SHOULD gracefully inform the user what happened
|
||||||
|
instead of displaying garbage.
|
||||||
|
|
||||||
|
If <META> is an empty string, the MIME type MUST default to
|
||||||
|
"text/gemini; charset=utf-8".
|
||||||
|
|
||||||
|
1.3.4 Response body handling
|
||||||
|
|
||||||
|
Response handling by clients should be informed by the provided MIME
|
||||||
|
type information. Gemini defines one MIME type of its own
|
||||||
|
(text/gemini) whose handling is discussed below in 1.3.5. In all
|
||||||
|
other cases, clients should do "something sensible" based on the MIME
|
||||||
|
type. Minimalistic clients might adopt a strategy of printing all
|
||||||
|
other text/* responses to the screen without formatting and saving
|
||||||
|
all non-text responses to the disk. Clients for unix systems may
|
||||||
|
consult /etc/mailcap to find installed programs for handling non-text
|
||||||
|
types.
|
||||||
|
|
||||||
|
1.3.5 text/gemini responses
|
||||||
|
|
||||||
|
1.3.5.1 Overview
|
||||||
|
|
||||||
|
In the same sense that HTML is the "native" response format of HTTP
|
||||||
|
and plain text is the native response format of gopher, Gemini defines
|
||||||
|
its own native response format - though of course, thanks to the
|
||||||
|
inclusion of a MIME type in the response header Gemini can be used to
|
||||||
|
serve plain text, rich text, HTML, Markdown, LaTeX, etc.
|
||||||
|
|
||||||
|
Response bodies of type "text/gemini" are a kind of lightweight
|
||||||
|
hypertext format, which takes inspiration from gophermaps and from
|
||||||
|
Markdown. The format permits richer typographic possibilities than
|
||||||
|
the plain text of Gopher, but remains extremely easy to parse. The
|
||||||
|
format is line-oriented, and a satisfactory rendering can be achieved
|
||||||
|
with a single pass of a document, processing each line independently.
|
||||||
|
As per gopher, links can only be displayed one per line, encouraging
|
||||||
|
neat, list-like structure.
|
||||||
|
|
||||||
|
Similar to how the two-digit Gemini status codes were designed so that
|
||||||
|
simple clients can function correctly while ignoring the second digit,
|
||||||
|
the text/gemini format has been designed so that simple clients can
|
||||||
|
ignore the more advanced features and still remain very usable.
|
||||||
|
|
||||||
|
1.3.5.2 Line-orientation
|
||||||
|
|
||||||
|
As mentioned, the text/gemini format is line-oriented. Each line of a
|
||||||
|
text/gemini document has a single "line type". It is possible to
|
||||||
|
unambiguously determine a line's type purely by inspecting its first
|
||||||
|
three characters. A line's type determines the manner in which it
|
||||||
|
should be presented to the user. Any details of presentation or
|
||||||
|
rendering associated with a particular line type are strictly limited
|
||||||
|
in scope to that individual line.
|
||||||
|
|
||||||
|
There are 6 different line types in total. However, a fully
|
||||||
|
functional and specification compliant Gemini client need only
|
||||||
|
recognise and handle 4 of them - these are the "core line types",
|
||||||
|
(see 1.3.5.3). Advanced clients can also handle the additional
|
||||||
|
"advanced line types" (see 1.3.5.4). Simple clients can treat all
|
||||||
|
advanced line types as one of the core line types and still offer an
|
||||||
|
adequate user experience.
|
||||||
|
|
||||||
|
1.3.5.3 Core line types
|
||||||
|
|
||||||
|
The four core line types are:
|
||||||
|
|
||||||
|
1.3.5.3.1 Text lines
|
||||||
|
|
||||||
|
Text lines are the most fundamenal line type - any line which does
|
||||||
|
not match the definition of another line type defined below defaults
|
||||||
|
to being a text line. The majority of lines in a typical text/gemini
|
||||||
|
document will be text lines.
|
||||||
|
|
||||||
|
Text lines should be presented to the user, after being wrapped to
|
||||||
|
the appropriate width for the client's viewport (see below). Text
|
||||||
|
lines may be presented to the user in a visually pleasing manner
|
||||||
|
for general reading, the precise meaning of which is at the
|
||||||
|
client's discretion. For example, variable width fonts may be used,
|
||||||
|
spacing may be normalised, with spaces between sentences being made
|
||||||
|
wider than spacing between words, and other such typographical
|
||||||
|
niceties may be applied. Clients may permit users to customise the
|
||||||
|
appearance of text lines by altering the font, font size, text and
|
||||||
|
background colour, etc. Authors should not expect to exercise any
|
||||||
|
control over the precise rendering of their text lines, only of their
|
||||||
|
actual textual content. Content such as ASCII art, computer source
|
||||||
|
code, etc. which may appear incorrectly when treated as such should
|
||||||
|
be enclosed beween preformatting toggle lines (see 1.3.5.3.3).
|
||||||
|
|
||||||
|
Blank lines are instances of text lines and have no special meaning.
|
||||||
|
They should be rendered individually as vertical blank space each
|
||||||
|
time they occur. In this way they are analogous to <br/> tags in HTML.
|
||||||
|
Consecutive blank lines should NOT be collapsed into a fewer blank
|
||||||
|
lines. Note also that consecutive non-blank text lines do not form
|
||||||
|
any kind of coherent unit or block such as a "paragraph": all text
|
||||||
|
lines are independent entities.
|
||||||
|
|
||||||
|
Text lines which are longer than can fit on a client's display device
|
||||||
|
SHOULD be "wrapped" to fit, i.e. long lines should be split (ideally
|
||||||
|
at whitespace or at hyphens) into multiple consecutive lines of a
|
||||||
|
device-appropriate width. This wrapping is applied to each line of
|
||||||
|
text independently. Multiple consecutive lines which are shorter
|
||||||
|
than the client's display device MUST NOT be combined into fewer,
|
||||||
|
longer lines.
|
||||||
|
|
||||||
|
In order to take full advantage of this method of text formatting,
|
||||||
|
authors of text/gemini content SHOULD avoid hard-wrapping to a
|
||||||
|
pecific fixed width, in contrast to the convention in Gopherspace
|
||||||
|
where text is typically wrapped at 80 characters or fewer. Instead,
|
||||||
|
text which should be displayed as a contiguous block should be written
|
||||||
|
as a single long line. Most text editors can be configured to
|
||||||
|
"soft-wrap", i.e. to write this kind of file while displaying the long
|
||||||
|
lines wrapped to fit the author's display device.
|
||||||
|
|
||||||
|
Authors who insist on hard-wrapping their content MUST be aware that
|
||||||
|
the content will display neatly on clients whose display device is as
|
||||||
|
wide as the hard-wrapped length or wider, but will appear with
|
||||||
|
irregular line widths on narrower clients.
|
||||||
|
|
||||||
|
1.3.5.3.2 Link lines
|
||||||
|
|
||||||
|
Lines beginning with the two characters "=>" are link lines, which
|
||||||
|
have the following syntax:
|
||||||
|
|
||||||
|
=>[<whitespace>]<URL>[<whitespace><USER-FRIENDLY LINK NAME>]<CR><LF>
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
* <whitespace> is any non-zero number of consecutive spaces or
|
||||||
|
tabs
|
||||||
|
* Square brackets indicate that the enclosed content is
|
||||||
|
optional.
|
||||||
|
* <URL> is a URL, which may be absolute or relative. If the URL does
|
||||||
|
not include a scheme, a scheme of gemini:// is implied.
|
||||||
|
|
||||||
|
All the following examples are valid link lines:
|
||||||
|
|
||||||
|
=> gemini://example.org/
|
||||||
|
=> gemini://example.org/ An example link
|
||||||
|
=> gemini://example.org/foo Another example link at the same host
|
||||||
|
=>gemini://example.org/bar Yet another example link at the same host
|
||||||
|
=> foo/bar/baz.txt A relative link
|
||||||
|
=> gopher://example.org:70/1 A gopher link
|
||||||
|
|
||||||
|
Note that link URLs may have schemes other than gemini://. This means
|
||||||
|
that Gemini documents can simply and elegantly link to documents
|
||||||
|
hosted via other protocols, unlike gophermaps which can only link to
|
||||||
|
non-gopher content via a non-standard adaptation of the `h` item-type.
|
||||||
|
|
||||||
|
Clients can present links to users in whatever fashion the client
|
||||||
|
author wishes.
|
||||||
|
|
||||||
|
1.3.5.3.3 Preformatting toggle lines
|
||||||
|
|
||||||
|
Any line whose first three characters are "```" (i.e. three
|
||||||
|
consecutive back ticks with no leading whitespace) are preformatted
|
||||||
|
toggle lines. These lines should NOT be included in the rendered
|
||||||
|
output shown to the user. Instead, these lines toggle the parser
|
||||||
|
between preformatted mode being "on" or "off". Preformatted mode
|
||||||
|
should be "off" at the beginning of a document. The current status
|
||||||
|
of preformatted mode is the only internal state a parser is required
|
||||||
|
to maintain. When preformatted mode is "on", the usual rules for
|
||||||
|
identifying line types are suspended, and all lines should be
|
||||||
|
identified as preformatted text lines (see 1.3.5.3.4).
|
||||||
|
|
||||||
|
Preformatting toggle lines can be thought of as analogous to <pre> and
|
||||||
|
</pre> tags in HTML.
|
||||||
|
|
||||||
|
1.3.5.3.4 Preformatted text lines
|
||||||
|
|
||||||
|
Preformatted text lines should be presented to the user in a "neutral",
|
||||||
|
monowidth font without any alteration to whitespace or stylistic
|
||||||
|
enhancements. Graphical clients should use scrolling mechanisms to
|
||||||
|
present preformatted text lines which are longer than the client
|
||||||
|
viewport, in preference to wrapping. In displaying preformatted text
|
||||||
|
lines, clients should keep in mind applications like ASCII art and
|
||||||
|
computer source code: in particular, source code in langugaes with
|
||||||
|
significant whitespace (e.g. Python) should be able to be copied and
|
||||||
|
pasted from the client into a file and interpreted/compiled without
|
||||||
|
any problems arising from the client's manner of displaying them.
|
||||||
|
|
||||||
|
1.3.5.4 Advanced line types
|
||||||
|
|
||||||
|
The following advanced line types MAY be recognised by advanced
|
||||||
|
clients. Simple clients may treat them all as text lines as per
|
||||||
|
1.3.5.3.1 without any loss of essential function.
|
||||||
|
|
||||||
|
1.3.5.4.1 Heading lines
|
||||||
|
|
||||||
|
Lines beginning with "#" are heading lines. Heading lines consist of
|
||||||
|
one, two or three consecutive "#" characters, followed by optional
|
||||||
|
whitespace, followed by heading text. The number of # characters
|
||||||
|
indicates the "level" of header; #, ## and ### can be thought of as
|
||||||
|
analogous to <h1>, <h2> and <h3> in HTML.
|
||||||
|
|
||||||
|
Heading text should be presented to the user, and clients MAY use
|
||||||
|
special formatting, e.g. a larger or bold font, to indicate its
|
||||||
|
status as a header (simple clients may simply print the line,
|
||||||
|
including its leading #s, without any styling at all). However, the
|
||||||
|
main motivation for the definition of heading lines is not stylistic
|
||||||
|
but to provide a machine-readable representation of the internal
|
||||||
|
structure of the document. Advanced clients can use this information
|
||||||
|
to, e.g. display an automatically generated and hierarchically
|
||||||
|
formatted "table of contents" for a long document in a side-pane,
|
||||||
|
allowing users to easily jump to specific sections without excessive
|
||||||
|
scrolling. CMS-style tools automatically generating menus or
|
||||||
|
Atom/RSS feeds for a directory of text/gemini files can use first
|
||||||
|
heading in the file as a human-friendly title.
|
||||||
|
|
||||||
|
1.3.5.4.2 Unordered list items
|
||||||
|
|
||||||
|
Lines beginning with a * are unordered list items. This line type
|
||||||
|
exists purely for stylistic reasons. The * may be replaced in
|
||||||
|
advanced clients by a bullet symbol. Any text after the * character
|
||||||
|
should be presented to the user as if it were a text line, i.e.
|
||||||
|
wrapped to fit the viewport and formatted "nicely". Advanced clients
|
||||||
|
can take the space of the bullet symbol into account when wrapping
|
||||||
|
long list items to ensure that all lines of text corresponding to
|
||||||
|
the item are offset an equal distance from the left of the screen.
|
||||||
|
|
||||||
|
1.4 TLS
|
||||||
|
|
||||||
|
1.4.1 Version requirements
|
||||||
|
|
||||||
|
Use of TLS for Gemini transactions is mandatory.
|
||||||
|
|
||||||
|
Servers MUST use TLS version 1.2 or higher and SHOULD use TLS version
|
||||||
|
1.3 or higher. Clients MAY refuse to connect to servers using TLS
|
||||||
|
version 1.2 or lower.
|
||||||
|
|
||||||
|
1.4.2 Server certificate validation
|
||||||
|
|
||||||
|
Clients can validate TLS connections however they like (including not
|
||||||
|
at all) but the strongly RECOMMENDED approach is to implement a
|
||||||
|
lightweight "TOFU" certificate-pinning system which treats self-signed
|
||||||
|
certificates as first- class citizens. This greatly reduces TLS
|
||||||
|
overhead on the network (only one cert needs to be sent, not a whole
|
||||||
|
chain) and lowers the barrier to entry for setting up a Gemini site
|
||||||
|
(no need to pay a CA or setup a Let's Encrypt cron job, just make a
|
||||||
|
cert and go).
|
||||||
|
|
||||||
|
TOFU stands for "Trust On First Use" and is public-key security model
|
||||||
|
similar to that used by OpenSSH. The first time a Gemini client
|
||||||
|
connects to a server, it accepts whatever certificate it is presented.
|
||||||
|
That certificate's fingerprint and expiry date are saved in a
|
||||||
|
persistent database (like the .known_hosts file for SSH), associated
|
||||||
|
with the server's hostname. On all subsequent connections to that
|
||||||
|
hostname, the received certificate's fingerprint is computed and
|
||||||
|
compared to the one in the database. If the certificate is not the
|
||||||
|
one previously received, but the previous certificate's expiry date
|
||||||
|
has not passed, the user is shown a warning, analogous to the one web
|
||||||
|
browser users are shown when receiving a certificate without a
|
||||||
|
signature chain leading to a trusted CA.
|
||||||
|
|
||||||
|
This model is by no means perfect, but it is not awful and is vastly
|
||||||
|
superior to just accepting self-signed certificates unconditionally.
|
||||||
|
|
||||||
|
1.4.3 Transient client certificate sessions
|
||||||
|
|
||||||
|
Self-signed client certificates can optionally be used by Gemini
|
||||||
|
clients to permit servers to recognise subsequent requests from the
|
||||||
|
same client as belonging to a single "session". This facilitates
|
||||||
|
maintaining state in server-side applications. The functionality is
|
||||||
|
very similar to HTTP cookies, but with important differences.
|
||||||
|
|
||||||
|
Whereas HTTP cookies are originally created by a webserver and given
|
||||||
|
to a client via a response header, client certificates are created by
|
||||||
|
the client and given to the server as part of the TLS handshake:
|
||||||
|
Client certificates are fundamentally a client-centric means of
|
||||||
|
identification. Further, whereas HTTP cookies can be "resurrected" by
|
||||||
|
webservers after a client deletes them if the server recognises the
|
||||||
|
client by means of browser finger-printing or some other tracking
|
||||||
|
technology (leading to unkillable "super cookies"), if a client
|
||||||
|
deletes a client certificate and also the accompanying private key
|
||||||
|
(which the server has never seen), then the session ID can never be
|
||||||
|
recreated. Thus clients not only need to opt in to a certificate
|
||||||
|
session, but once they have done so they retain a guaranteed ability
|
||||||
|
to opt out of it at any point and the server cannot defeat this
|
||||||
|
ability.
|
||||||
|
|
||||||
|
Gemini requests typically will be made without a client certificate
|
||||||
|
being sent to the server. If a requested resource is part of a
|
||||||
|
server-side application which requires persistent state, a Gemini
|
||||||
|
server can return a status code of 61 (see Appendix 1 below) to
|
||||||
|
request that the client repeat the request with a "transient
|
||||||
|
certificate" to initiate a client certificate section.
|
||||||
|
|
||||||
|
Interactive clients for human users MUST inform users that such a
|
||||||
|
session has been requested and require the user to approve generation
|
||||||
|
of such a certificate. Transient certificates MUST NOT be generated
|
||||||
|
automatically.
|
||||||
|
|
||||||
|
Transient certificates are limited in scope to a particular domain.
|
||||||
|
Transient certificates MUST NOT be reused across different domains.
|
||||||
|
|
||||||
|
Transient certificates MUST be permanently deleted when the matching
|
||||||
|
server issues a response with a status code of 21 (see Appendix 1
|
||||||
|
below).
|
||||||
|
|
||||||
|
Transient certificates MUST be permanently deleted when the client
|
||||||
|
process terminates.
|
||||||
|
|
||||||
|
Transient certificates SHOULD be permanently deleted after not having
|
||||||
|
been used for more than 24 hours.
|
||||||
|
|
||||||
|
Appendix 1. Full two digit status codes
|
||||||
|
|
||||||
|
10 INPUT
|
||||||
|
|
||||||
|
As per definition of single-digit code 1 in 1.3.2.
|
||||||
|
|
||||||
|
20 SUCCESS
|
||||||
|
|
||||||
|
As per definition of single-digit code 2 in 1.3.2.
|
||||||
|
|
||||||
|
21 SUCCESS - END OF CLIENT CERTIFICATE SESSION
|
||||||
|
|
||||||
|
The request was handled successfully and a response body will
|
||||||
|
follow the response header. The <META> line is a MIME media
|
||||||
|
type which applies to the response body. In addition, the
|
||||||
|
server is signalling the end of a transient client certificate
|
||||||
|
session which was previously initiated with a status 61
|
||||||
|
response. The client should immediately and permanently
|
||||||
|
delete the certificate and accompanying private key which was
|
||||||
|
used in this request.
|
||||||
|
|
||||||
|
30 REDIRECT - TEMPORARY
|
||||||
|
|
||||||
|
As per definition of single-digit code 3 in 1.3.2.
|
||||||
|
|
||||||
|
31 REDIRECT - PERMANENT
|
||||||
|
|
||||||
|
The requested resource should be consistently requested from
|
||||||
|
the new URL provided in future. Tools like search engine
|
||||||
|
indexers or content aggregators should update their
|
||||||
|
configurations to avoid requesting the old URL, and end-user
|
||||||
|
clients may automatically update bookmarks, etc. Note that
|
||||||
|
clients which only pay attention to the initial digit of
|
||||||
|
status codes will treat this as a temporary redirect. They
|
||||||
|
will still end up at the right place, they just won't be able
|
||||||
|
to make use of the knowledge that this redirect is permanent,
|
||||||
|
so they'll pay a small performance penalty by having to follow
|
||||||
|
the redirect each time.
|
||||||
|
|
||||||
|
40 TEMPORARY FAILURE
|
||||||
|
|
||||||
|
As per definition of single-digit code 4 in 1.3.2.
|
||||||
|
|
||||||
|
41 SERVER UNAVAILABLE
|
||||||
|
|
||||||
|
The server is unavailable due to overload or maintenance.
|
||||||
|
(cf HTTP 503)
|
||||||
|
|
||||||
|
42 CGI ERROR
|
||||||
|
|
||||||
|
A CGI process, or similar system for generating dynamic
|
||||||
|
content, died unexpectedly or timed out.
|
||||||
|
|
||||||
|
43 PROXY ERROR
|
||||||
|
|
||||||
|
A proxy request failed because the server was unable to
|
||||||
|
successfully complete a transaction with the remote host.
|
||||||
|
(cf HTTP 502, 504)
|
||||||
|
|
||||||
|
44 SLOW DOWN
|
||||||
|
|
||||||
|
Rate limiting is in effect. <META> is an integer number of
|
||||||
|
seconds which the client must wait before another request is
|
||||||
|
made to this server.
|
||||||
|
(cf HTTP 429)
|
||||||
|
|
||||||
|
50 PERMANENT FAILURE
|
||||||
|
|
||||||
|
As per definition of single-digit code 5 in 1.3.2.
|
||||||
|
|
||||||
|
51 NOT FOUND
|
||||||
|
|
||||||
|
The requested resource could not be found but may be available
|
||||||
|
in the future.
|
||||||
|
(cf HTTP 404)
|
||||||
|
(struggling to remember this important status code? Easy:
|
||||||
|
you can't find things hidden at Area 51!)
|
||||||
|
|
||||||
|
52 GONE
|
||||||
|
|
||||||
|
The resource requested is no longer available and will not be
|
||||||
|
available again. Search engines and similar tools should
|
||||||
|
remove this resource from their indices. Content aggregators
|
||||||
|
should stop requesting the resource and convey to their human
|
||||||
|
users that the subscribed resource is gone.
|
||||||
|
(cf HTTP 410)
|
||||||
|
|
||||||
|
53 PROXY REQUEST REFUSED
|
||||||
|
|
||||||
|
The request was for a resource at a domain not served by the
|
||||||
|
server and the server does not accept proxy requests.
|
||||||
|
|
||||||
|
59 BAD REQUEST
|
||||||
|
|
||||||
|
The server was unable to parse the client's request,
|
||||||
|
presumably due to a malformed request.
|
||||||
|
(cf HTTP 400)
|
||||||
|
|
||||||
|
60 CLIENT CERTIFICATE REQUIRED
|
||||||
|
|
||||||
|
As per definition of single-digit code 6 in 1.3.2.
|
||||||
|
|
||||||
|
61 TRANSIENT CERTIFICATE REQUESTED
|
||||||
|
|
||||||
|
The server is requesting the initiation of a transient client
|
||||||
|
certificate session, as described in 1.4.3. The client should
|
||||||
|
ask the user if they want to accept this and, if so, generate
|
||||||
|
a disposable key/cert pair and re-request the resource using it.
|
||||||
|
The key/cert pair should be destroyed when the client quits,
|
||||||
|
or some reasonable time after it was last used (24 hours?
|
||||||
|
Less?)
|
||||||
|
|
||||||
|
62 AUTHORISED CERTIFICATE REQUIRED
|
||||||
|
|
||||||
|
This resource is protected and a client certificate which the
|
||||||
|
server accepts as valid must be used - a disposable key/cert
|
||||||
|
generated on the fly in response to this status is not
|
||||||
|
appropriate as the server will do something like compare the
|
||||||
|
certificate fingerprint against a white-list of allowed
|
||||||
|
certificates. The client should ask the user if they want to
|
||||||
|
use a pre-existing certificate from a stored "key chain".
|
||||||
|
|
||||||
|
63 CERTIFICATE NOT ACCEPTED
|
||||||
|
|
||||||
|
The supplied client certificate is not valid for accessing the
|
||||||
|
requested resource.
|
||||||
|
|
||||||
|
64 FUTURE CERTIFICATE REJECTED
|
||||||
|
|
||||||
|
The supplied client certificate was not accepted because its
|
||||||
|
validity start date is in the future.
|
||||||
|
|
||||||
|
65 EXPIRED CERTIFICATE REJECTED
|
||||||
|
|
||||||
|
The supplied client certificate was not accepted because its
|
||||||
|
expiry date has passed.
|
45
src/main.rs
Normal file
45
src/main.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use openssl::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype};
|
||||||
|
use std::net::{TcpListener, TcpStream, SocketAddr};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
mod response;
|
||||||
|
|
||||||
|
fn build_acceptor() -> std::sync::Arc<SslAcceptor> {
|
||||||
|
let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap();
|
||||||
|
acceptor.set_private_key_file("doc/key.pem", SslFiletype::PEM).unwrap();
|
||||||
|
acceptor.set_certificate_chain_file("doc/cert.pem").unwrap();
|
||||||
|
acceptor.check_private_key().unwrap();
|
||||||
|
return Arc::new(acceptor.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_client(mut stream: SslStream<TcpStream>) {
|
||||||
|
let mut buffer = [0; 1026];
|
||||||
|
stream.ssl_read(&mut buffer);
|
||||||
|
let request = String::from_utf8(buffer.to_vec()).unwrap();
|
||||||
|
|
||||||
|
let header = response::Header::new(response::Status::Success, "text/gemini".to_string());
|
||||||
|
let response = response::Response::new(header, [].to_vec());
|
||||||
|
|
||||||
|
stream.ssl_write(&response.format());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let addrs = [
|
||||||
|
SocketAddr::from(([127, 0, 0, 1], 1965)),
|
||||||
|
];
|
||||||
|
let acceptor = build_acceptor();
|
||||||
|
let listener = TcpListener::bind(&addrs[..]).unwrap();
|
||||||
|
|
||||||
|
for stream in listener.incoming() {
|
||||||
|
match stream {
|
||||||
|
Ok(stream) => {
|
||||||
|
let acceptor = acceptor.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
let stream = acceptor.accept(stream).unwrap();
|
||||||
|
handle_client(stream);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => { /* connection failed */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/response.rs
Normal file
50
src/response.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use std::vec;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Status {
|
||||||
|
Input = 1,
|
||||||
|
Success = 2,
|
||||||
|
Redirect = 3,
|
||||||
|
TemporaryFailure = 4,
|
||||||
|
PermanentFailure = 5,
|
||||||
|
ClientCertificateRequired = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Header {
|
||||||
|
status: Status,
|
||||||
|
meta: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Response {
|
||||||
|
header: Header,
|
||||||
|
data: std::vec::Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
pub fn new(status: Status, meta: String) -> Header {
|
||||||
|
return Header{
|
||||||
|
status: status,
|
||||||
|
meta: meta,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> String {
|
||||||
|
let status: u8 = self.status as u8;
|
||||||
|
return format!("{} {}\r\n", status * 10, self.meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn new(header: Header, data: std::vec::Vec<u8>) -> Response {
|
||||||
|
return Response{
|
||||||
|
header: header,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> std::vec::Vec<u8> {
|
||||||
|
let mut resp: std::vec::Vec<u8> = self.header.format().as_bytes().to_vec();
|
||||||
|
resp.extend(&self.data);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue