https server
This commit is contained in:
@ -3,17 +3,61 @@ package http
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ServeCertificate() {
|
||||
func ServeHttp() {
|
||||
waitForCertificate()
|
||||
|
||||
go http.ListenAndServe(":80", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL
|
||||
url.Host = r.Host
|
||||
url.Scheme = "https"
|
||||
http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
|
||||
}))
|
||||
|
||||
http.HandleFunc("/server.key", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
http.ServeFile(w, r, "/certs/server.key")
|
||||
http.ServeFile(w, r, "/certs/wildcard/server.key")
|
||||
})
|
||||
http.HandleFunc("/server.pem", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/x-x509-ca-cert")
|
||||
http.ServeFile(w, r, "/certs/server.pem")
|
||||
http.ServeFile(w, r, "/certs/wildcard/server.pem")
|
||||
})
|
||||
log.Printf("Serving cert files on :9229\n")
|
||||
http.ListenAndServe(":9229", nil)
|
||||
http.HandleFunc("/og.png", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "image/png; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
http.ServeFile(w, r, "./http/static/og.png")
|
||||
})
|
||||
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "image/x-icon; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
http.ServeFile(w, r, "./http/static/favicon.ico")
|
||||
})
|
||||
http.HandleFunc("/styles.css", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/css; charset=utf-8")
|
||||
http.ServeFile(w, r, "./http/static/styles.css")
|
||||
})
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
http.ServeFile(w, r, "./http/static/index.html")
|
||||
})
|
||||
log.Printf("Serving HTTPS server on :443\n")
|
||||
http.ListenAndServeTLS(":443", "/certs/root/server.pem", "/certs/root/server.key", nil)
|
||||
}
|
||||
|
||||
func waitForCertificate() {
|
||||
for {
|
||||
_, err := os.Stat("/certs/root/output.json")
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no such file or directory") {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
BIN
http/static/favicon.ico
Normal file
BIN
http/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
89
http/static/index.html
Normal file
89
http/static/index.html
Normal file
@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1" />
|
||||
<title>local-ip.sh</title>
|
||||
<meta name="description" content="local-ip.sh is a magic domain name that provides wildcard DNS for any IP address." />
|
||||
<meta name="author" content="Mokhtar Mial" />
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="googlebot" content="index,follow" />
|
||||
<meta property="twitter:title" content="local-ip.sh" />
|
||||
<meta property="twitter:description" content="local-ip.sh is a magic domain name that provides wildcard DNS for any IP address." />
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:site" content="https://local-ip.sh/" />
|
||||
<meta property="twitter:image" content="https://local-ip.sh/og.png" />
|
||||
<meta property="twitter:image:alt" content="og image" />
|
||||
<meta property="og:title" content="local-ip.sh" />
|
||||
<meta property="og:description" content="local-ip.sh is a magic domain name that provides wildcard DNS for any IP address." />
|
||||
<meta property="og:url" content="https://local-ip.sh/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="https://local-ip.sh/og.png" />
|
||||
<meta property="og:image:alt" content="og image" />
|
||||
<link rel="stylesheet" href="/styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<pre>
|
||||
_ _ _ _
|
||||
| | | | (_) | |
|
||||
| | ___ ___ __ _| | _ _ __ ___| |__
|
||||
| |/ _ \ / __/ _` | |_____| | '_ \ / __| '_ \
|
||||
| | (_) | (_| (_| | |_____| | |_) |\__ \ | | |
|
||||
|_|\___/ \___\__,_|_| |_| .__(_)___/_| |_|
|
||||
| |
|
||||
|_|
|
||||
</pre>
|
||||
</header>
|
||||
<main>
|
||||
<section>
|
||||
<header><strong>What is local-ip.sh?</strong></header>
|
||||
<main>
|
||||
<article>local-ip.sh is a magic domain name that provides wildcard DNS for any IP address. It is heavily
|
||||
inspired by <a href="http://local-ip.co">local-ip.co</a>, <a
|
||||
href="https://sslip.io">sslip.io</a>, and <a href="https://xip.io">xip.io</a>.</article>
|
||||
<article>Quick example, say your LAN IP address is <strong>192.168.1.10</strong>. Using
|
||||
local-ip.sh,<br /><br />
|
||||
<pre> <strong>192.168.1.10</strong>.local-ip.sh resolves to 192.168.1.10
|
||||
dots.<strong>192.168.1.10</strong>.local-ip.sh resolves to 192.168.1.10
|
||||
dashes.<strong>192-168-1-10</strong>.local-ip.sh resolves to 192.168.1.10</pre>
|
||||
</article>
|
||||
<article>...and so on. You can use these domains to access virtual hosts on your development web server
|
||||
from devices on your local network. No configuration required!</article>
|
||||
<article>The best part is, you can serve your content over HTTPS with our TLS certificate for
|
||||
<code>*.local-ip.sh</code>:<ul>
|
||||
<li><a href="/server.pem">server.pem</a></li>
|
||||
<li><a href="/server.key">server.key</a></li>
|
||||
</ul>Be aware that wildcard certificates are not recursive, meaning they don't match
|
||||
"sub-subdomains". <br />In our case, this certificate will only match subdomains of
|
||||
<code>local-ip.sh</code> such as <code>192-168-1-10.local-ip.sh</code> where dashes separate
|
||||
the numbers that make up the IP address.</article>
|
||||
</main>
|
||||
</section>
|
||||
<section>
|
||||
<header><strong>How does it work?</strong></header>
|
||||
<main>
|
||||
<article>
|
||||
local-ip.sh runs publicly a <a href="https://git.capsulecorp.dev/mokhtar/local-ip.sh">custom DNS server</a>.
|
||||
When your computer looks up a local-ip.sh domain, the local-ip.sh DNS server resolves to the IP address it extracts from the domain.
|
||||
</article>
|
||||
<article>
|
||||
The TLS certificate is obtained from Let's Encrypt and renewed up to a month before it expires.
|
||||
</article>
|
||||
</main>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="copyright">© 2024 <a href="https://www.mokhtar.dev">Mokhtar Mial</a></footer>
|
||||
<script>
|
||||
(function () {
|
||||
window.counterscale = {
|
||||
q: [["set", "siteId", "local-ip"], ["trackPageview"]],
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
<script id="counterscale-script" src="https://counterscale.m5r.workers.dev/tracker.js" defer=""></script>
|
||||
</body>
|
||||
|
||||
</html>
|
BIN
http/static/og.png
Normal file
BIN
http/static/og.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
107
http/static/styles.css
Normal file
107
http/static/styles.css
Normal file
@ -0,0 +1,107 @@
|
||||
html {
|
||||
background: #111;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #728ea7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-inline: auto;
|
||||
padding-left: 1.5em;
|
||||
padding-right: 1.5em;
|
||||
width: min(100%, 41.5rem);
|
||||
margin-top: 50px;
|
||||
margin-bottom: 50px;
|
||||
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
header {
|
||||
color: #7aa6da;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
header > pre {
|
||||
margin: 1rem auto;
|
||||
}
|
||||
|
||||
main a {
|
||||
color: #728ea7;
|
||||
}
|
||||
|
||||
main a:hover {
|
||||
background: #7aa6da;
|
||||
color: #111;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
section:nth-child(n + 2) {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
section > main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 2rem;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27, 31, 35, 0.95);
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
header span.dim {
|
||||
color: #728ea7;
|
||||
}
|
||||
|
||||
section strong {
|
||||
color: #7aa6da;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin: 5rem auto 0;
|
||||
color: #556a7d;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: #728ea7;
|
||||
}
|
||||
|
||||
div.cursor {
|
||||
display: inline-block;
|
||||
background: #111;
|
||||
margin-left: 1px;
|
||||
margin-right: -1px;
|
||||
animation: blink 2s linear 0s infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% {
|
||||
background: #7aa6da;
|
||||
}
|
||||
47% {
|
||||
background: #728ea7;
|
||||
}
|
||||
50% {
|
||||
background: #111;
|
||||
}
|
||||
97% {
|
||||
background: #111;
|
||||
}
|
||||
100% {
|
||||
background: #728ea7;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user