Merge pull request #11 from kixelated/simplify-connect
Simplify the fingerprint code.
This commit is contained in:
commit
261d6927c1
|
@ -17,4 +17,4 @@ go run filippo.io/mkcert -ecdsa -install
|
||||||
go run filippo.io/mkcert -ecdsa -days 10 -cert-file "$CRT" -key-file "$KEY" localhost 127.0.0.1 ::1
|
go run filippo.io/mkcert -ecdsa -days 10 -cert-file "$CRT" -key-file "$KEY" localhost 127.0.0.1 ::1
|
||||||
|
|
||||||
# Compute the sha256 fingerprint of the certificate for WebTransport
|
# Compute the sha256 fingerprint of the certificate for WebTransport
|
||||||
openssl x509 -in "$CRT" -outform der | openssl dgst -sha256 > ../player/src/transport/fingerprint.hex
|
openssl x509 -in "$CRT" -outform der | openssl dgst -sha256 > ../player/fingerprint.hex
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
.parcel-cache
|
.parcel-cache
|
||||||
dist
|
dist
|
||||||
|
fingerprint.hex
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
import Player from "./player"
|
import Player from "./player"
|
||||||
|
|
||||||
|
// @ts-ignore embed the certificate fingerprint using bundler
|
||||||
|
import fingerprintHex from 'bundle-text:../fingerprint.hex';
|
||||||
|
|
||||||
|
// Convert the hex to binary.
|
||||||
|
let fingerprint = [];
|
||||||
|
for (let c = 0; c < fingerprintHex.length-1; c += 2) {
|
||||||
|
fingerprint.push(parseInt(fingerprintHex.substring(c, c+2), 16));
|
||||||
|
}
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search)
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
|
||||||
const url = params.get("url") || "https://localhost:4443/watch"
|
const url = params.get("url") || "https://localhost:4443/watch"
|
||||||
|
@ -7,6 +16,10 @@ const canvas = document.querySelector<HTMLCanvasElement>("canvas#video")!
|
||||||
|
|
||||||
const player = new Player({
|
const player = new Player({
|
||||||
url: url,
|
url: url,
|
||||||
|
fingerprint: { // TODO remove when Chrome accepts the system CA
|
||||||
|
"algorithm": "sha-256",
|
||||||
|
"value": new Uint8Array(fingerprint),
|
||||||
|
},
|
||||||
canvas: canvas,
|
canvas: canvas,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Video from "../video"
|
||||||
|
|
||||||
export interface PlayerInit {
|
export interface PlayerInit {
|
||||||
url: string;
|
url: string;
|
||||||
|
fingerprint?: WebTransportHash; // the certificate fingerprint, temporarily needed for local development
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +21,8 @@ export default class Player {
|
||||||
|
|
||||||
this.transport = new Transport({
|
this.transport = new Transport({
|
||||||
url: props.url,
|
url: props.url,
|
||||||
|
fingerprint: props.fingerprint,
|
||||||
|
|
||||||
audio: this.audio,
|
audio: this.audio,
|
||||||
video: this.video,
|
video: this.video,
|
||||||
})
|
})
|
||||||
|
@ -29,10 +32,6 @@ export default class Player {
|
||||||
this.transport.close()
|
this.transport.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(url: string) {
|
|
||||||
await this.transport.connect(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
play() {
|
play() {
|
||||||
this.audio.play({})
|
this.audio.play({})
|
||||||
//this.video.play()
|
//this.video.play()
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
fingerprint.hex
|
|
|
@ -5,11 +5,10 @@ import * as MP4 from "../mp4"
|
||||||
import Audio from "../audio"
|
import Audio from "../audio"
|
||||||
import Video from "../video"
|
import Video from "../video"
|
||||||
|
|
||||||
// @ts-ignore bundler embeds data
|
|
||||||
import fingerprint from 'bundle-text:./fingerprint.hex';
|
|
||||||
|
|
||||||
export interface TransportInit {
|
export interface TransportInit {
|
||||||
url: string;
|
url: string;
|
||||||
|
fingerprint?: WebTransportHash; // the certificate fingerprint, temporarily needed for local development
|
||||||
|
|
||||||
audio: Audio;
|
audio: Audio;
|
||||||
video: Video;
|
video: Video;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +27,7 @@ export default class Transport {
|
||||||
this.audio = props.audio;
|
this.audio = props.audio;
|
||||||
this.video = props.video;
|
this.video = props.video;
|
||||||
|
|
||||||
this.quic = this.connect(props.url)
|
this.quic = this.connect(props)
|
||||||
|
|
||||||
// Create a unidirectional stream for all of our messages
|
// Create a unidirectional stream for all of our messages
|
||||||
this.api = this.quic.then((q) => {
|
this.api = this.quic.then((q) => {
|
||||||
|
@ -43,22 +42,16 @@ export default class Transport {
|
||||||
(await this.quic).close()
|
(await this.quic).close()
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(url: string): Promise<WebTransport> {
|
// Helper function to make creating a promise easier
|
||||||
// Convert the hex to binary.
|
private async connect(props: TransportInit): Promise<WebTransport> {
|
||||||
let hash = [];
|
|
||||||
for (let c = 0; c < fingerprint.length-1; c += 2) {
|
let options: WebTransportOptions = {};
|
||||||
hash.push(parseInt(fingerprint.substring(c, c+2), 16));
|
if (props.fingerprint) {
|
||||||
|
options.serverCertificateHashes = [ props.fingerprint ]
|
||||||
}
|
}
|
||||||
|
|
||||||
const quic = new WebTransport(url, {
|
const quic = new WebTransport(props.url, options)
|
||||||
"serverCertificateHashes": [{
|
|
||||||
"algorithm": "sha-256",
|
|
||||||
"value": new Uint8Array(hash),
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
|
|
||||||
await quic.ready
|
await quic.ready
|
||||||
|
|
||||||
return quic
|
return quic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/kixelated/invoker"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
inner http.Server
|
|
||||||
config Config
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Addr string
|
|
||||||
CertFile string
|
|
||||||
KeyFile string
|
|
||||||
Fingerprint string // the TLS certificate fingerprint
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(config Config) (s *Server) {
|
|
||||||
s = new(Server)
|
|
||||||
s.config = config
|
|
||||||
|
|
||||||
s.inner = http.Server{
|
|
||||||
Addr: config.Addr,
|
|
||||||
}
|
|
||||||
|
|
||||||
http.HandleFunc("/fingerprint", s.handleFingerprint)
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Run(ctx context.Context) (err error) {
|
|
||||||
return invoker.Run(ctx, s.runServe, s.runShutdown)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) runServe(context.Context) (err error) {
|
|
||||||
// NOTE: Doesn't support context, which is why we need runShutdown
|
|
||||||
err = s.inner.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile)
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gracefully shut down the server when the context is cancelled
|
|
||||||
func (s *Server) runShutdown(ctx context.Context) (err error) {
|
|
||||||
<-ctx.Done()
|
|
||||||
|
|
||||||
timeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
_ = s.inner.Shutdown(timeout)
|
|
||||||
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the sha256 of the certificate as a temporary work-around for local development.
|
|
||||||
// TODO remove this when WebTransport uses the system CA
|
|
||||||
func (s *Server) handleFingerprint(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
||||||
_, _ = w.Write([]byte(s.config.Fingerprint))
|
|
||||||
}
|
|
|
@ -2,16 +2,13 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/hex"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/kixelated/invoker"
|
"github.com/kixelated/invoker"
|
||||||
"github.com/kixelated/warp/server/internal/warp"
|
"github.com/kixelated/warp/server/internal/warp"
|
||||||
"github.com/kixelated/warp/server/internal/web"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -53,19 +50,7 @@ func run(ctx context.Context) (err error) {
|
||||||
return fmt.Errorf("failed to create warp server: %w", err)
|
return fmt.Errorf("failed to create warp server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := sha256.Sum256(tlsCert.Certificate[0])
|
|
||||||
fingerprint := hex.EncodeToString(hash[:])
|
|
||||||
|
|
||||||
webConfig := web.Config{
|
|
||||||
Addr: *addr,
|
|
||||||
CertFile: *cert,
|
|
||||||
KeyFile: *key,
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
}
|
|
||||||
|
|
||||||
webServer := web.New(webConfig)
|
|
||||||
|
|
||||||
log.Printf("listening on %s", *addr)
|
log.Printf("listening on %s", *addr)
|
||||||
|
|
||||||
return invoker.Run(ctx, invoker.Interrupt, warpServer.Run, webServer.Run)
|
return invoker.Run(ctx, invoker.Interrupt, warpServer.Run)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue