The document discusses asynchronous JavaScript and XML (AJAX) techniques for making asynchronous HTTP requests from the browser. It provides code examples using XMLHttpRequest and the newer Fetch API to make requests to server-side handlers written in Go. The code sets up a simple page that displays buttons for different asynchronous actions, and uses JavaScript functions to make requests on button click, printing the responses to a log on the page. This demonstrates asynchronous interactivity between the browser and server.
3. Elric of Melniboné, Michael Moorcock
E
lric sent his mind into twisting tunnels of logic,
across endless plains of ideas, through
mountains of symbolism and endless universes
of alternate truths; he sent his mind out further
and further and as it went he sent with it the words [...] words
that few of his contemporaries would understand...
4. pay attention!
all code is BSD 2-clause licensed
any resemblance to actual code &
conceptstm, living or dead, is probably
your imagination playing tricks on you
if you can make money from it you're
doing a damn sight better than we are!
5. In Digital Spaces
•2006 -> present [inidsol.uk]
•13 US patents
•real-time systems
•digital identity
•distributed ledgers
•DNS -> [TCP|UDP] -> HTTP
•Ruby -> Go -> JavaScript
Seeking Identity
Ellie
[physicist]
Romek
[cryptographer]
19. AJAX
Asynchronous JavaScript and XML
•JavaScript is a single-threaded language
•but browsers are event-driven environments
•so JavaScript runtimes normally have three basic threads
•one to run the main script
•one to run scripts for high priority events
•one to run scripts for low priority events
•and each event can have callbacks de
fi
ned for it
20. AJAX
Asynchronous JavaScript and XML
•XMLHttpRequest
fi
rst appeared in MSXML
•available in IE5 as an ActiveX component from 1999
•similar functionality in other browsers from 2000 onwards
•fully supported in IE 7 2006
•despite its name it isn't restricted to XML
•most modern uses involve JSON
21. package main
import "fmt"
import "net/http"
import "os"
import "strings"
import "text/template"
const LAUNCH_FAILED = 1
const FILE_READ = 2
const BAD_TEMPLATE = 3
const ADDRESS = ":3000"
type Commands map[string] func(http.ResponseWriter, *http.Request)
type PageCon
fi
guration struct { Commands }
func main() {
p := PageCon
fi
guration{
Commands{ "A": AJAX_handler("A"), "B": AJAX_handler("B"), "C": AJAX_handler("C") }}
html, e := template.ParseFiles(BaseName() + ".html")
Abort(FILE_READ, e)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
Abort(BAD_TEMPLATE, html.Execute(w, p))
})
for c, f := range p.Commands {
http.HandleFunc("/"+c, f)
}
Abort(LAUNCH_FAILED, http.ListenAndServe(ADDRESS, nil))
}
func Abort(n int, e error) {
if e != nil {
fmt.Println(e)
os.Exit(n)
}
}
func BaseName() string {
s := strings.Split(os.Args[0], "/")
return s[len(s)-1]
}
func AJAX_handler(c string) func(http.ResponseWriter, *http.Request) {
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, c)
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8' />
<title>AJAX EXAMPLE</title>
<script>
function print(e, m) {
document.getElementById(e).innerHTML += `<div>${m}</div>`;
}
{{range $c, $v := .Commands}}
function {{$c}}() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
print("event_log", this.responseText);
}
};
xhttp.open("GET", "{{$c}}", true);
xhttp.send();
}
{{end}}
</script>
</head>
<body>
<h1>AJAX EXAMPLE</h1>
<h2>Known Asynchronous Actions</h2>
<div>
{{range $c, $v := .Commands}}
<span>
<button type="button" onclick="{{$c}}();">{{$c}}</button>
</span>
{{end}}
</div>
<h2>Server Output</h2>
<div id='event_log'></div>
</body>
</html>
59. package main
import "fmt"
import "log"
import "net/http"
import "os"
import "strings"
import "text/template"
import "golang.org/x/net/websocket"
const LAUNCH_FAILED = 1
const FILE_READ = 2
const BAD_TEMPLATE = 3
const ADDRESS = ":3000"
type PageCon
fi
guration struct {
URL string
Commands []string
}
func main() {
p := PageCon
fi
guration{"/socket", []string{"A", "B", "C"}}
html, e := template.ParseFiles(BaseName() + ".html")
Abort(FILE_READ, e)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
Abort(BAD_TEMPLATE, html.Execute(w, p))
})
events := make(chan string, 4096)
var monitors []*websocket.Conn
http.Handle(p.URL, websocket.Handler(func(ws *websocket.Conn) {
i := len(monitors)
monitors = append(monitors, ws)
defer func() {
if e := ws.Close(); e != nil {
log.Println(e.Error())
}
monitors[i] = nil
}()
var b struct{ Message string }
for {
if e := websocket.JSON.Receive(ws, &b); e == nil {
events <- b.Message
} else {
log.Printf("socket receive error: %vn", e)
break
}
}
}))
go func() {
for {
e := <-events
for _, ws := range monitors {
if ws != nil {
go func(ws *websocket.Conn, e string) {
websocket.JSON.Send(ws, []interface{}{e})
}(ws, e)
}
}
}
}()
Abort(LAUNCH_FAILED, http.ListenAndServe(ADDRESS, nil))
}
func Abort(n int, e error) {
if e != nil {
fmt.Println(e)
os.Exit(n)
}
}
func BaseName() string {
s := strings.Split(os.Args[0], "/")
return s[len(s)-1]
}
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8' />
<title>WebSocket EXAMPLE</title>
<script>
function print(m) {
document.getElementById("event_log").innerHTML += `<div>${m}</div>`;
}
function newButton(n, c) {
let b = document.createElement("BUTTON");
b.onclick = c;
b.appendChild(document.createTextNode(n));
return b;
}
window.onload = () => {
var socket = new WebSocket(`ws://${location.host}{{.URL}}`);
socket.onopen = (e) => print("opening socket: {{.URL}}");
socket.onclose = (e) => print("closing socket: {{.URL}}");
socket.onerror = (e) => print(e.message);
socket.onmessage = (m) => print(JSON.parse(m.data));
let button_bar = document.getElementById("action_buttons");
{{range $c, $v := .Commands}}
button_bar.appendChild(
newButton('{{$v}}', () => socket.send(
JSON.stringify({ Message: '{{$v}}' }))));
{{end}}
}
</script>
</head>
<body>
<h1>WebSocket EXAMPLE</h1>
<h2>Known Asynchronous Actions</h2>
<div id="action_buttons"></div>
<h2>Server Output</h2>
<div id='event_log'></div>
</body>
</html>
60. func main() {
p := PageCon
fi
guration{"/socket", []string{"A", "B", "C"}}
html, e := template.ParseFiles(BaseName() + ".html")
Abort(FILE_READ, e)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
Abort(BAD_TEMPLATE, html.Execute(w, p))
})
events := make(chan string, 4096)
var monitors []*websocket.Conn
http.Handle(p.URL, websocket.Handler(func(ws *websocket.Conn) {
i := len(monitors)
monitors = append(monitors, ws)
defer func() {
if e := ws.Close(); e != nil {
log.Println(e.Error())
}
monitors[i] = nil
}()
var b struct{ Message string }
for {
if e := websocket.JSON.Receive(ws, &b); e == nil {
events <- b.Message
} else {
log.Printf("socket receive error: %vn", e)
break
}
}
}))
go func() {
for {
e := <-events
for _, ws := range monitors {
if ws != nil {
go func(ws *websocket.Conn, e string) {
websocket.JSON.Send(ws, []interface{}{e})
}(ws, e)
}
}
}
}()
Abort(LAUNCH_FAILED, http.ListenAndServe(ADDRESS, nil))
}
events := make(chan string, 4096)
var monitors []*websocket.Conn
http.Handle(p.URL, websocket.Handler(func(ws *websocket.Conn) {
i := len(monitors)
monitors = append(monitors, ws)
defer func() {
if e := ws.Close(); e != nil {
log.Println(e.Error())
}
monitors[i] = nil
}()
var b struct{ Message string }
for {
if e := websocket.JSON.Receive(ws, &b); e == nil {
events <- b.Message
} else {
log.Printf("socket receive error: %vn", e)
break
}
}
}))
61. func main() {
p := PageCon
fi
guration{"/socket", []string{"A", "B", "C"}}
html, e := template.ParseFiles(BaseName() + ".html")
Abort(FILE_READ, e)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
Abort(BAD_TEMPLATE, html.Execute(w, p))
})
events := make(chan string, 4096)
var monitors []*websocket.Conn
http.Handle(p.URL, websocket.Handler(func(ws *websocket.Conn) {
i := len(monitors)
monitors = append(monitors, ws)
defer func() {
if e := ws.Close(); e != nil {
log.Println(e.Error())
}
monitors[i] = nil
}()
var b struct{ Message string }
for {
if e := websocket.JSON.Receive(ws, &b); e == nil {
events <- b.Message
} else {
log.Printf("socket receive error: %vn", e)
break
}
}
}))
go func() {
for {
e := <-events
for _, ws := range monitors {
if ws != nil {
go func(ws *websocket.Conn, e string) {
websocket.JSON.Send(ws, []interface{}{e})
}(ws, e)
}
}
}
}()
Abort(LAUNCH_FAILED, http.ListenAndServe(ADDRESS, nil))
}
events := make(chan string, 4096)
var monitors []*websocket.Conn
go func() {
for {
e := <-events
for _, ws := range monitors {
if ws != nil {
go func(ws *websocket.Conn, e string) {
websocket.JSON.Send(ws, []interface{}{e})
}(ws, e)
}
}
}
}()
62. func main() {
p := PageCon
fi
guration{"/socket", []string{"A", "B", "C"}}
html, e := template.ParseFiles(BaseName() + ".html")
Abort(FILE_READ, e)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
Abort(BAD_TEMPLATE, html.Execute(w, p))
})
events := make(chan string, 4096)
var monitors []*websocket.Conn
http.Handle(p.URL, websocket.Handler(func(ws *websocket.Conn) {
i := len(monitors)
monitors = append(monitors, ws)
defer func() {
if e := ws.Close(); e != nil {
log.Println(e.Error())
}
monitors[i] = nil
}()
var b struct{ Message string }
for {
if e := websocket.JSON.Receive(ws, &b); e == nil {
events <- b.Message
} else {
log.Printf("socket receive error: %vn", e)
break
}
}
}))
go func() {
for {
e := <-events
for _, ws := range monitors {
if ws != nil {
go func(ws *websocket.Conn, e string) {
websocket.JSON.Send(ws, []interface{}{e})
}(ws, e)
}
}
}
}()
Abort(LAUNCH_FAILED, http.ListenAndServe(ADDRESS, nil))
}
events := make(chan string, 4096)
var monitors []*websocket.Conn
http.Handle(p.URL, websocket.Handler(func(ws *websocket.Conn) {
i := len(monitors)
monitors = append(monitors, ws)
defer func() {
if e := ws.Close(); e != nil {
log.Println(e.Error())
}
monitors[i] = nil
}()
var b struct{ Message string }
for {
if e := websocket.JSON.Receive(ws, &b); e == nil {
events <- b.Message
} else {
log.Printf("socket receive error: %vn", e)
break
}
}
}))
63. func main() {
p := PageCon
fi
guration{"/socket", []string{"A", "B", "C"}}
html, e := template.ParseFiles(BaseName() + ".html")
Abort(FILE_READ, e)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
Abort(BAD_TEMPLATE, html.Execute(w, p))
})
events := make(chan string, 4096)
var monitors []*websocket.Conn
http.Handle(p.URL, websocket.Handler(func(ws *websocket.Conn) {
i := len(monitors)
monitors = append(monitors, ws)
defer func() {
if e := ws.Close(); e != nil {
log.Println(e.Error())
}
monitors[i] = nil
}()
var b struct{ Message string }
for {
if e := websocket.JSON.Receive(ws, &b); e == nil {
events <- b.Message
} else {
log.Printf("socket receive error: %vn", e)
break
}
}
}))
go func() {
for {
e := <-events
for _, ws := range monitors {
if ws != nil {
go func(ws *websocket.Conn, e string) {
websocket.JSON.Send(ws, []interface{}{e})
}(ws, e)
}
}
}
}()
Abort(LAUNCH_FAILED, http.ListenAndServe(ADDRESS, nil))
}
events := make(chan string, 4096)
var monitors []*websocket.Conn
go func() {
for {
e := <-events
for _, ws := range monitors {
if ws != nil {
go func(ws *websocket.Conn, e string) {
websocket.JSON.Send(ws, []interface{}{e})
}(ws, e)
}
}
}
}()
64. package main
import "log"
import "golang.org/x/net/websocket"
const SERVER = "ws://localhost:3000/socket"
const ADDRESS = "http://localhost/"
func main() {
if ws, e := websocket.Dial(SERVER, "", ADDRESS); e == nil {
var m []interface{}
for {
if e := websocket.JSON.Receive(ws, &m); e == nil {
log.Printf("message: %vn", m)
} else {
log.Fatal(e)
}
}
} else {
log.Fatal(e)
}
}
65. package main
import "log"
import "golang.org/x/net/websocket"
const SERVER = "ws://localhost:3000/socket"
const ADDRESS = "http://localhost/"
func main() {
if ws, e := websocket.Dial(SERVER, "", ADDRESS); e == nil {
var m []interface{}
for {
if e := websocket.JSON.Receive(ws, &m); e == nil {
log.Printf("message: %vn", m)
} else {
log.Fatal(e)
}
}
} else {
log.Fatal(e)
}
}
66. package main
import "log"
import "golang.org/x/net/websocket"
const SERVER = "ws://localhost:3000/socket"
const ADDRESS = "http://localhost/"
func main() {
if ws, e := websocket.Dial(SERVER, "", ADDRESS); e == nil {
var m []interface{}
for {
if e := websocket.JSON.Receive(ws, &m); e == nil {
log.Printf("message: %vn", m)
} else {
log.Fatal(e)
}
}
} else {
log.Fatal(e)
}
}
67. package main
import "log"
import "os"
import "time"
import "golang.org/x/net/websocket"
const SERVER = "ws://localhost:3000/socket"
const ADDRESS = "http://localhost/"
func main() {
if ws, e := websocket.Dial(SERVER, "", ADDRESS); e == nil {
var b struct{ Message string }
for _, v := range os.Args[1:] {
b.Message = v
websocket.JSON.Send(ws, &b)
time.Sleep(1 * time.Second)
}
} else {
log.Fatal(e)
}
}
68. package main
import "log"
import "os"
import "time"
import "golang.org/x/net/websocket"
const SERVER = "ws://localhost:3000/socket"
const ADDRESS = "http://localhost/"
func main() {
if ws, e := websocket.Dial(SERVER, "", ADDRESS); e == nil {
var b struct{ Message string }
for _, v := range os.Args[1:] {
b.Message = v
websocket.JSON.Send(ws, &b)
time.Sleep(1 * time.Second)
}
} else {
log.Fatal(e)
}
}
69. package main
import "log"
import "os"
import "time"
import "golang.org/x/net/websocket"
const SERVER = "ws://localhost:3000/socket"
const ADDRESS = "http://localhost/"
func main() {
if ws, e := websocket.Dial(SERVER, "", ADDRESS); e == nil {
var b struct{ Message string }
for _, v := range os.Args[1:] {
b.Message = v
websocket.JSON.Send(ws, &b)
time.Sleep(1 * time.Second)
}
} else {
log.Fatal(e)
}
}