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();
}
}
|