File: client.bs

package info (click to toggle)
storm-lang 0.7.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 51,836 kB
  • sloc: ansic: 261,420; cpp: 138,870; sh: 14,877; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (116 lines) | stat: -rw-r--r-- 2,657 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use core:io;
use core:net;
use lang:bs:macro;
use crypto;

/**
 * Thrown when the user needs to sign in.
 */
class SignInRedirect extends Exception {
	init(Url to) {
		init { to = to; }
	}

	// Redirect here.
	Url to;

	void message(StrBuf to) : override {
		to << "Redirect here to sign in: " << to;
	}
}

/**
 * Thrown on other errors.
 */
class ServerError extends Exception {
	init(Str msg) {
		init { msg = msg; }
	}

	Str msg;

	void message(StrBuf to) : override {
		to << msg;
	}
}

// Helper for 'connect'.
private NetStream connectOrThrow(Str host, Nat port) {
	if (c = connect(host, port))
		return c;
	throw ServerError("Failed to connect to ${host}.");
}

/**
 * Connection to the server.
 */
class Client on Network {
	// Connect and authenticate the user.
	Client connect(Str host, Str identity) : static {
		NetStream socket = connectOrThrow(host, serverPort);

		if (host == "storm-lang.org") {
			var context = ClientContext:pinnedTo(Certificate:loadPEM(serverCertificate));
			context.verifyHostname = false;

			var session = context.connect(socket, host);
			return Client(socket, session.input, session.output, identity);
		} else {
			return Client(socket, socket.input, socket.output, identity);
		}
	}

	// Authenticates the user.
	private init(NetStream socket, IStream input, OStream output, Str identity) {
		init {
			socket = socket;
			input = ObjIStream(input);
			output = ObjOStream(BufferedOStream(output));
		}

		var response = query(AuthRequest(identity, named{PROGVIS_VERSION}.version));
		if (response as AuthResponse) {
			uName = response.name;
			admin = response.admin;
			if (response.version > named{PROGVIS_VERSION}.version)
				throw ServerError("Please upgrade Progvis. Version ${response.version} is available.");
			print("Username: ${uName}");
		} else if (response as AuthLoginResponse) {
			throw SignInRedirect(response.url);
		}
	}

	private NetStream socket;
	private ObjIStream input;
	private ObjOStream output;

	// Lock to ensure that multiple threads do not attempt to read/write to IO at the same time.
	private core:sync:Lock lock;

	private Str uName;
	Str username() { uName; }

	private Bool admin;
	Bool isAdmin() { admin; }

	// Perform a query to the server.
	Response query(Request request) {
		// Make sure that we don't accidentally have multiple UThreads in here.
		core:sync:Lock:Guard guard(lock);

		request.write(output);
		output.flush();

		var reply = Response:read(input);
		if (reply as ErrorResponse)
			throw ServerError(reply.message);
		else if (reply as AuthResponse)
			uName = reply.name;
		reply;
	}

	// Close the connection.
	void close() {
		socket.close();
	}
}