惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
WordPress大学
WordPress大学
博客园 - 聂微东
博客园 - 【当耐特】
小众软件
小众软件
V
V2EX
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell
月光博客
月光博客
宝玉的分享
宝玉的分享
有赞技术团队
有赞技术团队
人人都是产品经理
人人都是产品经理
N
News and Events Feed by Topic
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Cloudflare Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 司徒正美
P
Privacy & Cybersecurity Law Blog
The Hacker News
The Hacker News
AI
AI
O
OpenAI News
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Jina AI
Jina AI
博客园_首页
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
IT之家
IT之家
V
Vulnerabilities – Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google Online Security Blog
Google Online Security Blog
G
GRAHAM CLULEY
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hacker News: Ask HN
Hacker News: Ask HN
P
Palo Alto Networks Blog
S
SegmentFault 最新的问题
T
The Exploit Database - CXSecurity.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News and Events Feed by Topic
V
Visual Studio Blog
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志

Rc-2020 on Julia Evans

Day 57: Trying to set up GitHub Actions Day 56: A little WebAssembly Day 53: a little nginx, IPv6, and wireguard Day 52: testing how many Firecracker VMs I can run Day 51: Fixed my logging and made a couple of puzzles Day 50: Building some tarballs for puzzles, and trying to make a kernel boot faster Day 49: making the VMs boot faster Day 48: Another Go program, and a little vim configuration Day 47: Using device mapper to manage Firecracker images Day 46: debugging an iptables problem Day 44: Building my VMs with Docker Day 43: Building VM images Day 42: Writing a Go program to manage Firecracker VMs Day 41: Trying to understand what a bridge is Day 40: screen flickering & a talk about containers Day 39: Customizing gotty's terminal Day 37: A new laptop and a little Vue Day 35: Launching my VMs more reliably Day 34: Learning about qemu Day 33: pairing is magic and beautiful git diffs Day 32: A Rails model that doesn't use the database with ActiveHash Day 24: a short talk about blogging myths, and a debugging tip Day 23: a little Rails testing Day 22: getting OAuth to work in Rails Day 21: wrangling systemd & setting up git deploys to a VM Day 19: Clustering faces (poorly) using an autoencoder Day 20: trying to figure out how Google Cloud IAM works Day 18: an answer to an autoencoder question Day 17: trying to wrap my head around autoencoders Day 13: BPTT, and debugging why a model isn't training is hard Day 11: learning about learning rates Day 10: Training an RNN to count to three Day 9: Generating a lot of nonsense with an RNN Day 8: Start with something that works Day 5: drawing lots of faces with sketch-rnn Day 3: an infinitely tall fridge Day 2: Rails associations & dragging divs around Day 1: a confusing Rails error message I'm doing another Recurse Center batch!
Day 38: Modifying gotty to serve many different terminal applications at once
Julia Evans · 2021-01-14 · via Rc-2020 on Julia Evans

In the last post I talked about a problem I was having with gotty.

I’m going to recap the problem, and then we’ll talk about what I did about it! Basically I paired on the problem with Chetan (another Recurser) and we solved it in a really satisfying way and it was way easier than I thought it would be.

Everything from this post is in a github repository here: https://github.com/jvns/multi-gotty/

what’s gotty?

gotty is a Go webserver that lets you put your terminal on a website. So for example if you run gotty top, it’ll start a webserver on port 8080 where it shows the output of top.

Or if you run gotty -w bash, it’ll start bash and start a webserver where anyone can type in commands and run them in your shell.

In my puzzle game thing I have people ssh to some virtual machines I’ve setup. I’ve been using gotty to give them a terminal in the browser.

the problem: a really fragile setup

My problem was that I needed to manage a bunch of different SSH connections (one per puzzle that a person has open), and gotty only supports one session at a time.

So I set something up where I ran a bunch of different gotty processes on different ports, and then stored a mapping of which session mapped to which port, and wrote a small go proxy server that proxied /proxy/SESSION_ID to the right port number on the backend.

There were 3 different pieces in this setup:

  • the gotty processes (potentially lots of them)
  • the other go server that proxied connections to those processes
  • the Rails server, which was responsible for starting the gotty processes on the right ports and telling the go server which ports they were running on

the idea: modify gotty so that it can manage multiple websocket connections

I wanted a much simpler setup where I could just go to http://mysite.com/terminal_session/SOME_ID/ and have the right SSH connection automatically set up.

I’m having some trouble explaining what I want the code to do in English so I’ll just show you some code, because it wasn’t really that much code.

the code: really simple!

I was originally worried that it would be really complicated to modify gotty to handle multiple websocket connections but actually it was very straightforward! gotty had just 3 HTTP handler functions (handleAuthToken, handleWS, and a statics handler), so all we needed to do was call those in a slightly different way.

Here’s what the new HTTP handler we wrote looks like. It basically just has an if statement which calls app.handleWS with a custom command if the path ends in /ws. I think there might be a more idiomatic Go way to do this but I don’t know what it is yet. You can see it in context here

func (app *App) handleRequest(w http.ResponseWriter, r *http.Request) {
	staticHandler := http.FileServer(
		&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"},
	)
	path := r.URL.Path
	parts := strings.Split(path, "/")
	// TODO: this panics if the path doesn't have enough stuff in it
	// TODO: actually match on /proxy and don't do this strings.Split thing
	prefix := strings.Join(parts[:3], "/")
	if strings.HasSuffix(path, "/auth_token.js") {
		app.handleAuthToken(w, r)
	} else if strings.HasSuffix(path, "/ws") {
		id := parts[2]
		mapping := app.readMapping()
		if command, ok := mapping[id]; ok {
			app.handleWS(command, w, r)
		}
	} else {
		http.StripPrefix(prefix, staticHandler).ServeHTTP(w, r)
	}
}

The parsing code for the path here is still really bad but I’ll fix it at some point.

The only real other code we had to write was this readMapping function which maps an ID from the URL to a command to run. This is just making an HTTP request to a server (my Rails app) and decoding some JSON:

func (app *App) readMapping() map[string][]string {
	resp, err := http.Get(app.commandServer)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	var mapping map[string][]string
	json.Unmarshal(body, &mapping)
	return mapping
}

http.ResponseWriter and http.Request are great

The real star here is the Go http interfaces. Because all Go HTTP handlers take a http.ResponseWriter and a http.Request, it’s super easy to wrap other HTTP request handlers and make them behave slightly differently.

I’d forgotten how it worked but it was pretty easy to pick up again.

I couldn’t use gotty as a library

We forked gotty instead of using it as a library because the functions we needed (handleWS and handleAuthToken) were private, you can tell because they’re lowercase and in Go the way you know if a function is private or public is based on whether it starts with an uppercase or lowercase letter.

pairing is magic

I paired on this with Chetan and it was SO MUCH easier to do with someone else than on my own. I really thought this would be super hard and it wasn’t – it was really helpful to talk it through with another person and we got the main functionality working in just 1 hour!

the new code works WAY BETTER than my old setup

Having everything coordinated in 1 simple Go program instead of having 3 different moving pieces to coordinate works SO MUCH BETTER. I tried it out and basically just worked immediately and reliably instead of constantly being flaky and failing like my old setup.

here’s the code

I put the code on Github here: https://github.com/jvns/multi-gotty/. It has some problems (like the bad path parsing code I mentioned, and it doesn’t let you change the accepted Origin: headers yet), but it does do what I wanted to do!

Here’s the complete diff of all the code we changed / deleted in gotty to make this work. We deleted most of gotty’s command line flags and support for a bunch of things like TLS because I didn’t need them and I didn’t think anyone else would want to use it.