Skip to content

Commit 38df5c9

Browse files
committed
FEAT: added support for HTTP redirection
fixes: metaeducation/rebol-issues#631 (partially as example from the issue is now redirecting to HTTPS)
1 parent 3b89ed9 commit 38df5c9

File tree

1 file changed

+74
-23
lines changed

1 file changed

+74
-23
lines changed

src/mezz/prot-http.r

+74-23
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ idate-to-date: function [date [string!]] [
4949
]
5050
;@@==================================================
5151

52+
;@@ just simple trace function
53+
;@@net-log: :print
5254

5355
sync-op: func [port body /local state] [
5456
unless port/state [open port port/state/close?: yes]
@@ -60,14 +62,26 @@ sync-op: func [port body /local state] [
6062
;The timeout should be triggered only when the response from other side exceeds the timeout value.
6163
;--Richard
6264
while [not find [ready close] state/state][
65+
;@@net-log ["########### sync-op.." state/state]
6366
unless port? wait [state/connection port/spec/timeout] [http-error "Timeout"]
64-
if state/state = 'reading-data [read state/connection]
67+
;@@net-log ["########### sync-op wakeup" state/state]
68+
switch state/state [
69+
reading-data [
70+
read state/connection
71+
]
72+
redirect [
73+
do-redirect port port/state/info/headers/location
74+
state: port/state
75+
state/awake: :read-sync-awake
76+
]
77+
]
6578
]
6679
body: copy port
6780
if state/close? [close port]
6881
body
6982
]
7083
read-sync-awake: func [event [event!] /local error] [
84+
;@@net-log ["[HTTP read-sync-awake]" event/type]
7185
switch/default event/type [
7286
connect ready [
7387
do-request event/port
@@ -79,6 +93,13 @@ read-sync-awake: func [event [event!] /local error] [
7993
close [
8094
true
8195
]
96+
custom [
97+
if event/offset/x = 300 [
98+
event/port/state/state: 'redirect
99+
return true
100+
]
101+
false
102+
]
82103
error [
83104
error: event/port/state/error
84105
event/port/state/error: none
@@ -94,6 +115,11 @@ http-awake: func [event /local port http-port state awake res] [
94115
state: http-port/state
95116
if any-function? :http-port/awake [state/awake: :http-port/awake]
96117
awake: :state/awake
118+
119+
;@@net-log ["[HTTP http-awake]" event/type]
120+
121+
;?? awake
122+
97123
switch/default event/type [
98124
read [
99125
awake make event! [type: 'read port: http-port]
@@ -200,9 +226,10 @@ do-request: func [
200226
port/state/state: 'doing-request
201227
info/headers: info/response-line: info/response-parsed: port/data:
202228
info/size: info/date: info/name: none
203-
write port/state/connection
204-
make-http-request spec/method to file! any [spec/path %/]
205-
spec/headers spec/content
229+
230+
;@@net-log ["[HTTP do-request]" spec/method spec/host spec/path]
231+
232+
write port/state/connection make-http-request spec/method to file! any [spec/path %/] spec/headers spec/content
206233
]
207234
parse-write-dialect: func [port block /local spec] [
208235
spec: port/spec
@@ -218,6 +245,9 @@ check-response: func [port /local conn res headers d1 d2 line info state awake s
218245
line: info/response-line
219246
awake: :state/awake
220247
spec: port/spec
248+
249+
;@@net-log ["[HTTP check-response]" info/response-parsed]
250+
221251
if all [
222252
not headers
223253
d1: find conn/data crlfbin
@@ -267,6 +297,9 @@ check-response: func [port /local conn res headers d1 d2 line info state awake s
267297
| (info/response-parsed: 'version-not-supported)
268298
]
269299
]
300+
;?? info/response-parsed
301+
;?? spec/method
302+
270303
switch/all info/response-parsed [
271304
ok [
272305
either spec/method = 'head [
@@ -290,9 +323,13 @@ check-response: func [port /local conn res headers d1 d2 line info state awake s
290323
unless open? port [
291324
;NOTE some servers(e.g. yahoo.com) don't supply content-data in the redirect header so the state/state can be left in 'reading-data after check-data call
292325
;I think it is better to check if port has been closed here and set the state so redirect sequence can happen. --Richard
293-
state/state: 'ready
326+
state/state: 'redirect ;ready
294327
]
295328
]
329+
;?? res
330+
;?? headers
331+
;?? state/state
332+
296333
if all [not res state/state = 'ready] [
297334
either all [
298335
any [
@@ -304,7 +341,7 @@ check-response: func [port /local conn res headers d1 d2 line info state awake s
304341
]
305342
in headers 'Location
306343
] [
307-
res: do-redirect port headers/location
344+
return awake make event! [type: 'custom port: port code: 300]
308345
] [
309346
state/error: make-http-error "Redirect requires manual intervention"
310347
res: awake make event! [type: 'error port: port]
@@ -379,30 +416,34 @@ do-redirect: func [port [port!] new-uri [url! string! file!] /local spec state]
379416
]
380417
]
381418
new-uri: construct/with new-uri port/scheme/spec
419+
new-uri/ref: to url! ajoin [new-uri/scheme "://" new-uri/host new-uri/path]
420+
new-uri/method: spec/method
421+
422+
;@@net-log ["[HTTP do-redirect] new-uri:" mold new-uri]
423+
;?? port
424+
382425
unless find [http https] new-uri/scheme [
383426
state/error: make-http-error {Redirect to a protocol different from HTTP or HTTPS not supported}
384427
return state/awake make event! [type: 'error port: port]
385428
]
386-
either all [
387-
new-uri/host = spec/host
388-
new-uri/port-id = spec/port-id
389-
] [
390-
spec/path: new-uri/path
391-
;we need to reset tcp connection here before doing a redirect
392-
close port/state/connection
393-
open port/state/connection
394-
do-request port
395-
false
396-
] [
397-
state/error: make-http-error "Redirect to other host - requires custom handling"
398-
state/awake make event! [type: 'error port: port]
399-
]
429+
430+
;we need to reset tcp connection here before doing a redirect
431+
clear spec/headers
432+
port/data: none
433+
close port/state/connection
434+
435+
port/spec: spec: new-uri
436+
port/state: none
437+
open port
400438
]
401439
check-data: func [port /local headers res data out chunk-size mk1 mk2 trailer state conn] [
402440
state: port/state
403441
headers: state/info/headers
404442
conn: state/connection
405443
res: false
444+
445+
;@@net-log ["[HTTP check-data] bytes:" length? conn/data]
446+
406447
case [
407448
headers/transfer-encoding = "chunked" [
408449
data: conn/data
@@ -449,7 +490,7 @@ check-data: func [port /local headers res data out chunk-size mk1 mk2 trailer st
449490
port/data: conn/data
450491
either headers/content-length <= length? port/data [
451492
state/state: 'ready
452-
conn/data: make binary! 32000
493+
conn/data: make binary! 32000 ;@@ Oldes: why not just none?
453494
res: state/awake make event! [type: 'custom port: port code: 0]
454495
] [
455496
;Awake from the WAIT loop to prevent timeout when reading big data. --Richard
@@ -489,6 +530,7 @@ sys/make-scheme [
489530
read: func [
490531
port [port!]
491532
] [
533+
;@@net-log "[HTTP read]"
492534
either any-function? :port/awake [
493535
unless open? port [cause-error 'Access 'not-open port/spec/ref]
494536
if port/state/state <> 'ready [http-error "Port not ready"]
@@ -503,8 +545,11 @@ sys/make-scheme [
503545
port [port!]
504546
value
505547
] [
548+
;@@net-log "[HTTP write]"
549+
;?? port
506550
unless any [block? :value binary? :value any-string? :value] [value: form :value]
507551
unless block? value [value: reduce [[Content-Type: "application/x-www-form-urlencoded; charset=utf-8"] value]]
552+
508553
either any-function? :port/awake [
509554
unless open? port [cause-error 'Access 'not-open port/spec/ref]
510555
if port/state/state <> 'ready [http-error "Port not ready"]
@@ -520,6 +565,7 @@ sys/make-scheme [
520565
port [port!]
521566
/local conn
522567
] [
568+
;@@net-log ["[HTTP open]" port/state]
523569
if port/state [return port]
524570
if none? port/spec/host [http-error "Missing host address"]
525571
port/state: context [
@@ -534,10 +580,13 @@ sys/make-scheme [
534580
scheme: (to lit-word! either port/spec/scheme = 'http ['tcp]['tls])
535581
host: port/spec/host
536582
port-id: port/spec/port-id
537-
ref: rejoin [tcp:// host ":" port-id]
583+
ref: rejoin [to url! scheme "://" host #":" port-id]
538584
]
585+
;?? conn
539586
conn/awake: :http-awake
540587
conn/locals: port
588+
;@@net-log ["[HTTP opne]" conn/spec/scheme conn/spec/host]
589+
;?? conn
541590
open conn
542591
port
543592
]
@@ -549,6 +598,7 @@ sys/make-scheme [
549598
close: func [
550599
port [port!]
551600
] [
601+
;@@net-log "[HTTP close]"
552602
if port/state [
553603
close port/state/connection
554604
port/state/connection/awake: none
@@ -577,8 +627,9 @@ sys/make-scheme [
577627
state: port/state
578628
close port
579629
]
630+
;?? state
580631
if none? state [return none]
581-
either state/info/response-parsed = 'ok [
632+
either find [ok redirect] state/info/response-parsed [
582633
state/info
583634
][ none ]
584635
]

0 commit comments

Comments
 (0)