kernel.anubis
19.8 KB
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
Warning: This file is obsolete. See 'multihost_http_server.anubis' instead.
The Anubis Project
Making a Web site
Copyright (c) Alain Proute' <alp@math.jusieu.fr> 2002.
All rights reserved.
read tools/basis.anubis
read web/html.anubis
This file defines a framework for making a web site. A web site should not only be seen
as a set of pages. It should also be seen as an interactive software. The client should
be in front of a system which reacts to his requests. In order to precise this
concept, we introduce the following notions.
*** Session info. *********************************************************************
This file essentially implements tools for managing session informations. Session
informations are used for following the clients during a session. In session
informations, you accumulate informations on your client, so that you don't have to ask
him at each page.
Session informations are divided into three groups:
- 'client session informations', which are stored in the page sent to the client (but
invisible to him),
- 'client cookie informations', which are stored on the client's disk in a cookie
(the difference with client session info is that these informations remain available
even if the client does not click in one if your pages, but simply enters the address
of the site in his browser),
- 'server session informations', which are stored on the server's disk.
Typically, you should put non sensitive informations in the first two groups, and
sensitive informations in the last group.
At each new request comming through the network, all groups of informations are
available (they get default values if they cannot be retrieved).
Technically, groups of informations are stored as follows.
* Client session informations: The data are serialized, and encoded into a string
which becomes the value of the web argument 'c_ticket' (put in a hidden field in the
client page). Hence the data are stored in the page sent to the client (the encoding is
just for avoiding problems of format with values of web arguments, not for
cryptographical purpose), hence it does not take any room on the server's disk. This
kind of information is by nature non sensitive, and is indefinitely valid.
* Client cookie informations: The information is the value of a cookie. It is
serialized and encoded in the same way as the previous information.
* Server session informations: The data are serialized and stored on the server's
disk in a file whose name is world unique (constructed by sha1). Only the name of that
file is transmitted to the client as the value of the web argument 's_ticket'. Hence,
the informations do not go to the network. Nevertheless, a SSL secured connection
should be used, if you are using such informations, because the value of 's_ticket'
indirectly allows access to the corresponding file. In particular, the value of
's_ticket' if it is valid is considered as a client identification proof. This kind of
ticket (value of 's_ticket') is not indefinitely valid. Typically, it should be valid
only half an hour or so. You define the validity of such tickets by defining the value
of 'server_session_info_validity' (see below).
If you do not want to use server session informations (which should be the case if your
site is not SSL secured), just set the value of 'use_server_session_info' to 'false'.
In order to define which kind of informations you want to store in session infos,
define the following public types:
ClientSessionInfo
ClientCookieInfo
ServerSessionInfo
Even if you set 'use_server_session_info' to 'false', you must define all types, but in
that case, just define 'ServerSessionInfo' as follows:
|
|public type ServerSessionInfo: none.
|
This is a simple way to define a type which is mandatory but not actually used.
Now, it is up to you to decide what are the alternatives and components of these
types. The sole constraint is that these types must be serializables.
The type 'SessionInfo' is defined as follows:
public type SessionInfo:
sinfo(ClientSessionInfo c_info,
ClientCookieInfo k_info,
ServerSessionInfo s_info).
You will have to use this type.
When informations are missing, the system provides default values. Hence, you must also
define such default values.
public define ClientSessionInfo default_client_session_info.
public define ClientCookieInfo default_client_cookie_info.
public define ServerSessionInfo default_server_session_info.
*** Your main file. *******************************************************************
Your site must have a 'main file'. If the symbolic name of the site (the value of the
symbol 'site_name'; see below), is for example 'my_site', call your main file
'my_site.anubis', and write it down as follows:
|
|read web/html.anubis
|
|public type ClientSessionInfo:
| <alternatives of this type>
|
|public type ClientCookieInfo:
| <alternatives of this type>
|
|public type ServerSessionInfo:
| <alternatives of this type>
|
|public define ClientSessionInfo default_client_session_info = <some value>
|public define ClientCookieInfo default_client_cookie_info = <some value>
|public define ServerSessionInfo default_server_session_info = <some value>
|
|read web/kernel.anubis
|
|public define Bool use_server_session_info = true.
|public define String site_name = "my_site".
|public define Int32 server_session_info_validity = 1200.
|
|public define Web_page
| compute_page
| (
| String frame_name, // the same state is used to compute all frames
| SessionInfo info
| ) =
| <body of this function>
|
|public define (String,SessionInfo) // frame name and new session info
| compute_new_session_info
| (
| List(Web_arg) request,
| SessionInfo previous
| ) =
| <body of this function>
|
| Next, put the stuff required by web/http_server.anubis.
|
|
Notes:
(1) 'kernel.anubis' must be read only after the types 'ClientSessionInfo',
'ClientCookieInfo' and 'ServerSessionInfo' have been defined.
(2) The two functions 'compute_page' and 'compute_new_session_info' do all the work.
Of course it is likely that they are only a gate to other functions which actually do
the job. For clarity, try to maintain the bodies of these two functions as small as
possible, and don't hesitate to call other functions from within these bodies, for each
particular task. You may also write these functions in separate files, and put a 'read'
instead of their definition in the main file.
(3) If you don't use frames, use "_top" as your unique frame name.
*** Computing pages. ******************************************************************
From session infos, it must be possible to compute the page (or particular frame) to be
sent to the client. Hence, you must define the function:
public define Web_page
compute_page
(
String frame_name,
SessionInfo info
).
Remember that session infos may contain the name of the page to be displayed (if pages
have names), together with any session informations which are particular to this
client. It may also contain a client id (in server session infos), which gives access
to permanent data for this client (SSL secured site recommended in that case).
*** Computing session informations. ***************************************************
In your pages, it is likely that you put forms, with input fields and submission
buttons or links which are able to send web arguments. When the client clicks on such
a button, he sends informations to the server. This information is of type
'List(Web_arg)'. Furthermore the system automatically retrieves the complete previous
session info. Hence, you must define the following function:
public define (String,SessionInfo)
compute_new_session_info
(
List(Web_arg) request,
SessionInfo previous
).
Notice that this function returns a pair (String,SessionInfo). The string is the name
of the frame for which the document is requested.
*** Using frames. *********************************************************************
If you use frames, you should have a 'main' frame. The other frames should be
considered as decorations attached to the 'main' frame. This secondary frames must be
reloaded by the Web_body_option 'reload_frame'.
*** Miscellaneous parameters. *********************************************************
You must also define some parameters:
public define Bool use_server_session_info.
Set this to 'true' (in your main file) if you want to use server session
informations. If you set it to 'false', define the type 'SeverSessionInfo' as suggested
above.
public define String site_name.
This string must be usable as a directory name or as a file name. Hence, it is
recommended to use only lower case non accented letters and underscores (no blanks and
such things).
public define Int32 server_session_info_validity.
This integer defines the time (in seconds) a 's_ticket' remains valid. 1200 (20
minutes) is reasonable for a secured site. The 's_ticket' is just a string which
uniquely identifies a file on the server (in the subdirectory site_name+"/s_tickets/"
of the server's directory), and which contains server session informations. The
's_ticket' web argument is automatically inserted into all forms, so that it is sent
back to the server by the client when he clicks a submission button.
*** Summary.
Summarizing, the system works as follows:
+--------+
| client |
+--------+
|
|
form
informations
|
| +-------------+
-------->| separate |
-------| tickets |-------------------------
| +-------------+ |
| | request
| c_ticket |
| | |
| v |
| +-------------+ |
| | decode | |
| +-------------+ |
| | |
| previous client |
| session info (maybe) |
| | |
| | +--------------+ v
| ---->| make | +--------------------------+
s_ticket ------------>| session info |----->| compute_new_session_info |
| | +--------------+ +--------------------------+
| | | |
| previous server | |
| session info (maybe) new session info |
v | | |
+----------+ | frame name
| server's | | |
| disk | ----------------------+ |
+----------+ | | |
^ | v v
| | +--------------+
new server | | compute_page |
session info v +--------------+
| +------------+ |
| | create new | page
-----------| tickets | |
+------------+ |
| v
new tickets +----------------+
| | insert tickets |
------------------->| in all forms |
| and links |
+----------------+
|
page with tickets
|
v
+--------+
| client |
+--------+
But you have only two boxes to fill in: 'compute_new_session_info' and
'compute_page'. That is to say: all tickets operations are already defined here.
The directories 'site_name' and 'site_name/s_tickets' are created automatically.
Recommendation: If you store permanent data on your clients, create the subdirectory
'my_site/clients/', and for a client whose identifier is 'smith' store his personal
informations in files 'my_site/clients/smith.1', 'my_site/clients/smith.2' (with more
appropriate extensions).
Recommendation: also create the subdirectory 'public/my_site', and put here all the
public documents pertaining to your site. This method helps to separate documents
belonging to different sites operated by the same server.
Important: Because the ticket mecanism uses the web arguments named 's_ticket' and
'c_ticket', do never give one of these names to a web argument.
--- That's all for the public part. ---------------------------------------------------
read web/web_arg_encode.anubis
define ClientSessionInfo
get_client_session_info
(
String c_ticket
) =
if (Maybe(ClientSessionInfo))web_arg_decode(c_ticket) is
{
failure then default_client_session_info,
success(info) then info
}.
define ClientSessionInfo
get_client_session_info
(
List(Web_arg) lwa
) =
if lwa is
{
[ ] then default_client_session_info,
[h . t] then
if h is
{
web_arg(name,value) then
if name = "c_ticket"
then get_client_session_info(value)
else get_client_session_info(t),
upload(_,_,_) then
get_client_session_info(t)
}
}.
Getting the previous server session info from the s_ticket.
define ServerSessionInfo
get_server_session_info
(
String s_ticket
) =
if (RetrieveResult((Int32,Bool,ServerSessionInfo)))retrieve(site_name+"/s_tickets/t"+s_ticket) is ok(d)
then (
if d is (time_stamp,_,info) then
if time_stamp < now
then default_server_session_info // ticket is out of date
else info // ticket is valid
)
else default_server_session_info. // ticket file not found
Getting the previous client state from the list of web arguments.
define ServerSessionInfo
get_server_session_info
(
List(Web_arg) lwa
) =
if lwa is
{
[ ] then default_server_session_info,
[h . t] then
if h is
{
web_arg(name,value) then
if name = "s_ticket"
then get_server_session_info(value)
else get_server_session_info(t),
upload(_,_,_) then
get_server_session_info(t)
}
}.
define SessionInfo
get_session_info
(
List(Web_arg) lwa
) =
sinfo(get_client_session_info(lwa),
default_client_cookie_info,
get_server_session_info(lwa)).
Making a new s_ticket. (time_stamp,true,info) is hashed by SHA1, which makes the
s_ticket name.
define String
new_s_ticket
(
SessionInfo info
) =
if use_server_session_info then
(
if info is sinfo(_,_,s_info) then
with time_stamp = now+server_session_info_validity,
s_ticket = web_arg_encode(sha1((time_stamp,(Bool)true,s_info))),
if save(((Int32,Bool,ServerSessionInfo))(time_stamp,true,s_info),
site_name+"/s_tickets/t"+s_ticket) is ok
then s_ticket
else /* probably the directory 'site_name/s_tickets/' does not exist */
forget(make_directory(site_name,default_directory_mode));
forget(make_directory(site_name+"/s_tickets/",default_directory_mode));
if save(((Int32,Bool,ServerSessionInfo))(time_stamp,true,s_info),
site_name+"/s_tickets/t"+s_ticket) is ok
then s_ticket
else ""
)
else "".
define String
new_c_ticket
(
SessionInfo info
) =
if info is sinfo(c_info,_,_) then
web_arg_encode(c_info).
Destroying out of date s_tickets.
define One
delete_out_of_date
(
List(String) s_tickets
) =
if s_tickets is
{
[ ] then unique,
[h . t] then
if (RetrieveResult((Int32,Bool,ServerSessionInfo)))retrieve(site_name+"/s_tickets/"+h) is ok(d)
then (
if d is (time_stamp,_,_) then
if time_stamp < now
then (forget(remove(site_name+"/s_tickets/"+h)); delete_out_of_date(t))
else delete_out_of_date(t)
)
else delete_out_of_date(t)
}.
define One
delete_out_of_date
=
// Do it on the average of 1/64.
if (now&63) = 0
then delete_out_of_date(directory_list(site_name+"/s_tickets/","t*"))
else unique.
The function 'the_web_page' is the realization of our flowchart.
public define One
the_web_page
(
HeaderSort hs,
List(Web_arg) lwa
) =
with old_info = get_session_info(lwa),
delete_out_of_date;
if compute_new_session_info(lwa,old_info) is (frame_name,new_info) then
with c_ticket = new_c_ticket(new_info),
s_ticket = new_s_ticket(new_info),
forget(print_with_headers(hs,c_ticket,s_ticket,compute_page(frame_name,new_info))).
The same one, but to be used with 'web/http_server.anubis':
public define (String,String,Web_page)
tickets_and_web_page
(
HeaderSort hs,
List(Web_arg) lwa
) =
with old_info = get_session_info(lwa),
delete_out_of_date;
if compute_new_session_info(lwa,old_info) is (frame_name,new_info) then
with c_ticket = new_c_ticket(new_info),
s_ticket = new_s_ticket(new_info),
(c_ticket,s_ticket,compute_page(frame_name,new_info)).
Note: insertion of the tickets in all forms is performed by the 'print' function (see
'web/html.anubis').