Gemini Capsule on Pico W

I received my Pico W long time ago but couldn't find the time to finish this.

I decided to do this in Python (using MicroPython) first, then translate to C and see how things can be improved.

It's very simple and very basic, without concurrency. Like my ESP32 setup, it uses Duck DNS. The key and the certificate are at /pyboard/{key,cert}.der and this is /pyboard/boot.py:

	import ussl
	import socket
	import network
	import _thread
	import machine
	import urequests

	SSID = 'AAA'
	PASSPHRASE = 'BBB'
	DOMAINS = 'CCC'
	TOKEN = 'DDD'
	PORT = 1965

	keyfile = open("key.der", "rb").read()
	certfile = open("cert.der", "rb").read()
	 
	def read_request(sc):
		buf = sc.read(len('gemini://a'))
		while not buf.endswith(b'\r') and len(buf) < 64:
			b = sc.read(1)
			buf += b
		if not buf.endswith(b'\r'):
			raise Exception('Truncated request')

		url = buf[:-1].decode('utf-8')
		_, _, _, path = url.split("/", 3)
		path = path.lstrip('/')
		print(url)

		if len(path) == 0 or path == '/':
			path = 'index.gmi'
		elif path == 'key.der' or path == 'cert.der' or path == 'main.py':
			sc.write(f'40 Forbidden\r\n')
			raise Exception(f"Forbidden path: {path}")
		
		return path

	def handle_request(c):
		try:
			sc = ussl.wrap_socket(c, server_side=True, key=keyfile, cert=certfile)
			try:
				path = read_request(sc)

				sc.write(f'20 text/gemini\r\n')

				with open(path, "rb") as f:
					while True:
						chunk = f.read(128)
						if len(chunk) == 0:
							break
						sc.write(chunk)
			finally:
				sc.close()
		finally:
			c.close()

	wlan = network.WLAN(network.STA_IF)
	wlan.active(True)
	wlan.connect(SSID, PASSPHRASE)

	s = socket.socket()
	s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
	s.bind(('0.0.0.0', PORT))
	s.listen(1)
	led = machine.Pin('LED', machine.Pin.OUT)

	print(f"Local address: {wlan.ifconfig()[0]}")
	print('Updating public address')
	urequests.get(f"https://www.duckdns.org/update?domains={DOMAINS}&token={TOKEN}")

	print('Ready')
	while True:
		try:
			print('Waiting for request')
			c, _ = s.accept()
		except Exception as e:
			print(e)
			continue

		try:
			print('Handling request')
			c.settimeout(10)
			led.value(1)
			try:
				handle_request(c)
			finally:
				led.value(0)
		except Exception as e:
			print(e)
			c.close()

It doesn't send close_notify, something I could fix in my ESP32 Gemini server written in C by forcing mbedtls_ssl_close_notify((mbedtls_ssl_context*)tls), although all other uses of mbedlts are wrapped nicely with the ESP-TLS API. I hope I can do something similar in the Pico without having to patch ussl and rebuild MicroPython.

Duck DNS

behind-the-scenes.gmi

self-hosting-on-esp32.gmi