-
Notifications
You must be signed in to change notification settings - Fork 3
/
demo.mo
164 lines (140 loc) · 5.35 KB
/
demo.mo
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import FHM "mo:StableHashMap/FunctionalStableHashMap";
import SHA256 "mo:motoko-sha/SHA256";
import CertTree "mo:ic-certification/CertTree";
import CanisterSigs "mo:ic-certification/CanisterSigs";
import CertifiedData "mo:base/CertifiedData";
import HTTP "./Http";
import Iter "mo:base/Iter";
import Blob "mo:base/Blob";
import Option "mo:base/Option";
import Time "mo:base/Time";
import Text "mo:base/Text";
import Debug "mo:base/Debug";
import Prelude "mo:base/Prelude";
import Principal "mo:base/Principal";
import Buffer "mo:base/Buffer";
import Nat8 "mo:base/Nat8";
import CertifiedCache "lib";
import Int "mo:base/Int";
actor Self {
type HttpRequest = HTTP.HttpRequest;
type HttpResponse = HTTP.HttpResponse;
var two_days_in_nanos = 2 * 24 * 60 * 60 * 1000 * 1000 * 1000;
stable var entries : [(Text, (Blob, Nat))] = [];
var cache = CertifiedCache.fromEntries<Text, Blob>(
entries,
Text.equal,
Text.hash,
Text.encodeUtf8,
func(b : Blob) : Blob { b },
two_days_in_nanos + Int.abs(Time.now()),
);
public query func keys() : async [Text] {
return Iter.toArray(cache.keys());
};
public query func http_request(req : HttpRequest) : async HttpResponse {
let cached = cache.get(req.url);
switch cached {
case (?body) {
// Print the body of the response
let message = Text.decodeUtf8(body);
switch message {
case (null) {};
case (?m) {
Debug.print(m);
};
};
let response : HttpResponse = {
status_code : Nat16 = 200;
headers = [("content-type", "text/html"), cache.certificationHeader(req.url)];
body = body;
streaming_strategy = null;
upgrade = null;
};
return response;
};
case null {
Debug.print("Request was not found in cache. Upgrading to update request.\n");
return {
status_code = 404;
headers = [];
body = Blob.fromArray([]);
streaming_strategy = null;
upgrade = ?true;
};
};
};
};
public func http_request_update(req : HttpRequest) : async HttpResponse {
let url = req.url;
Debug.print("Storing request in cache.");
let time = Time.now();
let message = "<pre>Request has been stored in cache: \n" # "URL is: " # url # "\n" # "Method is " # req.method # "\n" # "Body is: " # debug_show req.body # "\n" # "Timestamp is: \n" # debug_show Time.now() # "\n" # "</pre>";
if (req.url == "/" or req.url == "/index.html") {
let page = main_page();
let response : HttpResponse = {
status_code : Nat16 = 200;
headers = [("content-type", "text/html")];
body = page;
streaming_strategy = null;
upgrade = null;
};
let put = cache.put(req.url, page, null);
return response;
} else {
let page = page_template(message);
let response : HttpResponse = {
status_code : Nat16 = 200;
headers = [("content-type", "text/html")];
body = page;
streaming_strategy = null;
upgrade = null;
};
let put = cache.put(req.url, page, null);
// update index
let indexBody = main_page();
cache.put("/", indexBody, null);
return response;
};
};
func page_template(body : Text) : Blob {
return Text.encodeUtf8(
"<html>" # "<head>" # "<meta name='viewport' content='width=device-width, initial-scale=1'>" # "<link rel='stylesheet' href='https://unpkg.com/chota@latest'>" # "<title>IC certified assets demo</title>" # "</head>" # "<body>" # "<div class='container' role='document'>" #
body # "</div>" # "</body>" # "</html>"
);
};
func my_id() : Principal = Principal.fromActor(Self);
func main_page() : Blob {
page_template(
"<p>This canister demonstrates certified HTTP assets from Motoko.</p>" # "<p>You can see this text at <tt>https://" # debug_show my_id() # ".ic0.app/</tt> " # "(note, no <tt>raw</tt> in the URL!) and it will validate!</p>" # "<p>This canister is dynamic, and uses http_request updates to store any visited route as a cached \"asset\". Here is the list of " # "cached routes:</p>" # "<ul>" #
Text.join(
"",
Iter.map(
cache.keys(),
func(key : Text) : Text {
"<li><a href='" # key # "'>" # key # "</a></li>";
},
),
) # "</ul>" # "<p>And to demonstrate that this really is dynamic, you can visit a new route and it will show up in this list.<pp>" # "<p>Code for this canister can be found at " # "<a href='https://github.com/krpeacock/cache-example'>https://github.com/krpeacock/cache-example</a>.</p>"
# "<p>Many thanks to Joachim for the certification library behind this package, at <a href='https://github.com/nomeata/ic-certification/tree/main/demo'>https://github.com/nomeata/ic-certification/tree/main/demo</a>.</p>",
);
};
func value_page(key : Text) : Blob {
switch (cache.get(key)) {
case (null) { page_template("<p>Key " # key # " not found.</p>") };
case (?v) {
v;
};
};
};
/*
* Convenience function to implement SHA256 on Blobs rather than [Int8]
*/
system func preupgrade() {
entries := cache.entries();
};
// If your CertTree.Store is stable, it is recommended to prune all signatures in pre or post-upgrade:
system func postupgrade() {
let _ = cache.pruneAll();
};
};