-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver-test.r3
184 lines (176 loc) · 6.25 KB
/
server-test.r3
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
Rebol [
Title: "Test HTTPd Scheme"
File: %server-test.r3
Date: 14-Dec-2023
Author: "Oldes"
Version: 0.9.0
Needs: 3.11.0
Note: {
To test POST method from Rebol console, try this:
```
write http://localhost:8081 {msg=hello}
write http://localhost:8081 [post [user-agent: "bla"] "hello"]
```
}
]
import %httpd.reb
system/schemes/httpd/set-verbose 4 ; for verbose output
system/options/quiet: true
make-dir %logs/ ;; make sure that there is the directory for logs
robots.txt: {User-agent: *^/Disallow: /}
humans.txt: {
__
( )
||
||
___|""|__.._
/____________\
\____________/~~~> http://github.com/oldes/
}
serve-http [
port: 8081
;- Main server configuration
root: %hosts/test/
server-name: "nginx" ;= it's possible to hide real server name
keep-alive: [30 100] ;= [timeout max-requests] or FALSE to turn it off
log-access: %logs/test-access.log
log-errors: %logs/test-errors.log
list-dir?: true
trust-ips: [127.0.0.1]
;- Server's actor functions
actor: [
On-Accept: func [ctx][
; allow only connections from localhost
; TRUE = accepted, FALSE = refuse
find ctx/config/trust-ips ctx/remote-ip
]
On-Header: func [ctx [object!] /local path key][
path: ctx/inp/target/file
;- detect some of common hacking attempts...
unless parse path anti-hacking-rules [
ctx/out/status: 418 ;= I'm a teapot
ctx/out/header/Content-Type: "text/plain; charset=UTF-8"
ctx/out/content: "Your silly hacking attempt was detected!"
exit
]
;- serve valid content...
switch path [
%/form/ [
; path rewrite...
; http://localhost:8081/form/ is now same like http://localhost:8081/form.html
ctx/inp/target/file: %/form.html
; request processing will continue
]
%/form.htm
%/form.html [
ctx/out/status: 301 ;= Moved Permanently
ctx/out/header/Location: %/form/
; request processing will stop with redirection response
]
%/plain/ [
ctx/out/status: 200
ctx/out/header/Content-Type: "text/plain; charset=UTF-8"
ctx/out/content: "hello"
; request processing will stop with response 200 serving the plain text content
]
%/ip [
; service to resolve the remote ip like: http://ifconfig.me/ip
ctx/out/status: 200
ctx/out/header/Content-Type: "text/plain"
ctx/out/content: form ctx/remote-ip
]
%/header [
ctx/out/status: 200
ctx/out/header/Content-Type: "text/plain"
ctx/out/content: ajoin [
"Request input:" mold ctx/inp
]
]
%/echo [
;@@ Consider checking the ctx/out/header/Origin value
;@@ before accepting websocket connection upgrade!
system/schemes/httpd/actor/WS-handshake ctx
]
]
]
On-Post: func [ctx [object!] /local data][
if ctx/inp/target/file = %/api/v2/do [
;- A primitive API example
try/with [
;?? ctx/inp
data: ctx/inp/content
unless map? data [data: to map! ctx/inp/content/1] ;;= url-encoded input
;; using plain secret in this example,
;; but it should be replaced with something better in the real life
if data/token <> "SOME_SECRET" [
ctx/out/status: 401 ;= Unauthorized
exit
]
;; reusing the input for an output
;; without the token...
remove/key data 'token
;; but with result of the input expression...
;@@ NOTE that there is no security setting, so the code may be dangerous!
data/result: mold/all try load data/code
ctx/out/header/Content-Type: "application/json"
ctx/out/content: to-json data
][
ctx/out/status: 400 ;= Bad request
]
exit
]
;; else just return what we received...
ctx/out/content: ajoin [
"<br/>Request header:<pre>" mold ctx/inp/header </pre>
"Received <code>" ctx/inp/header/Content-Type/1
"</code> data:<pre>" mold ctx/inp/content </pre>
]
]
On-Not-Found: func[ctx][
;; Here may be provided custom content, when requested file is not found
unless parse ctx/inp/target/file [
;; we must work with an absolute path!
ctx/config/root [
;-- serving the content directly from the memory
%humans.txt (ctx/out/content: humans.txt) ;@@ https://codeburst.io/all-about-humans-humans-txt-actually-f571d37f92d2
| %robots.txt (ctx/out/content: robots.txt) ;@@ https://developers.google.com/search/docs/crawling-indexing/robots/create-robots-txt
| %bot-trap/ (ctx/out/content: ajoin ["Welcome bot: " ctx/inp/header/User-Agent])
]
][
ctx/out/status: 404
;; including an optional message...
ctx/out/content: "Content not found on this server!"
]
]
;-- WebSocket related actions
On-Read-Websocket: func[ctx final? opcode][
print ["WS opcode:" opcode "final frame:" final?]
either opcode = 1 [
probe ctx/out/content: to string! ctx/inp/content
][
? ctx/inp/content
]
]
On-Close-Websocket: func[ctx code /local reason][
reason: any [
select [
1000 "the purpose for which the connection was established has been fulfilled."
1001 "a browser navigated away from a page."
1002 "a protocol error."
1003 "it has received a type of data it cannot accept."
1007 "it has received data within a message that was not consistent with the type of the message."
1008 "it has received a message that violates its policy."
1009 "it has received a message that is too big for it to process."
1010 "it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake."
1011 "it encountered an unexpected condition that prevented it from fulfilling the request."
] code
ajoin ["an unknown reason (" code ")"]
]
print ["WS connection is closing because" reason]
unless empty? reason: ctx/inp/content [
;; optional client's reason
print ["Client's reason:" as-red to string! reason]
]
]
] ;end of actors
]