A server-side web framework written in Forth. http://www.1-9-9-1.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.9KB

  1. \ 1991
  2. include unix/socket.fs
  3. \ Helper words
  4. : +s ( addr1 u1 addr2 u2 -- addr3 u3 ) \ like s+ but prepend rather than append.
  5. 2swap s+ ;
  6. : exchange ( a1 a2 -- )
  7. 2dup c@ swap c@ rot c! swap c! ;
  8. : reverse ( caddr u -- ) \ reverse a string
  9. 1- bounds begin 2dup > while
  10. 2dup exchange
  11. -1 /string
  12. repeat 2drop ;
  13. \ User-defined routing
  14. wordlist constant routes
  15. : find-route ( addr u -- data )
  16. routes search-wordlist if
  17. >body @
  18. else 0 then ;
  19. : register-route ( data addr u -- )
  20. 2dup routes search-wordlist if
  21. routes drop nip nip
  22. >body !
  23. else
  24. routes get-current >r set-current \ switch definition word lists
  25. nextname create ,
  26. r> set-current
  27. then ;
  28. \ Public directory
  29. : pubvar create 0 , 0 , ;
  30. pubvar public
  31. : set-public-path ( addr u -- )
  32. public 2! ;
  33. : get-public-path ( -- addr u )
  34. public 2@ ;
  35. \ Request's Content-Type
  36. pubvar RequestContentType
  37. : set-content-type ( addr u -- )
  38. RequestContentType 2! ;
  39. : get-content-type ( -- addr u )
  40. RequestContentType 2@ ;
  41. : filetype: ( addr u "extension" -- ) \ takes a content-type and the extension
  42. create here over 1+ allot place
  43. does> count ;
  44. : get-filetype ( addr u -- caddr cu ) \ takes an extension, looks to find a definition
  45. find-name dup if
  46. name>int
  47. execute
  48. else
  49. drop
  50. s" text/plain"
  51. then ;
  52. s" text/plain" filetype: txt \ txt should always be defined
  53. s" text/html" filetype: html
  54. s" text/css" filetype: css
  55. s" text/javascript" filetype: js
  56. s" image/png" filetype: png
  57. s" image/gif" filetype: gif
  58. s" image/jpeg" filetype: jpg
  59. s" image/jpeg" filetype: jpeg
  60. s" image/x-icon" filetype: ico
  61. \ Internal request handling
  62. : HTTP/1.1 s" HTTP/1.1 " ;
  63. : response-status ( u -- addr u )
  64. dup case \ get status code info
  65. 200 of s" OK" endof
  66. 404 of s" Not Found" endof
  67. endcase
  68. s\" \n" s+
  69. rot s>d <# #s #> +s \ convert status code to string and prepend to info
  70. HTTP/1.1 +s ; \ prepend HTTP/1.1
  71. : content-type ( addr u -- caddr cu )
  72. s" Content-Type: " +s \ Prepend to the provided content type
  73. s\" \n\n" s+ ; \ Append 2 new lines
  74. : set-header ( u addr u -- raddr ru ) \ Accepts status code and content type string
  75. rot response-status \ Set response status
  76. 2swap content-type \ Set content-type
  77. s+ ; \ Join
  78. : read-request ( socket -- addr u )
  79. pad 4096 read-socket ;
  80. : send-response ( addr u socket -- )
  81. dup >r write-socket r> close-socket ;
  82. : requested-route ( addr u -- routeaddr routeu )
  83. bl scan 1- swap 1+ swap 2dup bl scan swap drop - ;
  84. : file-exists? ( addr u -- addr u bool )
  85. 2dup file-status nip 0= ;
  86. : .extension ( addr u -- addr u )
  87. 2dup reverse \ reverse the file name
  88. 2dup s" ." search \ search for the first occurance of "."
  89. if
  90. swap drop - \ remove the "." from the search results
  91. else
  92. s" txt"
  93. then
  94. 2dup reverse ; \ reverse reversed extension
  95. : serve-file-type ( addr u -- )
  96. .extension get-filetype set-content-type ;
  97. : serve-file ( addr u -- addr u )
  98. slurp-file ;
  99. : 404content-type txt ;
  100. : 404html s" 404";
  101. : either-resolve ( addr u -- resolveaddr resolveu )
  102. s" GET" search if
  103. s" html" get-filetype set-content-type \ reset the request's content-type
  104. requested-route
  105. 2dup find-route dup if
  106. >r 2drop r> \ keep xt, drop the route string
  107. execute \ execute the user's route handler
  108. else
  109. drop \ drop the null xt
  110. get-public-path +s \ see if route exists in public dir
  111. file-exists? if
  112. 2dup serve-file \ collect file contents
  113. 2swap serve-file-type \ set the file type
  114. else
  115. exit \ continue to 404
  116. then
  117. then
  118. 200 get-content-type set-header +s
  119. rdrop exit then ;
  120. : or-404 ( addr u -- 404addr 404u )
  121. 2drop
  122. 404 404content-type set-header 404html s+ ;
  123. : prepare-response ( addr u -- returnaddr returnu)
  124. either-resolve or-404 ;
  125. : start-server { server client }
  126. begin
  127. server 255 listen
  128. server accept-socket to client
  129. client read-request prepare-response client send-response
  130. again ;
  131. \ Userland
  132. : 1991: ( port -- )
  133. create-server 0 start-server ;
  134. : /1991 ( "<path> <word>" -- )
  135. bl word ' swap count register-route ;