kibi-swipl-linux-x64-gnu 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. package/README.md +29 -0
  2. package/bin/swipl +0 -0
  3. package/bin/swipl-win +0 -0
  4. package/build-manifest.json +70 -0
  5. package/lib/cmake/swipl/SWIPLConfig.cmake +21 -0
  6. package/lib/cmake/swipl/SWIPLConfigVersion.cmake +65 -0
  7. package/lib/cmake/swipl/SWIPLTargets-release.cmake +28 -0
  8. package/lib/cmake/swipl/SWIPLTargets.cmake +109 -0
  9. package/lib/swipl/ABI +1 -0
  10. package/lib/swipl/LICENSE +41 -0
  11. package/lib/swipl/README.md +116 -0
  12. package/lib/swipl/app/README.md +25 -0
  13. package/lib/swipl/app/app.pl +87 -0
  14. package/lib/swipl/app/pack.pl +350 -0
  15. package/lib/swipl/app/qlf.pl +521 -0
  16. package/lib/swipl/app/splfr.pl +177 -0
  17. package/lib/swipl/app/sys.pl +310 -0
  18. package/lib/swipl/bin/swipl.home +1 -0
  19. package/lib/swipl/boot/apply.pl +73 -0
  20. package/lib/swipl/boot/attvar.pl +344 -0
  21. package/lib/swipl/boot/autoload.pl +1226 -0
  22. package/lib/swipl/boot/bags.pl +283 -0
  23. package/lib/swipl/boot/dcg.pl +388 -0
  24. package/lib/swipl/boot/dicts.pl +193 -0
  25. package/lib/swipl/boot/dwim.pl +485 -0
  26. package/lib/swipl/boot/engines.pl +152 -0
  27. package/lib/swipl/boot/expand.pl +1628 -0
  28. package/lib/swipl/boot/gc.pl +65 -0
  29. package/lib/swipl/boot/history.pl +304 -0
  30. package/lib/swipl/boot/init.pl +4682 -0
  31. package/lib/swipl/boot/iri.pl +64 -0
  32. package/lib/swipl/boot/license.pl +390 -0
  33. package/lib/swipl/boot/load.pl +81 -0
  34. package/lib/swipl/boot/messages.pl +2521 -0
  35. package/lib/swipl/boot/packs.pl +306 -0
  36. package/lib/swipl/boot/predopts.pl +154 -0
  37. package/lib/swipl/boot/qlf.pl +141 -0
  38. package/lib/swipl/boot/rc.pl +263 -0
  39. package/lib/swipl/boot/syspred.pl +1634 -0
  40. package/lib/swipl/boot/tabling.pl +2321 -0
  41. package/lib/swipl/boot/threads.pl +42 -0
  42. package/lib/swipl/boot/toplevel.pl +2248 -0
  43. package/lib/swipl/boot/topvars.pl +261 -0
  44. package/lib/swipl/boot.prc +0 -0
  45. package/lib/swipl/cmake/swipl.cmake +131 -0
  46. package/lib/swipl/customize/README.md +23 -0
  47. package/lib/swipl/customize/edit +101 -0
  48. package/lib/swipl/customize/init.pl +164 -0
  49. package/lib/swipl/demo/README.md +33 -0
  50. package/lib/swipl/demo/likes.pl +44 -0
  51. package/lib/swipl/doc/packages/examples/chr/bool.chr +281 -0
  52. package/lib/swipl/doc/packages/examples/chr/chrdif.chr +87 -0
  53. package/lib/swipl/doc/packages/examples/chr/chrfreeze.chr +6 -0
  54. package/lib/swipl/doc/packages/examples/chr/family.chr +116 -0
  55. package/lib/swipl/doc/packages/examples/chr/fib.chr +24 -0
  56. package/lib/swipl/doc/packages/examples/chr/fibonacci.chr +31 -0
  57. package/lib/swipl/doc/packages/examples/chr/gcd.chr +28 -0
  58. package/lib/swipl/doc/packages/examples/chr/leq.chr +34 -0
  59. package/lib/swipl/doc/packages/examples/chr/listdom.chr +138 -0
  60. package/lib/swipl/doc/packages/examples/chr/primes.chr +29 -0
  61. package/lib/swipl/doc/packages/examples/http/README.md +98 -0
  62. package/lib/swipl/doc/packages/examples/http/calc.pl +92 -0
  63. package/lib/swipl/doc/packages/examples/http/demo_body.pl +232 -0
  64. package/lib/swipl/doc/packages/examples/http/demo_client.pl +165 -0
  65. package/lib/swipl/doc/packages/examples/http/demo_daemon.pl +38 -0
  66. package/lib/swipl/doc/packages/examples/http/demo_files.pl +50 -0
  67. package/lib/swipl/doc/packages/examples/http/demo_hello.pl +15 -0
  68. package/lib/swipl/doc/packages/examples/http/demo_login.pl +280 -0
  69. package/lib/swipl/doc/packages/examples/http/demo_openid.pl +177 -0
  70. package/lib/swipl/doc/packages/examples/http/demo_pwp.pl +20 -0
  71. package/lib/swipl/doc/packages/examples/http/demo_rest.pl +56 -0
  72. package/lib/swipl/doc/packages/examples/http/demo_threads.pl +52 -0
  73. package/lib/swipl/doc/packages/examples/http/linux-init-script +126 -0
  74. package/lib/swipl/doc/packages/examples/http/pwp/context.pwp +23 -0
  75. package/lib/swipl/doc/packages/examples/http/pwp/index.pwp +25 -0
  76. package/lib/swipl/doc/packages/examples/http/pwp/pwp1.pwp +12 -0
  77. package/lib/swipl/doc/packages/examples/http/pwp/pwp2.pwp +10 -0
  78. package/lib/swipl/doc/packages/examples/http/pwp/pwp3.pwp +25 -0
  79. package/lib/swipl/doc/packages/examples/http/pwp/pwp4.pwp +23 -0
  80. package/lib/swipl/doc/packages/examples/http/pwp/pwp5.pwp +11 -0
  81. package/lib/swipl/doc/packages/examples/http/pwp/pwp6.pwp +3 -0
  82. package/lib/swipl/doc/packages/examples/http/pwp/pwp7.pwp +3 -0
  83. package/lib/swipl/doc/packages/examples/http/pwp/pwp8.pwp +3 -0
  84. package/lib/swipl/doc/packages/examples/http/pwp/pwpdb.pl +12 -0
  85. package/lib/swipl/doc/packages/examples/http/systemd-script.service +18 -0
  86. package/lib/swipl/doc/packages/examples/http/upstart-script.conf +20 -0
  87. package/lib/swipl/doc/packages/examples/plunit/read.pl +17 -0
  88. package/lib/swipl/doc/packages/examples/plunit/simple.pl +33 -0
  89. package/lib/swipl/doc/packages/examples/ssl/client.pl +83 -0
  90. package/lib/swipl/doc/packages/examples/ssl/etc/README +52 -0
  91. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.p12 +0 -0
  92. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.pem +85 -0
  93. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-key.pem +30 -0
  94. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/cacert.pem +82 -0
  95. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/careq.pem +18 -0
  96. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/crlnumber +1 -0
  97. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt +3 -0
  98. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt.attr +1 -0
  99. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7C.pem +85 -0
  100. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7D.pem +85 -0
  101. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/private/cakey.pem +30 -0
  102. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/serial +1 -0
  103. package/lib/swipl/doc/packages/examples/ssl/etc/server/server-cert.pem +85 -0
  104. package/lib/swipl/doc/packages/examples/ssl/etc/server/server-key.pem +30 -0
  105. package/lib/swipl/doc/packages/examples/ssl/https.pl +267 -0
  106. package/lib/swipl/doc/packages/examples/ssl/server.pl +106 -0
  107. package/lib/swipl/include/SWI-Prolog.h +1544 -0
  108. package/lib/swipl/include/SWI-Stream.h +537 -0
  109. package/lib/swipl/include/Yap/YapInterface.h +283 -0
  110. package/lib/swipl/include/sicstus/sicstus.h +452 -0
  111. package/lib/swipl/lib/x86_64-linux/cgi.so +0 -0
  112. package/lib/swipl/lib/x86_64-linux/crypt.so +0 -0
  113. package/lib/swipl/lib/x86_64-linux/crypto4pl.so +0 -0
  114. package/lib/swipl/lib/x86_64-linux/double_metaphone.so +0 -0
  115. package/lib/swipl/lib/x86_64-linux/files.so +0 -0
  116. package/lib/swipl/lib/x86_64-linux/hashstream.so +0 -0
  117. package/lib/swipl/lib/x86_64-linux/http_stream.so +0 -0
  118. package/lib/swipl/lib/x86_64-linux/isub.so +0 -0
  119. package/lib/swipl/lib/x86_64-linux/json.so +0 -0
  120. package/lib/swipl/lib/x86_64-linux/libswipl.so.10 +0 -0
  121. package/lib/swipl/lib/x86_64-linux/mallocinfo.so +0 -0
  122. package/lib/swipl/lib/x86_64-linux/md54pl.so +0 -0
  123. package/lib/swipl/lib/x86_64-linux/memfile.so +0 -0
  124. package/lib/swipl/lib/x86_64-linux/ntriples.so +0 -0
  125. package/lib/swipl/lib/x86_64-linux/pcre4pl.so +0 -0
  126. package/lib/swipl/lib/x86_64-linux/porter_stem.so +0 -0
  127. package/lib/swipl/lib/x86_64-linux/process.so +0 -0
  128. package/lib/swipl/lib/x86_64-linux/prolog_stream.so +0 -0
  129. package/lib/swipl/lib/x86_64-linux/rdf_db.so +0 -0
  130. package/lib/swipl/lib/x86_64-linux/readutil.so +0 -0
  131. package/lib/swipl/lib/x86_64-linux/rlimit.so +0 -0
  132. package/lib/swipl/lib/x86_64-linux/sched.so +0 -0
  133. package/lib/swipl/lib/x86_64-linux/sgml2pl.so +0 -0
  134. package/lib/swipl/lib/x86_64-linux/sha4pl.so +0 -0
  135. package/lib/swipl/lib/x86_64-linux/snowball.so +0 -0
  136. package/lib/swipl/lib/x86_64-linux/socket.so +0 -0
  137. package/lib/swipl/lib/x86_64-linux/ssl4pl.so +0 -0
  138. package/lib/swipl/lib/x86_64-linux/streaminfo.so +0 -0
  139. package/lib/swipl/lib/x86_64-linux/syslog.so +0 -0
  140. package/lib/swipl/lib/x86_64-linux/time.so +0 -0
  141. package/lib/swipl/lib/x86_64-linux/turtle.so +0 -0
  142. package/lib/swipl/lib/x86_64-linux/uid.so +0 -0
  143. package/lib/swipl/lib/x86_64-linux/unix.so +0 -0
  144. package/lib/swipl/lib/x86_64-linux/uri.so +0 -0
  145. package/lib/swipl/lib/x86_64-linux/websocket.so +0 -0
  146. package/lib/swipl/lib/x86_64-linux/zlib4pl.so +0 -0
  147. package/lib/swipl/library/INDEX.pl +1020 -0
  148. package/lib/swipl/library/aggregate.pl +755 -0
  149. package/lib/swipl/library/ansi_term.pl +653 -0
  150. package/lib/swipl/library/apply.pl +426 -0
  151. package/lib/swipl/library/apply_macros.pl +497 -0
  152. package/lib/swipl/library/arithmetic.pl +320 -0
  153. package/lib/swipl/library/assoc.pl +522 -0
  154. package/lib/swipl/library/atom.pl +169 -0
  155. package/lib/swipl/library/backcomp.pl +688 -0
  156. package/lib/swipl/library/base32.pl +291 -0
  157. package/lib/swipl/library/base64.pl +441 -0
  158. package/lib/swipl/library/broadcast.pl +161 -0
  159. package/lib/swipl/library/build/cmake.pl +137 -0
  160. package/lib/swipl/library/build/conan.pl +232 -0
  161. package/lib/swipl/library/build/make.pl +168 -0
  162. package/lib/swipl/library/build/tools.pl +825 -0
  163. package/lib/swipl/library/charsio.pl +195 -0
  164. package/lib/swipl/library/check.pl +1097 -0
  165. package/lib/swipl/library/check_installation.pl +912 -0
  166. package/lib/swipl/library/clp/INDEX.pl +157 -0
  167. package/lib/swipl/library/clp/bounds.pl +2036 -0
  168. package/lib/swipl/library/clp/clp_distinct.pl +263 -0
  169. package/lib/swipl/library/clp/clp_events.pl +92 -0
  170. package/lib/swipl/library/clp/clpb.pl +1896 -0
  171. package/lib/swipl/library/clp/clpfd.pl +7906 -0
  172. package/lib/swipl/library/clp/simplex.pl +1580 -0
  173. package/lib/swipl/library/codesio.pl +182 -0
  174. package/lib/swipl/library/coinduction.pl +183 -0
  175. package/lib/swipl/library/console_input.pl +289 -0
  176. package/lib/swipl/library/csv.pl +611 -0
  177. package/lib/swipl/library/ctypes.pl +161 -0
  178. package/lib/swipl/library/date.pl +370 -0
  179. package/lib/swipl/library/dcg/INDEX.pl +35 -0
  180. package/lib/swipl/library/dcg/basics.pl +514 -0
  181. package/lib/swipl/library/dcg/high_order.pl +227 -0
  182. package/lib/swipl/library/debug.pl +499 -0
  183. package/lib/swipl/library/dialect/bim.pl +186 -0
  184. package/lib/swipl/library/dialect/commons.pl +77 -0
  185. package/lib/swipl/library/dialect/eclipse/test_util_iso.pl +355 -0
  186. package/lib/swipl/library/dialect/hprolog/format.pl +44 -0
  187. package/lib/swipl/library/dialect/hprolog.pl +314 -0
  188. package/lib/swipl/library/dialect/ifprolog.pl +1317 -0
  189. package/lib/swipl/library/dialect/iso/iso_predicates.pl +277 -0
  190. package/lib/swipl/library/dialect/sicstus/README.TXT +1 -0
  191. package/lib/swipl/library/dialect/sicstus/arrays.pl +121 -0
  192. package/lib/swipl/library/dialect/sicstus/block.pl +278 -0
  193. package/lib/swipl/library/dialect/sicstus/lists.pl +167 -0
  194. package/lib/swipl/library/dialect/sicstus/ordsets.pl +52 -0
  195. package/lib/swipl/library/dialect/sicstus/sockets.pl +174 -0
  196. package/lib/swipl/library/dialect/sicstus/system.pl +262 -0
  197. package/lib/swipl/library/dialect/sicstus/terms.pl +57 -0
  198. package/lib/swipl/library/dialect/sicstus/timeout.pl +92 -0
  199. package/lib/swipl/library/dialect/sicstus.pl +582 -0
  200. package/lib/swipl/library/dialect/sicstus4/aggregate.pl +60 -0
  201. package/lib/swipl/library/dialect/sicstus4/between.pl +62 -0
  202. package/lib/swipl/library/dialect/sicstus4/clpfd.pl +75 -0
  203. package/lib/swipl/library/dialect/sicstus4/file_systems.pl +413 -0
  204. package/lib/swipl/library/dialect/sicstus4/lists.pl +274 -0
  205. package/lib/swipl/library/dialect/sicstus4/ordsets.pl +54 -0
  206. package/lib/swipl/library/dialect/sicstus4/samsort.pl +86 -0
  207. package/lib/swipl/library/dialect/sicstus4/sets.pl +124 -0
  208. package/lib/swipl/library/dialect/sicstus4/sockets.pl +164 -0
  209. package/lib/swipl/library/dialect/sicstus4/system.pl +80 -0
  210. package/lib/swipl/library/dialect/sicstus4/terms.pl +89 -0
  211. package/lib/swipl/library/dialect/sicstus4/timeout.pl +43 -0
  212. package/lib/swipl/library/dialect/sicstus4/types.pl +103 -0
  213. package/lib/swipl/library/dialect/sicstus4.pl +200 -0
  214. package/lib/swipl/library/dialect/swi/syspred_options.pl +250 -0
  215. package/lib/swipl/library/dialect/xsb/README.md +20 -0
  216. package/lib/swipl/library/dialect/xsb/basics.pl +318 -0
  217. package/lib/swipl/library/dialect/xsb/consult.pl +87 -0
  218. package/lib/swipl/library/dialect/xsb/curr_sym.pl +57 -0
  219. package/lib/swipl/library/dialect/xsb/error_handler.pl +111 -0
  220. package/lib/swipl/library/dialect/xsb/gensym.pl +53 -0
  221. package/lib/swipl/library/dialect/xsb/gpp.pl +138 -0
  222. package/lib/swipl/library/dialect/xsb/intern.pl +48 -0
  223. package/lib/swipl/library/dialect/xsb/lists.pl +47 -0
  224. package/lib/swipl/library/dialect/xsb/machine.pl +295 -0
  225. package/lib/swipl/library/dialect/xsb/ordsets.pl +53 -0
  226. package/lib/swipl/library/dialect/xsb/setof.pl +54 -0
  227. package/lib/swipl/library/dialect/xsb/source.pl +308 -0
  228. package/lib/swipl/library/dialect/xsb/standard.pl +169 -0
  229. package/lib/swipl/library/dialect/xsb/storage.pl +70 -0
  230. package/lib/swipl/library/dialect/xsb/string.pl +76 -0
  231. package/lib/swipl/library/dialect/xsb/thread.pl +62 -0
  232. package/lib/swipl/library/dialect/xsb/timed_call.pl +158 -0
  233. package/lib/swipl/library/dialect/xsb.pl +688 -0
  234. package/lib/swipl/library/dialect/yap/README.TXT +8 -0
  235. package/lib/swipl/library/dialect/yap.pl +283 -0
  236. package/lib/swipl/library/dialect.pl +123 -0
  237. package/lib/swipl/library/dicts.pl +399 -0
  238. package/lib/swipl/library/dif.pl +426 -0
  239. package/lib/swipl/library/edinburgh.pl +138 -0
  240. package/lib/swipl/library/edit.pl +783 -0
  241. package/lib/swipl/library/error.pl +495 -0
  242. package/lib/swipl/library/exceptions.pl +231 -0
  243. package/lib/swipl/library/explain.pl +543 -0
  244. package/lib/swipl/library/ext/RDF/INDEX.pl +25 -0
  245. package/lib/swipl/library/ext/RDF/rdf.pl +450 -0
  246. package/lib/swipl/library/ext/RDF/rdf_parser.pl +649 -0
  247. package/lib/swipl/library/ext/RDF/rdf_triple.pl +448 -0
  248. package/lib/swipl/library/ext/RDF/rdf_write.pl +701 -0
  249. package/lib/swipl/library/ext/RDF/rewrite_term.pl +177 -0
  250. package/lib/swipl/library/ext/chr/chr/a_star.pl +80 -0
  251. package/lib/swipl/library/ext/chr/chr/binomialheap.pl +142 -0
  252. package/lib/swipl/library/ext/chr/chr/builtins.pl +629 -0
  253. package/lib/swipl/library/ext/chr/chr/chr_compiler_errors.pl +179 -0
  254. package/lib/swipl/library/ext/chr/chr/chr_compiler_options.pl +382 -0
  255. package/lib/swipl/library/ext/chr/chr/chr_compiler_utility.pl +338 -0
  256. package/lib/swipl/library/ext/chr/chr/chr_debug.pl +59 -0
  257. package/lib/swipl/library/ext/chr/chr/chr_hashtable_store.pl +425 -0
  258. package/lib/swipl/library/ext/chr/chr/chr_integertable_store.pl +139 -0
  259. package/lib/swipl/library/ext/chr/chr/chr_messages.pl +176 -0
  260. package/lib/swipl/library/ext/chr/chr/chr_op.pl +53 -0
  261. package/lib/swipl/library/ext/chr/chr/chr_runtime.pl +1016 -0
  262. package/lib/swipl/library/ext/chr/chr/chr_translate.pl +25424 -0
  263. package/lib/swipl/library/ext/chr/chr/clean_code.pl +252 -0
  264. package/lib/swipl/library/ext/chr/chr/find.pl +78 -0
  265. package/lib/swipl/library/ext/chr/chr/guard_entailment.pl +19432 -0
  266. package/lib/swipl/library/ext/chr/chr/listmap.pl +108 -0
  267. package/lib/swipl/library/ext/chr/chr/pairlist.pl +78 -0
  268. package/lib/swipl/library/ext/chr/chr.pl +686 -0
  269. package/lib/swipl/library/ext/clib/INDEX.pl +158 -0
  270. package/lib/swipl/library/ext/clib/cgi.pl +86 -0
  271. package/lib/swipl/library/ext/clib/crypt.pl +44 -0
  272. package/lib/swipl/library/ext/clib/filesex.pl +644 -0
  273. package/lib/swipl/library/ext/clib/hash_stream.pl +119 -0
  274. package/lib/swipl/library/ext/clib/mallocinfo.pl +138 -0
  275. package/lib/swipl/library/ext/clib/md5.pl +68 -0
  276. package/lib/swipl/library/ext/clib/memfile.pl +61 -0
  277. package/lib/swipl/library/ext/clib/process.pl +573 -0
  278. package/lib/swipl/library/ext/clib/prolog_server.pl +157 -0
  279. package/lib/swipl/library/ext/clib/prolog_stream.pl +80 -0
  280. package/lib/swipl/library/ext/clib/rlimit.pl +54 -0
  281. package/lib/swipl/library/ext/clib/sched.pl +85 -0
  282. package/lib/swipl/library/ext/clib/sha.pl +162 -0
  283. package/lib/swipl/library/ext/clib/socket.pl +1102 -0
  284. package/lib/swipl/library/ext/clib/streaminfo.pl +103 -0
  285. package/lib/swipl/library/ext/clib/streampool.pl +136 -0
  286. package/lib/swipl/library/ext/clib/syslog.pl +180 -0
  287. package/lib/swipl/library/ext/clib/time.pl +194 -0
  288. package/lib/swipl/library/ext/clib/udp_broadcast.pl +975 -0
  289. package/lib/swipl/library/ext/clib/uid.pl +220 -0
  290. package/lib/swipl/library/ext/clib/unix.pl +317 -0
  291. package/lib/swipl/library/ext/clib/uri.pl +530 -0
  292. package/lib/swipl/library/ext/clib/uuid.pl +207 -0
  293. package/lib/swipl/library/ext/http/http/INDEX.pl +266 -0
  294. package/lib/swipl/library/ext/http/http/README.md +34 -0
  295. package/lib/swipl/library/ext/http/http/ax.pl +189 -0
  296. package/lib/swipl/library/ext/http/http/dcg_basics.pl +49 -0
  297. package/lib/swipl/library/ext/http/http/graphql.pl +1367 -0
  298. package/lib/swipl/library/ext/http/http/html_decl.pl +416 -0
  299. package/lib/swipl/library/ext/http/http/html_head.pl +654 -0
  300. package/lib/swipl/library/ext/http/http/html_quasiquotations.pl +120 -0
  301. package/lib/swipl/library/ext/http/http/html_write.pl +1425 -0
  302. package/lib/swipl/library/ext/http/http/htmx.pl +139 -0
  303. package/lib/swipl/library/ext/http/http/http_authenticate.pl +364 -0
  304. package/lib/swipl/library/ext/http/http/http_client.pl +412 -0
  305. package/lib/swipl/library/ext/http/http/http_cookie.pl +245 -0
  306. package/lib/swipl/library/ext/http/http/http_cors.pl +176 -0
  307. package/lib/swipl/library/ext/http/http/http_digest.pl +820 -0
  308. package/lib/swipl/library/ext/http/http/http_dirindex.pl +296 -0
  309. package/lib/swipl/library/ext/http/http/http_dispatch.pl +1552 -0
  310. package/lib/swipl/library/ext/http/http/http_dyn_workers.pl +214 -0
  311. package/lib/swipl/library/ext/http/http/http_error.pl +126 -0
  312. package/lib/swipl/library/ext/http/http/http_exception.pl +225 -0
  313. package/lib/swipl/library/ext/http/http/http_files.pl +163 -0
  314. package/lib/swipl/library/ext/http/http/http_header.pl +2820 -0
  315. package/lib/swipl/library/ext/http/http/http_hook.pl +106 -0
  316. package/lib/swipl/library/ext/http/http/http_host.pl +204 -0
  317. package/lib/swipl/library/ext/http/http/http_load.pl +113 -0
  318. package/lib/swipl/library/ext/http/http/http_log.pl +738 -0
  319. package/lib/swipl/library/ext/http/http/http_multipart_plugin.pl +232 -0
  320. package/lib/swipl/library/ext/http/http/http_open.pl +1821 -0
  321. package/lib/swipl/library/ext/http/http/http_openid.pl +1561 -0
  322. package/lib/swipl/library/ext/http/http/http_parameters.pl +504 -0
  323. package/lib/swipl/library/ext/http/http/http_path.pl +380 -0
  324. package/lib/swipl/library/ext/http/http/http_proxy.pl +80 -0
  325. package/lib/swipl/library/ext/http/http/http_pwp.pl +351 -0
  326. package/lib/swipl/library/ext/http/http/http_redis_plugin.pl +332 -0
  327. package/lib/swipl/library/ext/http/http/http_server.pl +105 -0
  328. package/lib/swipl/library/ext/http/http/http_server_files.pl +126 -0
  329. package/lib/swipl/library/ext/http/http/http_server_health.pl +186 -0
  330. package/lib/swipl/library/ext/http/http/http_session.pl +1040 -0
  331. package/lib/swipl/library/ext/http/http/http_sgml_plugin.pl +137 -0
  332. package/lib/swipl/library/ext/http/http/http_stream.pl +376 -0
  333. package/lib/swipl/library/ext/http/http/http_unix_daemon.pl +989 -0
  334. package/lib/swipl/library/ext/http/http/http_wrapper.pl +559 -0
  335. package/lib/swipl/library/ext/http/http/hub.pl +600 -0
  336. package/lib/swipl/library/ext/http/http/js_write.pl +461 -0
  337. package/lib/swipl/library/ext/http/http/mimepack.pl +224 -0
  338. package/lib/swipl/library/ext/http/http/mimetype.pl +276 -0
  339. package/lib/swipl/library/ext/http/http/term_html.pl +797 -0
  340. package/lib/swipl/library/ext/http/http/thread_httpd.pl +1181 -0
  341. package/lib/swipl/library/ext/http/http/web/css/dirindex.css +24 -0
  342. package/lib/swipl/library/ext/http/http/web/css/openid.css +57 -0
  343. package/lib/swipl/library/ext/http/http/web/icons/back.png +0 -0
  344. package/lib/swipl/library/ext/http/http/web/icons/c.png +0 -0
  345. package/lib/swipl/library/ext/http/http/web/icons/compressed.png +0 -0
  346. package/lib/swipl/library/ext/http/http/web/icons/folder.png +0 -0
  347. package/lib/swipl/library/ext/http/http/web/icons/generic.png +0 -0
  348. package/lib/swipl/library/ext/http/http/web/icons/layout.png +0 -0
  349. package/lib/swipl/library/ext/http/http/web/icons/openid-logo-square.png +0 -0
  350. package/lib/swipl/library/ext/http/http/web/icons/openid-logo-tiny.png +0 -0
  351. package/lib/swipl/library/ext/http/http/websocket.pl +729 -0
  352. package/lib/swipl/library/ext/http/http/yadis.pl +205 -0
  353. package/lib/swipl/library/ext/json/INDEX.pl +36 -0
  354. package/lib/swipl/library/ext/json/http/INDEX.pl +16 -0
  355. package/lib/swipl/library/ext/json/http/http_json.pl +490 -0
  356. package/lib/swipl/library/ext/json/http/js_grammar.pl +53 -0
  357. package/lib/swipl/library/ext/json/http/json.pl +48 -0
  358. package/lib/swipl/library/ext/json/http/json_convert.pl +48 -0
  359. package/lib/swipl/library/ext/json/json.pl +1215 -0
  360. package/lib/swipl/library/ext/json/json_convert.pl +681 -0
  361. package/lib/swipl/library/ext/json/json_grammar.pl +349 -0
  362. package/lib/swipl/library/ext/json/json_rpc_client.pl +423 -0
  363. package/lib/swipl/library/ext/json/json_rpc_common.pl +104 -0
  364. package/lib/swipl/library/ext/json/json_rpc_server.pl +489 -0
  365. package/lib/swipl/library/ext/json/json_schema.pl +2111 -0
  366. package/lib/swipl/library/ext/nlp/INDEX.pl +15 -0
  367. package/lib/swipl/library/ext/nlp/double_metaphone.pl +86 -0
  368. package/lib/swipl/library/ext/nlp/isub.pl +162 -0
  369. package/lib/swipl/library/ext/nlp/porter_stem.pl +50 -0
  370. package/lib/swipl/library/ext/nlp/snowball.pl +97 -0
  371. package/lib/swipl/library/ext/pcre/INDEX.pl +17 -0
  372. package/lib/swipl/library/ext/pcre/pcre.pl +974 -0
  373. package/lib/swipl/library/ext/plunit/INDEX.pl +20 -0
  374. package/lib/swipl/library/ext/plunit/plunit.pl +2631 -0
  375. package/lib/swipl/library/ext/plunit/test_wizard.pl +249 -0
  376. package/lib/swipl/library/ext/semweb/semweb/INDEX.pl +288 -0
  377. package/lib/swipl/library/ext/semweb/semweb/rdf11.pl +2106 -0
  378. package/lib/swipl/library/ext/semweb/semweb/rdf11_containers.pl +299 -0
  379. package/lib/swipl/library/ext/semweb/semweb/rdf_cache.pl +278 -0
  380. package/lib/swipl/library/ext/semweb/semweb/rdf_compare.pl +132 -0
  381. package/lib/swipl/library/ext/semweb/semweb/rdf_db.pl +3468 -0
  382. package/lib/swipl/library/ext/semweb/semweb/rdf_edit.pl +1145 -0
  383. package/lib/swipl/library/ext/semweb/semweb/rdf_http_plugin.pl +274 -0
  384. package/lib/swipl/library/ext/semweb/semweb/rdf_library.pl +1021 -0
  385. package/lib/swipl/library/ext/semweb/semweb/rdf_litindex.pl +1035 -0
  386. package/lib/swipl/library/ext/semweb/semweb/rdf_ntriples.pl +422 -0
  387. package/lib/swipl/library/ext/semweb/semweb/rdf_persistency.pl +1576 -0
  388. package/lib/swipl/library/ext/semweb/semweb/rdf_portray.pl +182 -0
  389. package/lib/swipl/library/ext/semweb/semweb/rdf_prefixes.pl +681 -0
  390. package/lib/swipl/library/ext/semweb/semweb/rdf_sandbox.pl +153 -0
  391. package/lib/swipl/library/ext/semweb/semweb/rdf_turtle.pl +54 -0
  392. package/lib/swipl/library/ext/semweb/semweb/rdf_turtle_write.pl +1587 -0
  393. package/lib/swipl/library/ext/semweb/semweb/rdf_zlib_plugin.pl +52 -0
  394. package/lib/swipl/library/ext/semweb/semweb/rdfa.pl +1510 -0
  395. package/lib/swipl/library/ext/semweb/semweb/rdfs.pl +475 -0
  396. package/lib/swipl/library/ext/semweb/semweb/sparql_client.pl +643 -0
  397. package/lib/swipl/library/ext/semweb/semweb/turtle.pl +455 -0
  398. package/lib/swipl/library/ext/sgml/DTD/HTML4.dcl +88 -0
  399. package/lib/swipl/library/ext/sgml/DTD/HTML4.dtd +1092 -0
  400. package/lib/swipl/library/ext/sgml/DTD/HTML4.soc +6 -0
  401. package/lib/swipl/library/ext/sgml/DTD/HTML5.dtd +906 -0
  402. package/lib/swipl/library/ext/sgml/DTD/HTMLlat1.ent +195 -0
  403. package/lib/swipl/library/ext/sgml/DTD/HTMLspec.ent +77 -0
  404. package/lib/swipl/library/ext/sgml/DTD/HTMLsym.ent +241 -0
  405. package/lib/swipl/library/ext/sgml/INDEX.pl +64 -0
  406. package/lib/swipl/library/ext/sgml/c14n2.pl +203 -0
  407. package/lib/swipl/library/ext/sgml/iso_639.pl +631 -0
  408. package/lib/swipl/library/ext/sgml/pwp.pl +705 -0
  409. package/lib/swipl/library/ext/sgml/sgml.pl +815 -0
  410. package/lib/swipl/library/ext/sgml/sgml_write.pl +1003 -0
  411. package/lib/swipl/library/ext/sgml/xpath.pl +620 -0
  412. package/lib/swipl/library/ext/sgml/xsdp_types.pl +232 -0
  413. package/lib/swipl/library/ext/ssl/INDEX.pl +60 -0
  414. package/lib/swipl/library/ext/ssl/crypto.pl +1015 -0
  415. package/lib/swipl/library/ext/ssl/http/INDEX.pl +5 -0
  416. package/lib/swipl/library/ext/ssl/http/http_ssl_plugin.pl +296 -0
  417. package/lib/swipl/library/ext/ssl/saml.pl +609 -0
  418. package/lib/swipl/library/ext/ssl/ssl.pl +647 -0
  419. package/lib/swipl/library/ext/ssl/xmldsig.pl +391 -0
  420. package/lib/swipl/library/ext/ssl/xmlenc.pl +354 -0
  421. package/lib/swipl/library/ext/zlib/INDEX.pl +8 -0
  422. package/lib/swipl/library/ext/zlib/zlib.pl +112 -0
  423. package/lib/swipl/library/fastrw.pl +105 -0
  424. package/lib/swipl/library/files.pl +80 -0
  425. package/lib/swipl/library/gensym.pl +111 -0
  426. package/lib/swipl/library/git.pl +1027 -0
  427. package/lib/swipl/library/hashtable.pl +365 -0
  428. package/lib/swipl/library/heaps.pl +252 -0
  429. package/lib/swipl/library/hotfix.pl +239 -0
  430. package/lib/swipl/library/http/jquery.pl +103 -0
  431. package/lib/swipl/library/http/web/js/jquery-3.6.0.min.js +2 -0
  432. package/lib/swipl/library/increval.pl +217 -0
  433. package/lib/swipl/library/intercept.pl +263 -0
  434. package/lib/swipl/library/iostream.pl +237 -0
  435. package/lib/swipl/library/iri_scheme/file.pl +70 -0
  436. package/lib/swipl/library/lazy_lists.pl +481 -0
  437. package/lib/swipl/library/listing.pl +1362 -0
  438. package/lib/swipl/library/lists.pl +860 -0
  439. package/lib/swipl/library/lynx/INDEX.pl +14 -0
  440. package/lib/swipl/library/lynx/format.pl +380 -0
  441. package/lib/swipl/library/lynx/html_style.pl +129 -0
  442. package/lib/swipl/library/lynx/html_text.pl +865 -0
  443. package/lib/swipl/library/lynx/pldoc_style.pl +87 -0
  444. package/lib/swipl/library/macros.pl +451 -0
  445. package/lib/swipl/library/main.pl +1200 -0
  446. package/lib/swipl/library/make.pl +197 -0
  447. package/lib/swipl/library/modules.pl +129 -0
  448. package/lib/swipl/library/nb_rbtrees.pl +267 -0
  449. package/lib/swipl/library/nb_set.pl +203 -0
  450. package/lib/swipl/library/obfuscate.pl +115 -0
  451. package/lib/swipl/library/occurs.pl +241 -0
  452. package/lib/swipl/library/operators.pl +182 -0
  453. package/lib/swipl/library/option.pl +388 -0
  454. package/lib/swipl/library/optparse.pl +966 -0
  455. package/lib/swipl/library/ordsets.pl +518 -0
  456. package/lib/swipl/library/oset.pl +160 -0
  457. package/lib/swipl/library/pairs.pl +187 -0
  458. package/lib/swipl/library/persistency.pl +713 -0
  459. package/lib/swipl/library/pio.pl +47 -0
  460. package/lib/swipl/library/portray_text.pl +277 -0
  461. package/lib/swipl/library/pprint.pl +940 -0
  462. package/lib/swipl/library/predicate_options.pl +975 -0
  463. package/lib/swipl/library/prolog_autoload.pl +233 -0
  464. package/lib/swipl/library/prolog_breakpoints.pl +396 -0
  465. package/lib/swipl/library/prolog_clause.pl +1104 -0
  466. package/lib/swipl/library/prolog_code.pl +416 -0
  467. package/lib/swipl/library/prolog_codewalk.pl +1248 -0
  468. package/lib/swipl/library/prolog_colour.pl +3301 -0
  469. package/lib/swipl/library/prolog_colour.qlf +0 -0
  470. package/lib/swipl/library/prolog_config.pl +154 -0
  471. package/lib/swipl/library/prolog_coverage.pl +1333 -0
  472. package/lib/swipl/library/prolog_debug.pl +404 -0
  473. package/lib/swipl/library/prolog_deps.pl +675 -0
  474. package/lib/swipl/library/prolog_evaluable.pl +234 -0
  475. package/lib/swipl/library/prolog_format.pl +219 -0
  476. package/lib/swipl/library/prolog_history.pl +161 -0
  477. package/lib/swipl/library/prolog_jiti.pl +333 -0
  478. package/lib/swipl/library/prolog_locale.pl +61 -0
  479. package/lib/swipl/library/prolog_metainference.pl +307 -0
  480. package/lib/swipl/library/prolog_pack.pl +3906 -0
  481. package/lib/swipl/library/prolog_profile.pl +346 -0
  482. package/lib/swipl/library/prolog_qlfmake.pl +533 -0
  483. package/lib/swipl/library/prolog_source.pl +1188 -0
  484. package/lib/swipl/library/prolog_stack.pl +794 -0
  485. package/lib/swipl/library/prolog_trace.pl +365 -0
  486. package/lib/swipl/library/prolog_versions.pl +347 -0
  487. package/lib/swipl/library/prolog_wrap.pl +164 -0
  488. package/lib/swipl/library/prolog_xref.pl +3090 -0
  489. package/lib/swipl/library/prolog_xref.qlf +0 -0
  490. package/lib/swipl/library/pure_input.pl +266 -0
  491. package/lib/swipl/library/qpforeign.pl +695 -0
  492. package/lib/swipl/library/qsave.pl +1516 -0
  493. package/lib/swipl/library/quasi_quotations.pl +300 -0
  494. package/lib/swipl/library/quintus.pl +437 -0
  495. package/lib/swipl/library/random.pl +459 -0
  496. package/lib/swipl/library/rbtrees.pl +1153 -0
  497. package/lib/swipl/library/readln.pl +256 -0
  498. package/lib/swipl/library/readutil.pl +304 -0
  499. package/lib/swipl/library/record.pl +544 -0
  500. package/lib/swipl/library/rwlocks.pl +172 -0
  501. package/lib/swipl/library/sandbox.pl +1384 -0
  502. package/lib/swipl/library/semweb/dc.rdfs +198 -0
  503. package/lib/swipl/library/semweb/eor.rdfs +95 -0
  504. package/lib/swipl/library/semweb/owl.owl +287 -0
  505. package/lib/swipl/library/semweb/rdf_library.ttl +90 -0
  506. package/lib/swipl/library/semweb/rdfs.rdfs +251 -0
  507. package/lib/swipl/library/settings.pl +755 -0
  508. package/lib/swipl/library/shell.pl +462 -0
  509. package/lib/swipl/library/shlib.pl +670 -0
  510. package/lib/swipl/library/solution_sequences.pl +439 -0
  511. package/lib/swipl/library/sort.pl +119 -0
  512. package/lib/swipl/library/statistics.pl +541 -0
  513. package/lib/swipl/library/streams.pl +118 -0
  514. package/lib/swipl/library/strings.pl +469 -0
  515. package/lib/swipl/library/system.pl +95 -0
  516. package/lib/swipl/library/tables.pl +381 -0
  517. package/lib/swipl/library/tableutil.pl +771 -0
  518. package/lib/swipl/library/tabling.pl +42 -0
  519. package/lib/swipl/library/terms.pl +457 -0
  520. package/lib/swipl/library/theme/auto.pl +61 -0
  521. package/lib/swipl/library/theme/dark.pl +455 -0
  522. package/lib/swipl/library/theme/light.pl +45 -0
  523. package/lib/swipl/library/thread.pl +810 -0
  524. package/lib/swipl/library/thread_pool.pl +508 -0
  525. package/lib/swipl/library/threadutil.pl +574 -0
  526. package/lib/swipl/library/tty.pl +316 -0
  527. package/lib/swipl/library/ugraphs.pl +644 -0
  528. package/lib/swipl/library/unicode/INDEX.pl +6 -0
  529. package/lib/swipl/library/unicode/blocks.pl +414 -0
  530. package/lib/swipl/library/url.pl +1129 -0
  531. package/lib/swipl/library/utf8.pl +145 -0
  532. package/lib/swipl/library/varnumbers.pl +221 -0
  533. package/lib/swipl/library/vm.pl +251 -0
  534. package/lib/swipl/library/wfs.pl +205 -0
  535. package/lib/swipl/library/when.pl +263 -0
  536. package/lib/swipl/library/writef.pl +278 -0
  537. package/lib/swipl/library/www_browser.pl +251 -0
  538. package/lib/swipl/library/yall.pl +574 -0
  539. package/lib/swipl/library/zip.pl +211 -0
  540. package/lib/swipl/swipl.home +1 -0
  541. package/lib/vendor/libcrypto.so.3 +0 -0
  542. package/lib/vendor/libpcre2-8.so.0 +0 -0
  543. package/lib/vendor/libssl.so.3 +0 -0
  544. package/lib/vendor/libz.so.1 +0 -0
  545. package/licenses/OpenSSL-LICENSE.txt +177 -0
  546. package/licenses/PCRE2-COPYING +5 -0
  547. package/licenses/README.md +12 -0
  548. package/licenses/SWI-Prolog-LICENSE +41 -0
  549. package/licenses/zlib-LICENSE +22 -0
  550. package/package.json +27 -0
@@ -0,0 +1,2820 @@
1
+ /* Part of SWI-Prolog
2
+
3
+ Author: Jan Wielemaker
4
+ E-mail: J.Wielemaker@vu.nl
5
+ WWW: http://www.swi-prolog.org
6
+ Copyright (c) 2002-2025, University of Amsterdam
7
+ VU University Amsterdam
8
+ SWI-Prolog Solutions b.v.
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions
13
+ are met:
14
+
15
+ 1. Redistributions of source code must retain the above copyright
16
+ notice, this list of conditions and the following disclaimer.
17
+
18
+ 2. Redistributions in binary form must reproduce the above copyright
19
+ notice, this list of conditions and the following disclaimer in
20
+ the documentation and/or other materials provided with the
21
+ distribution.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ POSSIBILITY OF SUCH DAMAGE.
35
+ */
36
+
37
+ :- module(http_header,
38
+ [ http_read_request/2, % +Stream, -Request
39
+ http_read_reply_header/2, % +Stream, -Reply
40
+ http_reply/2, % +What, +Stream
41
+ http_reply/3, % +What, +Stream, +HdrExtra
42
+ http_reply/4, % +What, +Stream, +HdrExtra, -Code
43
+ http_reply/5, % +What, +Stream, +HdrExtra, +Context,
44
+ % -Code
45
+ http_reply/6, % +What, +Stream, +HdrExtra, +Context,
46
+ % +Request, -Code
47
+ http_reply_header/3, % +Stream, +What, +HdrExtra
48
+ http_status_reply/4, % +Status, +Out, +HdrExtra, -Code
49
+ http_status_reply/5, % +Status, +Out, +HdrExtra,
50
+ % +Context, -Code
51
+
52
+ http_timestamp/2, % ?Time, ?HTTPstring
53
+
54
+ http_post_data/3, % +Stream, +Data, +HdrExtra
55
+
56
+ http_read_header/2, % +Fd, -Header
57
+ http_parse_header/2, % +Codes, -Header
58
+ http_parse_header_value/3, % +Header, +HeaderValue, -MediaTypes
59
+ http_join_headers/3, % +Default, +InHdr, -OutHdr
60
+ http_update_encoding/3, % +HeaderIn, -Encoding, -HeaderOut
61
+ http_update_connection/4, % +HeaderIn, +Request, -Connection, -HeaderOut
62
+ http_update_transfer/4 % +HeaderIn, +Request, -Transfer, -HeaderOut
63
+ ]).
64
+ :- autoload(html_write,
65
+ [ print_html/2, print_html/1, page/4, html/3,
66
+ html_print_length/2
67
+ ]).
68
+ :- if(exists_source(http_exception)).
69
+ :- autoload(http_exception,[map_exception_to_http_status/4]).
70
+ :- endif.
71
+ :- autoload(mimepack,[mime_pack/3]).
72
+ :- autoload(mimetype,[file_mime_type/2]).
73
+ :- autoload(library(apply),[maplist/2]).
74
+ :- autoload(library(base64),[base64/2]).
75
+ :- use_module(library(debug),[debug/3,debugging/1]).
76
+ :- autoload(library(error),[syntax_error/1,domain_error/2]).
77
+ :- autoload(library(lists),[append/3,member/2,select/3,delete/3]).
78
+ :- autoload(library(memfile),
79
+ [ new_memory_file/1, open_memory_file/3,
80
+ free_memory_file/1, open_memory_file/4,
81
+ size_memory_file/3
82
+ ]).
83
+ :- autoload(library(option),[option/3,option/2]).
84
+ :- autoload(library(pairs),[pairs_values/2]).
85
+ :- autoload(library(readutil),
86
+ [read_line_to_codes/2,read_line_to_codes/3]).
87
+ :- autoload(library(sgml_write),[xml_write/3]).
88
+ :- autoload(library(socket),[gethostname/1]).
89
+ :- autoload(library(uri),
90
+ [ uri_components/2, uri_data/3, uri_encoded/3, uri_query_components/2
91
+ ]).
92
+ :- autoload(library(url),[parse_url_search/2]).
93
+ :- autoload(library(dcg/basics),
94
+ [ integer/3, atom/3, whites/2, blanks_to_nl/2, string/3,
95
+ number/3, blanks/2, float/3, nonblanks/3, eos/2
96
+ ]).
97
+ :- autoload(library(date), [parse_time/3]).
98
+ :- use_module(library(settings),[setting/4,setting/2]).
99
+
100
+ :- multifile
101
+ http:status_page/3, % +Status, +Context, -HTML
102
+ http:status_reply/3, % +Status, -Reply, +Options
103
+ http:serialize_reply/2, % +Reply, -Body
104
+ http:post_data_hook/3, % +Data, +Out, +HdrExtra
105
+ http:mime_type_encoding/2. % +MimeType, -Encoding
106
+
107
+ % see http_update_transfer/4.
108
+
109
+ :- setting(http:chunked_transfer, oneof([never,on_request,if_possible]),
110
+ on_request, 'When to use Transfer-Encoding: Chunked').
111
+
112
+
113
+ /** <module> Handling HTTP headers
114
+
115
+ The library library(http/http_header) provides primitives for parsing
116
+ and composing HTTP headers. Its functionality is normally hidden by the
117
+ other parts of the HTTP server and client libraries.
118
+ */
119
+
120
+ :- discontiguous
121
+ term_expansion/2.
122
+
123
+
124
+ /*******************************
125
+ * READ REQUEST *
126
+ *******************************/
127
+
128
+ %! http_read_request(+FdIn:stream, -Request) is det.
129
+ %
130
+ % Read an HTTP request-header from FdIn and return the broken-down
131
+ % request fields as +Name(+Value) pairs in a list. Request is
132
+ % unified to =end_of_file= if FdIn is at the end of input.
133
+
134
+ http_read_request(In, Request) :-
135
+ catch(read_line_to_codes(In, Codes), E, true),
136
+ ( var(E)
137
+ -> ( Codes == end_of_file
138
+ -> debug(http(header), 'end-of-file', []),
139
+ Request = end_of_file
140
+ ; debug(http(header), 'First line: ~s', [Codes]),
141
+ Request = [input(In)|Request1],
142
+ phrase(request(In, Request1), Codes),
143
+ ( Request1 = [unknown(Text)|_]
144
+ -> string_codes(S, Text),
145
+ syntax_error(http_request(S))
146
+ ; true
147
+ )
148
+ )
149
+ ; ( debugging(http(request))
150
+ -> message_to_string(E, Msg),
151
+ debug(http(request), "Exception reading 1st line: ~s", [Msg])
152
+ ; true
153
+ ),
154
+ Request = end_of_file
155
+ ).
156
+
157
+
158
+ %! http_read_reply_header(+FdIn, -Reply)
159
+ %
160
+ % Read the HTTP reply header. Throws an exception if the current
161
+ % input does not contain a valid reply header.
162
+
163
+ http_read_reply_header(In, [input(In)|Reply]) :-
164
+ read_line_to_codes(In, Codes),
165
+ ( Codes == end_of_file
166
+ -> debug(http(header), 'end-of-file', []),
167
+ throw(error(syntax(http_reply_header, end_of_file), _))
168
+ ; debug(http(header), 'First line: ~s~n', [Codes]),
169
+ ( phrase(reply(In, Reply), Codes)
170
+ -> true
171
+ ; atom_codes(Header, Codes),
172
+ syntax_error(http_reply_header(Header))
173
+ )
174
+ ).
175
+
176
+
177
+ /*******************************
178
+ * FORMULATE REPLY *
179
+ *******************************/
180
+
181
+ %! http_reply(+Data, +Out:stream) is det.
182
+ %! http_reply(+Data, +Out:stream, +HdrExtra) is det.
183
+ %! http_reply(+Data, +Out:stream, +HdrExtra, -Code) is det.
184
+ %! http_reply(+Data, +Out:stream, +HdrExtra, +Context, -Code) is det.
185
+ %! http_reply(+Data, +Out:stream, +HdrExtra, +Context, +Request, -Code) is det.
186
+ %
187
+ % Compose a complete HTTP reply from the term Data using
188
+ % additional headers from HdrExtra to the output stream Out.
189
+ % ExtraHeader is a list of Field(Value). Data is one of:
190
+ %
191
+ % * html(HTML)
192
+ % HTML tokens as produced by html//1 from html_write.pl
193
+ %
194
+ % * file(+MimeType, +FileName)
195
+ % Reply content of FileName using MimeType
196
+ %
197
+ % * file(+MimeType, +FileName, +Range)
198
+ % Reply partial content of FileName with given MimeType
199
+ %
200
+ % * tmp_file(+MimeType, +FileName)
201
+ % Same as =file=, but do not include modification time
202
+ %
203
+ % * bytes(+MimeType, +Bytes)
204
+ % Send a sequence of Bytes with the indicated MimeType.
205
+ % Bytes is either a string of character codes 0..255 or
206
+ % list of integers in the range 0..255. Out-of-bound codes
207
+ % result in a representation error exception.
208
+ %
209
+ % * stream(+In, +Len)
210
+ % Reply content of stream.
211
+ %
212
+ % * cgi_stream(+In, +Len)
213
+ % Reply content of stream, which should start with an
214
+ % HTTP header, followed by a blank line. This is the
215
+ % typical output from a CGI script.
216
+ %
217
+ % * Status
218
+ % HTTP status report as defined by http_status_reply/4.
219
+ %
220
+ % @param HdrExtra provides additional reply-header fields, encoded
221
+ % as Name(Value). It can also contain a field
222
+ % content_length(-Len) to _retrieve_ the
223
+ % value of the Content-length header that is replied.
224
+ % @param Code is the numeric HTTP status code sent
225
+ %
226
+ % @tbd Complete documentation
227
+
228
+ http_reply(What, Out) :-
229
+ http_reply(What, Out, [connection(close)], _).
230
+
231
+ http_reply(Data, Out, HdrExtra) :-
232
+ http_reply(Data, Out, HdrExtra, _Code).
233
+
234
+ http_reply(Data, Out, HdrExtra, Code) :-
235
+ http_reply(Data, Out, HdrExtra, [], Code).
236
+
237
+ http_reply(Data, Out, HdrExtra, Context, Code) :-
238
+ http_reply(Data, Out, HdrExtra, Context, [method(get)], Code).
239
+
240
+ http_reply(Data, Out, HdrExtra, _Context, Request, Code) :-
241
+ byte_count(Out, C0),
242
+ memberchk(method(Method), Request),
243
+ catch(http_reply_data(Data, Out, HdrExtra, Method, Code), E, true),
244
+ !,
245
+ ( var(E)
246
+ -> true
247
+ ; ( E = error(io_error(write,_), _)
248
+ ; E = error(socket_error(_,_), _)
249
+ )
250
+ -> byte_count(Out, C1),
251
+ Sent is C1 - C0,
252
+ throw(error(http_write_short(Data, Sent), _))
253
+ ; E = error(timeout_error(write, _), _)
254
+ -> throw(E)
255
+ ; map_exception_to_http_status(E, Status, NewHdr, NewContext)
256
+ -> http_status_reply(Status, Out, NewHdr, NewContext, Request, Code)
257
+ ; throw(E)
258
+ ).
259
+ http_reply(Status, Out, HdrExtra, Context, Request, Code) :-
260
+ http_status_reply(Status, Out, HdrExtra, Context, Request, Code).
261
+
262
+ :- if(\+current_predicate(map_exception_to_http_status/4)).
263
+ map_exception_to_http_status(_E, _Status, _NewHdr, _NewContext) :-
264
+ fail.
265
+ :- endif.
266
+
267
+ :- meta_predicate
268
+ if_no_head(0, +),
269
+ with_encoding(+, +, 0).
270
+
271
+ %! http_reply_data(+Data, +Out, +HdrExtra, +Method, -Code) is semidet.
272
+ %
273
+ % Fails if Data is not a defined reply-data format, but a status
274
+ % term. See http_reply/3 and http_status_reply/6.
275
+ %
276
+ % @error Various I/O errors.
277
+
278
+ http_reply_data(Data, Out, HdrExtra, Method, Code) :-
279
+ http_reply_data_(Data, Out, HdrExtra, Method, Code),
280
+ flush_output(Out).
281
+
282
+ http_reply_data_(html(HTML), Out, HdrExtra, Method, Code) :-
283
+ !,
284
+ phrase(reply_header(html(HTML), HdrExtra, Code), Header),
285
+ send_reply_header(Out, Header),
286
+ if_no_head(with_encoding(Out, utf8, print_html(Out, HTML)), Method).
287
+ http_reply_data_(file(Type, File), Out, HdrExtra, Method, Code) :-
288
+ !,
289
+ phrase(reply_header(file(Type, File), HdrExtra, Code), Header),
290
+ reply_file(Out, File, Header, Method).
291
+ http_reply_data_(gzip_file(Type, File), Out, HdrExtra, Method, Code) :-
292
+ !,
293
+ phrase(reply_header(gzip_file(Type, File), HdrExtra, Code), Header),
294
+ reply_file(Out, File, Header, Method).
295
+ http_reply_data_(file(Type, File, Range), Out, HdrExtra, Method, Code) :-
296
+ !,
297
+ phrase(reply_header(file(Type, File, Range), HdrExtra, Code), Header),
298
+ reply_file_range(Out, File, Header, Range, Method).
299
+ http_reply_data_(tmp_file(Type, File), Out, HdrExtra, Method, Code) :-
300
+ !,
301
+ phrase(reply_header(tmp_file(Type, File), HdrExtra, Code), Header),
302
+ reply_file(Out, File, Header, Method).
303
+ http_reply_data_(bytes(Type, Bytes), Out, HdrExtra, Method, Code) :-
304
+ !,
305
+ phrase(reply_header(bytes(Type, Bytes), HdrExtra, Code), Header),
306
+ send_reply_header(Out, Header),
307
+ if_no_head(format(Out, '~s', [Bytes]), Method).
308
+ http_reply_data_(stream(In, Len), Out, HdrExtra, Method, Code) :-
309
+ !,
310
+ phrase(reply_header(cgi_data(Len), HdrExtra, Code), Header),
311
+ copy_stream(Out, In, Header, Method, 0, end).
312
+ http_reply_data_(cgi_stream(In, Len), Out, HdrExtra, Method, Code) :-
313
+ !,
314
+ http_read_header(In, CgiHeader),
315
+ seek(In, 0, current, Pos),
316
+ Size is Len - Pos,
317
+ http_join_headers(HdrExtra, CgiHeader, Hdr2),
318
+ phrase(reply_header(cgi_data(Size), Hdr2, Code), Header),
319
+ copy_stream(Out, In, Header, Method, 0, end).
320
+
321
+ if_no_head(_, head) :-
322
+ !.
323
+ if_no_head(Goal, _) :-
324
+ call(Goal).
325
+
326
+ with_encoding(Out, Encoding, Goal) :-
327
+ stream_property(Out, encoding(Old)),
328
+ ( Old == Encoding
329
+ -> call(Goal)
330
+ ; setup_call_cleanup(
331
+ set_stream(Out, encoding(Encoding)),
332
+ call(Goal),
333
+ set_stream(Out, encoding(Old)))
334
+ ).
335
+
336
+ reply_file(Out, _File, Header, head) :-
337
+ !,
338
+ send_reply_header(Out, Header).
339
+ reply_file(Out, File, Header, _) :-
340
+ setup_call_cleanup(
341
+ open(File, read, In, [type(binary)]),
342
+ copy_stream(Out, In, Header, 0, end),
343
+ close(In)).
344
+
345
+ reply_file_range(Out, _File, Header, _Range, head) :-
346
+ !,
347
+ send_reply_header(Out, Header).
348
+ reply_file_range(Out, File, Header, bytes(From, To), _) :-
349
+ setup_call_cleanup(
350
+ open(File, read, In, [type(binary)]),
351
+ copy_stream(Out, In, Header, From, To),
352
+ close(In)).
353
+
354
+ copy_stream(Out, _, Header, head, _, _) :-
355
+ !,
356
+ send_reply_header(Out, Header).
357
+ copy_stream(Out, In, Header, _, From, To) :-
358
+ copy_stream(Out, In, Header, From, To).
359
+
360
+ copy_stream(Out, In, Header, From, To) :-
361
+ ( From == 0
362
+ -> true
363
+ ; seek(In, From, bof, _)
364
+ ),
365
+ peek_byte(In, _),
366
+ send_reply_header(Out, Header),
367
+ ( To == end
368
+ -> copy_stream_data(In, Out)
369
+ ; Len is To - From,
370
+ copy_stream_data(In, Out, Len)
371
+ ).
372
+
373
+
374
+ %! http_status_reply(+Status, +Out, +HdrExtra, -Code) is det.
375
+ %! http_status_reply(+Status, +Out, +HdrExtra, +Context, -Code) is det.
376
+ %! http_status_reply(+Status, +Out, +HdrExtra, +Context, +Request, -Code) is det.
377
+ %
378
+ % Emit HTML non-200 status reports. Such requests are always sent
379
+ % as UTF-8 documents.
380
+ %
381
+ % Status can be one of the following:
382
+ % - authorise(Method)
383
+ % Challenge authorization. Method is one of
384
+ % - basic(Realm)
385
+ % - digest(Digest)
386
+ % - authorise(basic,Realm)
387
+ % Same as authorise(basic(Realm)). Deprecated.
388
+ % - bad_request(ErrorTerm)
389
+ % - busy
390
+ % - created(Location)
391
+ % - forbidden(Url)
392
+ % - moved(To)
393
+ % - moved_temporary(To)
394
+ % - no_content
395
+ % - not_acceptable(WhyHtml)
396
+ % - not_found(Path)
397
+ % - method_not_allowed(Method, Path)
398
+ % - not_modified
399
+ % - resource_error(ErrorTerm)
400
+ % - see_other(To)
401
+ % - switching_protocols(Goal,Options)
402
+ % - server_error(ErrorTerm)
403
+ % - unavailable(WhyHtml)
404
+
405
+ http_status_reply(Status, Out, Options) :-
406
+ _{header:HdrExtra, context:Context, code:Code, method:Method} :< Options,
407
+ http_status_reply(Status, Out, HdrExtra, Context, [method(Method)], Code).
408
+
409
+ http_status_reply(Status, Out, HdrExtra, Code) :-
410
+ http_status_reply(Status, Out, HdrExtra, [], Code).
411
+
412
+ http_status_reply(Status, Out, HdrExtra, Context, Code) :-
413
+ http_status_reply(Status, Out, HdrExtra, Context, [method(get)], Code).
414
+
415
+ http_status_reply(Status, Out, HdrExtra, Context, Request, Code) :-
416
+ option(method(Method), Request, get),
417
+ parsed_accept(Request, Accept),
418
+ status_reply_flush(Status, Out,
419
+ _{ context: Context,
420
+ method: Method,
421
+ code: Code,
422
+ accept: Accept,
423
+ header: HdrExtra
424
+ }).
425
+
426
+ parsed_accept(Request, Accept) :-
427
+ memberchk(accept(Accept0), Request),
428
+ http_parse_header_value(accept, Accept0, Accept1),
429
+ !,
430
+ Accept = Accept1.
431
+ parsed_accept(_, [ media(text/html, [], 0.1, []),
432
+ media(_, [], 0.01, [])
433
+ ]).
434
+
435
+ status_reply_flush(Status, Out, Options) :-
436
+ status_reply(Status, Out, Options),
437
+ !,
438
+ flush_output(Out).
439
+
440
+ %! status_reply(+Status, +Out, +Options:dict)
441
+ %
442
+ % Formulate a non-200 reply and send it to the stream Out. Options
443
+ % is a dict containing:
444
+ %
445
+ % - header
446
+ % - context
447
+ % - method
448
+ % - code
449
+ % - accept
450
+
451
+ % Replies without content
452
+ status_reply(no_content, Out, Options) :-
453
+ !,
454
+ phrase(reply_header(status(no_content), Options), Header),
455
+ send_reply_header(Out, Header).
456
+ status_reply(switching_protocols(_Goal,SwitchOptions), Out, Options) :-
457
+ !,
458
+ ( option(headers(Extra1), SwitchOptions)
459
+ -> true
460
+ ; option(header(Extra1), SwitchOptions, [])
461
+ ),
462
+ http_join_headers(Options.header, Extra1, HdrExtra),
463
+ phrase(reply_header(status(switching_protocols),
464
+ Options.put(header,HdrExtra)), Header),
465
+ send_reply_header(Out, Header).
466
+ status_reply(authorise(basic, ''), Out, Options) :-
467
+ !,
468
+ status_reply(authorise(basic), Out, Options).
469
+ status_reply(authorise(basic, Realm), Out, Options) :-
470
+ !,
471
+ status_reply(authorise(basic(Realm)), Out, Options).
472
+ status_reply(not_modified, Out, Options) :-
473
+ !,
474
+ phrase(reply_header(status(not_modified), Options), Header),
475
+ send_reply_header(Out, Header).
476
+ % aliases (compatibility)
477
+ status_reply(busy, Out, Options) :-
478
+ status_reply(service_unavailable(busy), Out, Options).
479
+ status_reply(unavailable(Why), Out, Options) :-
480
+ status_reply(service_unavailable(Why), Out, Options).
481
+ status_reply(resource_error(Why), Out, Options) :-
482
+ status_reply(service_unavailable(Why), Out, Options).
483
+ % replies with content
484
+ status_reply(Status, Out, Options) :-
485
+ status_has_content(Status),
486
+ status_page_hook(Status, Reply, Options),
487
+ serialize_body(Reply, Body),
488
+ Status =.. List,
489
+ append(List, [Body], ExList),
490
+ ExStatus =.. ExList,
491
+ phrase(reply_header(ExStatus, Options), Header),
492
+ send_reply_header(Out, Header),
493
+ reply_status_body(Out, Body, Options).
494
+
495
+ %! status_has_content(+StatusTerm, -HTTPCode)
496
+ %
497
+ % True when StatusTerm is a status that usually comes with an
498
+ % explanatory content message.
499
+
500
+ status_has_content(created(_Location)).
501
+ status_has_content(moved(_To)).
502
+ status_has_content(moved_temporary(_To)).
503
+ status_has_content(gone(_URL)).
504
+ status_has_content(see_other(_To)).
505
+ status_has_content(bad_request(_ErrorTerm)).
506
+ status_has_content(authorise(_Method)).
507
+ status_has_content(forbidden(_URL)).
508
+ status_has_content(not_found(_URL)).
509
+ status_has_content(method_not_allowed(_Method, _URL)).
510
+ status_has_content(not_acceptable(_Why)).
511
+ status_has_content(server_error(_ErrorTerm)).
512
+ status_has_content(service_unavailable(_Why)).
513
+
514
+ %! serialize_body(+Reply, -Body) is det.
515
+ %
516
+ % Serialize the reply as returned by status_page_hook/3 into a term:
517
+ %
518
+ % - body(Type, Encoding, Content)
519
+ % In this term, Type is the media type, Encoding is the
520
+ % required wire encoding and Content a string representing the
521
+ % content.
522
+
523
+ serialize_body(Reply, Body) :-
524
+ http:serialize_reply(Reply, Body),
525
+ !.
526
+ serialize_body(html_tokens(Tokens), body(text/html, utf8, Content)) :-
527
+ !,
528
+ with_output_to(string(Content), print_html(Tokens)).
529
+ serialize_body(Reply, Reply) :-
530
+ Reply = body(_,_,_),
531
+ !.
532
+ serialize_body(Reply, _) :-
533
+ domain_error(http_reply_body, Reply).
534
+
535
+ reply_status_body(_, _, Options) :-
536
+ Options.method == head,
537
+ !.
538
+ reply_status_body(Out, body(_Type, Encoding, Content), _Options) :-
539
+ ( Encoding == octet
540
+ -> format(Out, '~s', [Content])
541
+ ; setup_call_cleanup(
542
+ set_stream(Out, encoding(Encoding)),
543
+ format(Out, '~s', [Content]),
544
+ set_stream(Out, encoding(octet)))
545
+ ).
546
+
547
+ %! http:serialize_reply(+Reply, -Body) is semidet.
548
+ %
549
+ % Multifile hook to serialize the result of http:status_reply/3
550
+ % into a term
551
+ %
552
+ % - body(Type, Encoding, Content)
553
+ % In this term, Type is the media type, Encoding is the
554
+ % required wire encoding and Content a string representing the
555
+ % content.
556
+
557
+ %! status_page_hook(+Term, -Reply, +Options) is det.
558
+ %
559
+ % Calls the following two hooks to generate an HTML page from a
560
+ % status reply.
561
+ %
562
+ % - http:status_reply(+Term, -Reply, +Options)
563
+ % Provide non-HTML description of the (non-200) reply.
564
+ % The term Reply is handed to serialize_body/2, calling
565
+ % the hook http:serialize_reply/2.
566
+ % - http:status_page(+Term, +Context, -HTML)
567
+ % - http:status_page(+Code, +Context, -HTML)
568
+ %
569
+ % @arg Term is the status term, e.g., not_found(URL)
570
+ % @see http:status_page/3
571
+
572
+ status_page_hook(Term, Reply, Options) :-
573
+ Context = Options.context,
574
+ functor(Term, Name, _),
575
+ status_number_fact(Name, Code),
576
+ ( Options.code = Code,
577
+ http:status_reply(Term, Reply, Options)
578
+ ; http:status_page(Term, Context, HTML),
579
+ Reply = html_tokens(HTML)
580
+ ; http:status_page(Code, Context, HTML), % deprecated
581
+ Reply = html_tokens(HTML)
582
+ ),
583
+ !.
584
+ status_page_hook(created(Location), html_tokens(HTML), _Options) :-
585
+ phrase(page([ title('201 Created')
586
+ ],
587
+ [ h1('Created'),
588
+ p(['The document was created ',
589
+ a(href(Location), ' Here')
590
+ ]),
591
+ \address
592
+ ]),
593
+ HTML).
594
+ status_page_hook(moved(To), html_tokens(HTML), _Options) :-
595
+ phrase(page([ title('301 Moved Permanently')
596
+ ],
597
+ [ h1('Moved Permanently'),
598
+ p(['The document has moved ',
599
+ a(href(To), ' Here')
600
+ ]),
601
+ \address
602
+ ]),
603
+ HTML).
604
+ status_page_hook(moved_temporary(To), html_tokens(HTML), _Options) :-
605
+ phrase(page([ title('302 Moved Temporary')
606
+ ],
607
+ [ h1('Moved Temporary'),
608
+ p(['The document is currently ',
609
+ a(href(To), ' Here')
610
+ ]),
611
+ \address
612
+ ]),
613
+ HTML).
614
+ status_page_hook(gone(URL), html_tokens(HTML), _Options) :-
615
+ phrase(page([ title('410 Resource Gone')
616
+ ],
617
+ [ h1('Resource Gone'),
618
+ p(['The document has been removed ',
619
+ a(href(URL), ' from here')
620
+ ]),
621
+ \address
622
+ ]),
623
+ HTML).
624
+ status_page_hook(see_other(To), html_tokens(HTML), _Options) :-
625
+ phrase(page([ title('303 See Other')
626
+ ],
627
+ [ h1('See Other'),
628
+ p(['See other document ',
629
+ a(href(To), ' Here')
630
+ ]),
631
+ \address
632
+ ]),
633
+ HTML).
634
+ status_page_hook(bad_request(ErrorTerm), html_tokens(HTML), _Options) :-
635
+ '$messages':translate_message(ErrorTerm, Lines, []),
636
+ phrase(page([ title('400 Bad Request')
637
+ ],
638
+ [ h1('Bad Request'),
639
+ p(\html_message_lines(Lines)),
640
+ \address
641
+ ]),
642
+ HTML).
643
+ status_page_hook(authorise(_Method), html_tokens(HTML), _Options):-
644
+ phrase(page([ title('401 Authorization Required')
645
+ ],
646
+ [ h1('Authorization Required'),
647
+ p(['This server could not verify that you ',
648
+ 'are authorized to access the document ',
649
+ 'requested. Either you supplied the wrong ',
650
+ 'credentials (e.g., bad password), or your ',
651
+ 'browser doesn\'t understand how to supply ',
652
+ 'the credentials required.'
653
+ ]),
654
+ \address
655
+ ]),
656
+ HTML).
657
+ status_page_hook(forbidden(URL), html_tokens(HTML), _Options) :-
658
+ phrase(page([ title('403 Forbidden')
659
+ ],
660
+ [ h1('Forbidden'),
661
+ p(['You don\'t have permission to access ', URL,
662
+ ' on this server'
663
+ ]),
664
+ \address
665
+ ]),
666
+ HTML).
667
+ status_page_hook(not_found(URL), html_tokens(HTML), _Options) :-
668
+ phrase(page([ title('404 Not Found')
669
+ ],
670
+ [ h1('Not Found'),
671
+ p(['The requested URL ', tt(URL),
672
+ ' was not found on this server'
673
+ ]),
674
+ \address
675
+ ]),
676
+ HTML).
677
+ status_page_hook(method_not_allowed(Method,URL), html_tokens(HTML), _Options) :-
678
+ upcase_atom(Method, UMethod),
679
+ phrase(page([ title('405 Method not allowed')
680
+ ],
681
+ [ h1('Method not allowed'),
682
+ p(['The requested URL ', tt(URL),
683
+ ' does not support method ', tt(UMethod), '.'
684
+ ]),
685
+ \address
686
+ ]),
687
+ HTML).
688
+ status_page_hook(not_acceptable(WhyHTML), html_tokens(HTML), _Options) :-
689
+ phrase(page([ title('406 Not Acceptable')
690
+ ],
691
+ [ h1('Not Acceptable'),
692
+ WhyHTML,
693
+ \address
694
+ ]),
695
+ HTML).
696
+ status_page_hook(server_error(ErrorTerm), html_tokens(HTML), _Options) :-
697
+ '$messages':translate_message(ErrorTerm, Lines, []),
698
+ phrase(page([ title('500 Internal server error')
699
+ ],
700
+ [ h1('Internal server error'),
701
+ p(\html_message_lines(Lines)),
702
+ \address
703
+ ]),
704
+ HTML).
705
+ status_page_hook(service_unavailable(Why), html_tokens(HTML), _Options) :-
706
+ phrase(page([ title('503 Service Unavailable')
707
+ ],
708
+ [ h1('Service Unavailable'),
709
+ \unavailable(Why),
710
+ \address
711
+ ]),
712
+ HTML).
713
+
714
+ unavailable(busy) -->
715
+ html(p(['The server is temporarily out of resources, ',
716
+ 'please try again later'])).
717
+ unavailable(error(Formal,Context)) -->
718
+ { '$messages':translate_message(error(Formal,Context), Lines, []) },
719
+ html_message_lines(Lines).
720
+ unavailable(HTML) -->
721
+ html(HTML).
722
+
723
+ html_message_lines([]) -->
724
+ [].
725
+ html_message_lines([nl|T]) -->
726
+ !,
727
+ html([br([])]),
728
+ html_message_lines(T).
729
+ html_message_lines([flush]) -->
730
+ [].
731
+ html_message_lines([ansi(_Style,Fmt,Args)|T]) -->
732
+ !,
733
+ { format(string(S), Fmt, Args)
734
+ },
735
+ html([S]),
736
+ html_message_lines(T).
737
+ html_message_lines([url(Pos)|T]) -->
738
+ !,
739
+ msg_url(Pos),
740
+ html_message_lines(T).
741
+ html_message_lines([url(URL, Label)|T]) -->
742
+ !,
743
+ html(a(href(URL), Label)),
744
+ html_message_lines(T).
745
+ html_message_lines([Fmt-Args|T]) -->
746
+ !,
747
+ { format(string(S), Fmt, Args)
748
+ },
749
+ html([S]),
750
+ html_message_lines(T).
751
+ html_message_lines([Fmt|T]) -->
752
+ !,
753
+ { format(string(S), Fmt, [])
754
+ },
755
+ html([S]),
756
+ html_message_lines(T).
757
+
758
+ msg_url(File:Line:Pos) -->
759
+ !,
760
+ html([File, :, Line, :, Pos]).
761
+ msg_url(File:Line) -->
762
+ !,
763
+ html([File, :, Line]).
764
+ msg_url(File) -->
765
+ html([File]).
766
+
767
+ %! http_join_headers(+Default, +Header, -Out)
768
+ %
769
+ % Append headers from Default to Header if they are not
770
+ % already part of it.
771
+
772
+ http_join_headers([], H, H).
773
+ http_join_headers([H|T], Hdr0, Hdr) :-
774
+ functor(H, N, A),
775
+ functor(H2, N, A),
776
+ member(H2, Hdr0),
777
+ !,
778
+ http_join_headers(T, Hdr0, Hdr).
779
+ http_join_headers([H|T], Hdr0, [H|Hdr]) :-
780
+ http_join_headers(T, Hdr0, Hdr).
781
+
782
+
783
+ %! http_update_encoding(+HeaderIn, -Encoding, -HeaderOut)
784
+ %
785
+ % Allow for rewrite of the header, adjusting the encoding. We
786
+ % distinguish three options. If the user announces `text', we
787
+ % always use UTF-8 encoding. If the user announces charset=utf-8
788
+ % we use UTF-8 and otherwise we use octet (raw) encoding.
789
+ % Alternatively we could dynamically choose for ASCII, ISO-Latin-1
790
+ % or UTF-8.
791
+
792
+ http_update_encoding(Header0, Encoding, Header) :-
793
+ memberchk(content_type(Type), Header0),
794
+ !,
795
+ http_update_encoding(Type, Header0, Encoding, Header).
796
+ http_update_encoding(Header, octet, Header).
797
+
798
+ http_update_encoding('text/event-stream', Header, utf8, Header) :-
799
+ !.
800
+ http_update_encoding(Type0, Header0, utf8, [content_type(Type)|Header]) :-
801
+ sub_atom(Type0, 0, _, _, 'text/'),
802
+ !,
803
+ select(content_type(_), Header0, Header),
804
+ !,
805
+ ( sub_atom(Type0, S, _, _, ';')
806
+ -> sub_atom(Type0, 0, S, _, B)
807
+ ; B = Type0
808
+ ),
809
+ atom_concat(B, '; charset=UTF-8', Type).
810
+ http_update_encoding(Type, Header, Encoding, Header) :-
811
+ ( sub_atom_icasechk(Type, _, 'utf-8')
812
+ -> Encoding = utf8
813
+ ; http:mime_type_encoding(Type, Encoding)
814
+ -> true
815
+ ; mime_type_encoding(Type, Encoding)
816
+ -> true
817
+ ; Encoding = octet
818
+ ).
819
+
820
+ %! mime_type_encoding(+MimeType, -Encoding) is semidet.
821
+ %
822
+ % Encoding is the (default) character encoding for MimeType. Hooked by
823
+ % http:mime_type_encoding/2.
824
+
825
+ mime_type_encoding('application/json', utf8).
826
+ mime_type_encoding('application/jsonrequest', utf8).
827
+ mime_type_encoding('application/x-prolog', utf8).
828
+ mime_type_encoding('application/n-quads', utf8).
829
+ mime_type_encoding('application/n-triples', utf8).
830
+ mime_type_encoding('application/sparql-query', utf8).
831
+ mime_type_encoding('application/trig', utf8).
832
+ mime_type_encoding('application/sparql-results+json', utf8).
833
+ mime_type_encoding('application/sparql-results+xml', utf8).
834
+
835
+ %! http:mime_type_encoding(+MimeType, -Encoding) is semidet.
836
+ %
837
+ % Encoding is the (default) character encoding for MimeType. This is
838
+ % used for setting the encoding for HTTP replies after the user calls
839
+ % format('Content-type: <MIME type>~n'). This hook is called before
840
+ % mime_type_encoding/2. This default defines `utf8` for JSON and
841
+ % Turtle derived =|application/|= MIME types.
842
+
843
+
844
+ %! http_update_connection(+CGIHeader, +Request, -Connection, -Header)
845
+ %
846
+ % Merge keep-alive information from Request and CGIHeader into
847
+ % Header.
848
+
849
+ http_update_connection(CgiHeader, Request, Connect,
850
+ [connection(Connect)|Rest]) :-
851
+ select(connection(CgiConn), CgiHeader, Rest),
852
+ !,
853
+ connection(Request, ReqConnection),
854
+ join_connection(ReqConnection, CgiConn, Connect).
855
+ http_update_connection(CgiHeader, Request, Connect,
856
+ [connection(Connect)|CgiHeader]) :-
857
+ connection(Request, Connect).
858
+
859
+ join_connection(Keep1, Keep2, Connection) :-
860
+ ( downcase_atom(Keep1, 'keep-alive'),
861
+ downcase_atom(Keep2, 'keep-alive')
862
+ -> Connection = 'Keep-Alive'
863
+ ; Connection = close
864
+ ).
865
+
866
+
867
+ %! connection(+Header, -Connection)
868
+ %
869
+ % Extract the desired connection from a header.
870
+
871
+ connection(Header, Close) :-
872
+ ( memberchk(connection(Connection), Header)
873
+ -> Close = Connection
874
+ ; memberchk(http_version(1-X), Header),
875
+ X >= 1
876
+ -> Close = 'Keep-Alive'
877
+ ; Close = close
878
+ ).
879
+
880
+
881
+ %! http_update_transfer(+Request, +CGIHeader, -Transfer, -Header)
882
+ %
883
+ % Decide on the transfer encoding from the Request and the CGI
884
+ % header. The behaviour depends on the setting
885
+ % http:chunked_transfer. If =never=, even explicit requests are
886
+ % ignored. If =on_request=, chunked encoding is used if requested
887
+ % through the CGI header and allowed by the client. If
888
+ % =if_possible=, chunked encoding is used whenever the client
889
+ % allows for it, which is interpreted as the client supporting
890
+ % HTTP 1.1 or higher.
891
+ %
892
+ % Chunked encoding is more space efficient and allows the client
893
+ % to start processing partial results. The drawback is that errors
894
+ % lead to incomplete pages instead of a nicely formatted complete
895
+ % page.
896
+
897
+ http_update_transfer(Request, CgiHeader, Transfer, Header) :-
898
+ setting(http:chunked_transfer, When),
899
+ http_update_transfer(When, Request, CgiHeader, Transfer, Header).
900
+
901
+ http_update_transfer(never, _, CgiHeader, none, Header) :-
902
+ !,
903
+ delete(CgiHeader, transfer_encoding(_), Header).
904
+ http_update_transfer(_, _, CgiHeader, none, Header) :-
905
+ memberchk(location(_), CgiHeader),
906
+ !,
907
+ delete(CgiHeader, transfer_encoding(_), Header).
908
+ http_update_transfer(_, Request, CgiHeader, Transfer, Header) :-
909
+ select(transfer_encoding(CgiTransfer), CgiHeader, Rest),
910
+ !,
911
+ transfer(Request, ReqConnection),
912
+ join_transfer(ReqConnection, CgiTransfer, Transfer),
913
+ ( Transfer == none
914
+ -> Header = Rest
915
+ ; Header = [transfer_encoding(Transfer)|Rest]
916
+ ).
917
+ http_update_transfer(if_possible, Request, CgiHeader, Transfer, Header) :-
918
+ transfer(Request, Transfer),
919
+ Transfer \== none,
920
+ !,
921
+ Header = [transfer_encoding(Transfer)|CgiHeader].
922
+ http_update_transfer(_, _, CgiHeader, event_stream, CgiHeader) :-
923
+ memberchk(content_type('text/event-stream'), CgiHeader),
924
+ !.
925
+ http_update_transfer(_, _, CgiHeader, none, CgiHeader).
926
+
927
+ join_transfer(chunked, chunked, chunked) :- !.
928
+ join_transfer(_, _, none).
929
+
930
+
931
+ %! transfer(+Header, -Connection)
932
+ %
933
+ % Extract the desired connection from a header.
934
+
935
+ transfer(Header, Transfer) :-
936
+ ( memberchk(transfer_encoding(Transfer0), Header)
937
+ -> Transfer = Transfer0
938
+ ; memberchk(http_version(1-X), Header),
939
+ X >= 1
940
+ -> Transfer = chunked
941
+ ; Transfer = none
942
+ ).
943
+
944
+
945
+ %! content_length_in_encoding(+Encoding, +In, -Bytes)
946
+ %
947
+ % Determine how many bytes are required to represent the data from
948
+ % stream In using the given encoding. Fails if the data cannot be
949
+ % represented with the given encoding.
950
+
951
+ content_length_in_encoding(Enc, Stream, Bytes) :-
952
+ stream_property(Stream, position(Here)),
953
+ setup_call_cleanup(
954
+ open_null_stream(Out),
955
+ ( set_stream(Out, encoding(Enc)),
956
+ catch(copy_stream_data(Stream, Out), _, fail),
957
+ flush_output(Out),
958
+ byte_count(Out, Bytes)
959
+ ),
960
+ ( close(Out, [force(true)]),
961
+ set_stream_position(Stream, Here)
962
+ )).
963
+
964
+
965
+ /*******************************
966
+ * POST SUPPORT *
967
+ *******************************/
968
+
969
+ %! http_post_data(+Data, +Out:stream, +HdrExtra) is det.
970
+ %
971
+ % Send data on behalf on an HTTP POST request. This predicate is
972
+ % normally called by http_post/4 from http_client.pl to send the
973
+ % POST data to the server. Data is one of:
974
+ %
975
+ % * html(+Tokens)
976
+ % Result of html//1 from html_write.pl
977
+ %
978
+ % * json(+Term)
979
+ % Posting a JSON query and processing the JSON reply (or any other
980
+ % reply understood by http_read_data/3) is simple as
981
+ % =|http_post(URL, json(Term), Reply, [])|=, where Term is a JSON
982
+ % term as described in json.pl and reply is of the same format if
983
+ % the server replies with JSON, when using module =|:-
984
+ % use_module(library(http/http_json))|=. Note that the module is
985
+ % used in both http server and http client, see
986
+ % library(http/http_json).
987
+ %
988
+ % * xml(+Term)
989
+ % Post the result of xml_write/3 using the Mime-type
990
+ % =|text/xml|=
991
+ %
992
+ % * xml(+Type, +Term)
993
+ % Post the result of xml_write/3 using the given Mime-type
994
+ % and an empty option list to xml_write/3.
995
+ %
996
+ % * xml(+Type, +Term, +Options)
997
+ % Post the result of xml_write/3 using the given Mime-type
998
+ % and option list for xml_write/3.
999
+ %
1000
+ % * file(+File)
1001
+ % Send contents of a file. Mime-type is determined by
1002
+ % file_mime_type/2.
1003
+ %
1004
+ % * file(+Type, +File)
1005
+ % Send file with content of indicated mime-type.
1006
+ %
1007
+ % * memory_file(+Type, +Handle)
1008
+ % Similar to file(+Type, +File), but using a memory file
1009
+ % instead of a real file. See new_memory_file/1.
1010
+ %
1011
+ % * codes(+Codes)
1012
+ % As codes(text/plain, Codes).
1013
+ %
1014
+ % * codes(+Type, +Codes)
1015
+ % Send Codes using the indicated MIME-type.
1016
+ %
1017
+ % * bytes(+Type, +Bytes)
1018
+ % Send Bytes using the indicated MIME-type. Bytes is either a
1019
+ % string of character codes 0..255 or list of integers in the
1020
+ % range 0..255. Out-of-bound codes result in a representation
1021
+ % error exception.
1022
+ %
1023
+ % * atom(+Atom)
1024
+ % As atom(text/plain, Atom).
1025
+ %
1026
+ % * atom(+Type, +Atom)
1027
+ % Send Atom using the indicated MIME-type.
1028
+ %
1029
+ % * string(+String)
1030
+ % * string(+Type, +String)
1031
+ % Similar to atom(Atom) and atom(Type,Atom), accepting a SWI-Prolog
1032
+ % string.
1033
+ %
1034
+ % * cgi_stream(+Stream, +Len) Read the input from Stream which,
1035
+ % like CGI data starts with a partial HTTP header. The fields of
1036
+ % this header are merged with the provided HdrExtra fields. The
1037
+ % first Len characters of Stream are used.
1038
+ %
1039
+ % * form(+ListOfParameter)
1040
+ % Send data of the MIME type application/x-www-form-urlencoded as
1041
+ % produced by browsers issuing a POST request from an HTML form.
1042
+ % ListOfParameter is a list of Name=Value or Name(Value).
1043
+ %
1044
+ % * form_data(+ListOfData)
1045
+ % Send data of the MIME type =|multipart/form-data|= as produced
1046
+ % by browsers issuing a POST request from an HTML form using
1047
+ % enctype =|multipart/form-data|=. ListOfData is the same as for
1048
+ % the List alternative described below. Below is an example.
1049
+ % Repository, etc. are atoms providing the value, while the last
1050
+ % argument provides a value from a file.
1051
+ %
1052
+ % ==
1053
+ % ...,
1054
+ % http_post([ protocol(http),
1055
+ % host(Host),
1056
+ % port(Port),
1057
+ % path(ActionPath)
1058
+ % ],
1059
+ % form_data([ repository = Repository,
1060
+ % dataFormat = DataFormat,
1061
+ % baseURI = BaseURI,
1062
+ % verifyData = Verify,
1063
+ % data = file(File)
1064
+ % ]),
1065
+ % _Reply,
1066
+ % []),
1067
+ % ...,
1068
+ % ==
1069
+ %
1070
+ % * List
1071
+ % If the argument is a plain list, it is sent using the MIME type
1072
+ % multipart/mixed and packed using mime_pack/3. See mime_pack/3
1073
+ % for details on the argument format.
1074
+
1075
+ http_post_data(Data, Out, HdrExtra),
1076
+ http:post_data_hook(Data, Out, HdrExtra) =>
1077
+ true.
1078
+ http_post_data(html(HTML), Out, HdrExtra) =>
1079
+ phrase(post_header(html(HTML), HdrExtra), Header),
1080
+ send_request_header(Out, Header),
1081
+ print_html(Out, HTML).
1082
+ http_post_data(xml(XML), Out, HdrExtra) =>
1083
+ http_post_data(xml(text/xml, XML, []), Out, HdrExtra).
1084
+ http_post_data(xml(Type, XML), Out, HdrExtra) =>
1085
+ http_post_data(xml(Type, XML, []), Out, HdrExtra).
1086
+ http_post_data(xml(Type, XML, Options), Out, HdrExtra) =>
1087
+ setup_call_cleanup(
1088
+ new_memory_file(MemFile),
1089
+ ( setup_call_cleanup(
1090
+ open_memory_file(MemFile, write, MemOut),
1091
+ xml_write(MemOut, XML, Options),
1092
+ close(MemOut)),
1093
+ http_post_data(memory_file(Type, MemFile), Out, HdrExtra)
1094
+ ),
1095
+ free_memory_file(MemFile)).
1096
+ http_post_data(file(File), Out, HdrExtra) =>
1097
+ ( file_mime_type(File, Type)
1098
+ -> true
1099
+ ; Type = text/plain
1100
+ ),
1101
+ http_post_data(file(Type, File), Out, HdrExtra).
1102
+ http_post_data(file(Type, File), Out, HdrExtra) =>
1103
+ phrase(post_header(file(Type, File), HdrExtra), Header),
1104
+ send_request_header(Out, Header),
1105
+ setup_call_cleanup(
1106
+ open(File, read, In, [type(binary)]),
1107
+ copy_stream_data(In, Out),
1108
+ close(In)).
1109
+ http_post_data(memory_file(Type, Handle), Out, HdrExtra) =>
1110
+ phrase(post_header(memory_file(Type, Handle), HdrExtra), Header),
1111
+ send_request_header(Out, Header),
1112
+ setup_call_cleanup(
1113
+ open_memory_file(Handle, read, In, [encoding(octet)]),
1114
+ copy_stream_data(In, Out),
1115
+ close(In)).
1116
+ http_post_data(codes(Codes), Out, HdrExtra) =>
1117
+ http_post_data(codes(text/plain, Codes), Out, HdrExtra).
1118
+ http_post_data(codes(Type, Codes), Out, HdrExtra) =>
1119
+ phrase(post_header(codes(Type, Codes), HdrExtra), Header),
1120
+ send_request_header(Out, Header),
1121
+ setup_call_cleanup(
1122
+ set_stream(Out, encoding(utf8)),
1123
+ format(Out, '~s', [Codes]),
1124
+ set_stream(Out, encoding(octet))).
1125
+ http_post_data(bytes(Type, Bytes), Out, HdrExtra) =>
1126
+ phrase(post_header(bytes(Type, Bytes), HdrExtra), Header),
1127
+ send_request_header(Out, Header),
1128
+ format(Out, '~s', [Bytes]).
1129
+ http_post_data(atom(Atom), Out, HdrExtra) =>
1130
+ http_post_data(atom(text/plain, Atom), Out, HdrExtra).
1131
+ http_post_data(atom(Type, Atom), Out, HdrExtra) =>
1132
+ phrase(post_header(atom(Type, Atom), HdrExtra), Header),
1133
+ send_request_header(Out, Header),
1134
+ setup_call_cleanup(
1135
+ set_stream(Out, encoding(utf8)),
1136
+ write(Out, Atom),
1137
+ set_stream(Out, encoding(octet))).
1138
+ http_post_data(string(String), Out, HdrExtra) =>
1139
+ http_post_data(atom(text/plain, String), Out, HdrExtra).
1140
+ http_post_data(string(Type, String), Out, HdrExtra) =>
1141
+ phrase(post_header(string(Type, String), HdrExtra), Header),
1142
+ send_request_header(Out, Header),
1143
+ setup_call_cleanup(
1144
+ set_stream(Out, encoding(utf8)),
1145
+ write(Out, String),
1146
+ set_stream(Out, encoding(octet))).
1147
+ http_post_data(cgi_stream(In, _Len), Out, HdrExtra) =>
1148
+ debug(obsolete, 'Obsolete 2nd argument in cgi_stream(In,Len)', []),
1149
+ http_post_data(cgi_stream(In), Out, HdrExtra).
1150
+ http_post_data(cgi_stream(In), Out, HdrExtra) =>
1151
+ http_read_header(In, Header0),
1152
+ http_update_encoding(Header0, Encoding, Header),
1153
+ content_length_in_encoding(Encoding, In, Size),
1154
+ http_join_headers(HdrExtra, Header, Hdr2),
1155
+ phrase(post_header(cgi_data(Size), Hdr2), HeaderText),
1156
+ send_request_header(Out, HeaderText),
1157
+ setup_call_cleanup(
1158
+ set_stream(Out, encoding(Encoding)),
1159
+ copy_stream_data(In, Out),
1160
+ set_stream(Out, encoding(octet))).
1161
+ http_post_data(form(Fields), Out, HdrExtra) =>
1162
+ parse_url_search(Codes, Fields),
1163
+ length(Codes, Size),
1164
+ http_join_headers(HdrExtra,
1165
+ [ content_type('application/x-www-form-urlencoded')
1166
+ ], Header),
1167
+ phrase(post_header(cgi_data(Size), Header), HeaderChars),
1168
+ send_request_header(Out, HeaderChars),
1169
+ format(Out, '~s', [Codes]).
1170
+ http_post_data(form_data(Data), Out, HdrExtra) =>
1171
+ setup_call_cleanup(
1172
+ new_memory_file(MemFile),
1173
+ ( setup_call_cleanup(
1174
+ open_memory_file(MemFile, write, MimeOut),
1175
+ mime_pack(Data, MimeOut, Boundary),
1176
+ close(MimeOut)),
1177
+ size_memory_file(MemFile, Size, octet),
1178
+ format(string(ContentType),
1179
+ 'multipart/form-data; boundary=~w', [Boundary]),
1180
+ http_join_headers(HdrExtra,
1181
+ [ mime_version('1.0'),
1182
+ content_type(ContentType)
1183
+ ], Header),
1184
+ phrase(post_header(cgi_data(Size), Header), HeaderChars),
1185
+ send_request_header(Out, HeaderChars),
1186
+ setup_call_cleanup(
1187
+ open_memory_file(MemFile, read, In, [encoding(octet)]),
1188
+ copy_stream_data(In, Out),
1189
+ close(In))
1190
+ ),
1191
+ free_memory_file(MemFile)).
1192
+ http_post_data(List, Out, HdrExtra), is_list(List) => % multipart-mixed
1193
+ setup_call_cleanup(
1194
+ new_memory_file(MemFile),
1195
+ ( setup_call_cleanup(
1196
+ open_memory_file(MemFile, write, MimeOut),
1197
+ mime_pack(List, MimeOut, Boundary),
1198
+ close(MimeOut)),
1199
+ size_memory_file(MemFile, Size, octet),
1200
+ format(string(ContentType),
1201
+ 'multipart/mixed; boundary=~w', [Boundary]),
1202
+ http_join_headers(HdrExtra,
1203
+ [ mime_version('1.0'),
1204
+ content_type(ContentType)
1205
+ ], Header),
1206
+ phrase(post_header(cgi_data(Size), Header), HeaderChars),
1207
+ send_request_header(Out, HeaderChars),
1208
+ setup_call_cleanup(
1209
+ open_memory_file(MemFile, read, In, [encoding(octet)]),
1210
+ copy_stream_data(In, Out),
1211
+ close(In))
1212
+ ),
1213
+ free_memory_file(MemFile)).
1214
+
1215
+ %! post_header(+Data, +HeaderExtra)//
1216
+ %
1217
+ % Generate the POST header, emitting HeaderExtra, followed by the
1218
+ % HTTP Content-length and Content-type fields.
1219
+
1220
+ post_header(html(Tokens), HdrExtra) -->
1221
+ header_fields(HdrExtra, Len),
1222
+ content_length(html(Tokens), Len),
1223
+ content_type(text/html),
1224
+ "\r\n".
1225
+ post_header(file(Type, File), HdrExtra) -->
1226
+ header_fields(HdrExtra, Len),
1227
+ content_length(file(File), Len),
1228
+ content_type(Type),
1229
+ "\r\n".
1230
+ post_header(memory_file(Type, File), HdrExtra) -->
1231
+ header_fields(HdrExtra, Len),
1232
+ content_length(memory_file(File), Len),
1233
+ content_type(Type),
1234
+ "\r\n".
1235
+ post_header(cgi_data(Size), HdrExtra) -->
1236
+ header_fields(HdrExtra, Len),
1237
+ content_length(Size, Len),
1238
+ "\r\n".
1239
+ post_header(codes(Type, Codes), HdrExtra) -->
1240
+ header_fields(HdrExtra, Len),
1241
+ content_length(codes(Codes, utf8), Len),
1242
+ content_type(Type, utf8),
1243
+ "\r\n".
1244
+ post_header(bytes(Type, Bytes), HdrExtra) -->
1245
+ header_fields(HdrExtra, Len),
1246
+ content_length(bytes(Bytes), Len),
1247
+ content_type(Type),
1248
+ "\r\n".
1249
+ post_header(atom(Type, Atom), HdrExtra) -->
1250
+ header_fields(HdrExtra, Len),
1251
+ content_length(atom(Atom, utf8), Len),
1252
+ content_type(Type, utf8),
1253
+ "\r\n".
1254
+ post_header(string(Type, String), HdrExtra) -->
1255
+ header_fields(HdrExtra, Len),
1256
+ content_length(string(String, utf8), Len),
1257
+ content_type(Type, utf8),
1258
+ "\r\n".
1259
+
1260
+
1261
+ /*******************************
1262
+ * OUTPUT HEADER DCG *
1263
+ *******************************/
1264
+
1265
+ %! http_reply_header(+Out:stream, +What, +HdrExtra) is det.
1266
+ %
1267
+ % Create a reply header using reply_header//3 and send it to
1268
+ % Stream.
1269
+
1270
+ http_reply_header(Out, What, HdrExtra) :-
1271
+ phrase(reply_header(What, HdrExtra, _Code), String),
1272
+ !,
1273
+ send_reply_header(Out, String).
1274
+
1275
+ %! reply_header(+Data, +HdrExtra, -Code)// is det.
1276
+ %
1277
+ % Grammar that realises the HTTP handler for sending Data. Data is
1278
+ % a real data object as described with http_reply/2 or a
1279
+ % not-200-ok HTTP status reply. The following status replies are
1280
+ % defined.
1281
+ %
1282
+ % * created(+URL, +HTMLTokens)
1283
+ % * moved(+URL, +HTMLTokens)
1284
+ % * moved_temporary(+URL, +HTMLTokens)
1285
+ % * see_other(+URL, +HTMLTokens)
1286
+ % * status(+Status)
1287
+ % * status(+Status, +HTMLTokens)
1288
+ % * authorise(+Method, +Realm, +Tokens)
1289
+ % * authorise(+Method, +Tokens)
1290
+ % * not_found(+URL, +HTMLTokens)
1291
+ % * server_error(+Error, +Tokens)
1292
+ % * resource_error(+Error, +Tokens)
1293
+ % * service_unavailable(+Why, +Tokens)
1294
+ %
1295
+ % @see http_status_reply/4 formulates the not-200-ok HTTP replies.
1296
+
1297
+ reply_header(Data, Dict) -->
1298
+ { _{header:HdrExtra, code:Code} :< Dict },
1299
+ reply_header(Data, HdrExtra, Code).
1300
+
1301
+ reply_header(string(String), HdrExtra, Code) -->
1302
+ reply_header(string(text/plain, String), HdrExtra, Code).
1303
+ reply_header(string(Type, String), HdrExtra, Code) -->
1304
+ vstatus(ok, Code, HdrExtra),
1305
+ date(now),
1306
+ header_fields(HdrExtra, CLen),
1307
+ content_length(codes(String, utf8), CLen),
1308
+ content_type(Type, utf8),
1309
+ "\r\n".
1310
+ reply_header(bytes(Type, Bytes), HdrExtra, Code) -->
1311
+ vstatus(ok, Code, HdrExtra),
1312
+ date(now),
1313
+ header_fields(HdrExtra, CLen),
1314
+ content_length(bytes(Bytes), CLen),
1315
+ content_type(Type),
1316
+ "\r\n".
1317
+ reply_header(html(Tokens), HdrExtra, Code) -->
1318
+ vstatus(ok, Code, HdrExtra),
1319
+ date(now),
1320
+ header_fields(HdrExtra, CLen),
1321
+ content_length(html(Tokens, utf8), CLen),
1322
+ content_type(text/html, utf8),
1323
+ "\r\n".
1324
+ reply_header(file(Type, File), HdrExtra, Code) -->
1325
+ vstatus(ok, Code, HdrExtra),
1326
+ date(now),
1327
+ modified(file(File)),
1328
+ header_fields(HdrExtra, CLen),
1329
+ content_length(file(File), CLen),
1330
+ content_type(Type),
1331
+ "\r\n".
1332
+ reply_header(gzip_file(Type, File), HdrExtra, Code) -->
1333
+ vstatus(ok, Code, HdrExtra),
1334
+ date(now),
1335
+ modified(file(File)),
1336
+ header_fields(HdrExtra, CLen),
1337
+ content_length(file(File), CLen),
1338
+ content_type(Type),
1339
+ content_encoding(gzip),
1340
+ "\r\n".
1341
+ reply_header(file(Type, File, Range), HdrExtra, Code) -->
1342
+ vstatus(partial_content, Code, HdrExtra),
1343
+ date(now),
1344
+ modified(file(File)),
1345
+ header_fields(HdrExtra, CLen),
1346
+ content_length(file(File, Range), CLen),
1347
+ content_type(Type),
1348
+ "\r\n".
1349
+ reply_header(tmp_file(Type, File), HdrExtra, Code) -->
1350
+ vstatus(ok, Code, HdrExtra),
1351
+ date(now),
1352
+ header_fields(HdrExtra, CLen),
1353
+ content_length(file(File), CLen),
1354
+ content_type(Type),
1355
+ "\r\n".
1356
+ reply_header(cgi_data(Size), HdrExtra, Code) -->
1357
+ vstatus(ok, Code, HdrExtra),
1358
+ date(now),
1359
+ header_fields(HdrExtra, CLen),
1360
+ content_length(Size, CLen),
1361
+ "\r\n".
1362
+ reply_header(event_stream, HdrExtra, Code) -->
1363
+ vstatus(ok, Code, HdrExtra),
1364
+ date(now),
1365
+ header_fields(HdrExtra, _),
1366
+ "\r\n".
1367
+ reply_header(chunked_data, HdrExtra, Code) -->
1368
+ vstatus(ok, Code, HdrExtra),
1369
+ date(now),
1370
+ header_fields(HdrExtra, _),
1371
+ ( {memberchk(transfer_encoding(_), HdrExtra)}
1372
+ -> ""
1373
+ ; transfer_encoding(chunked)
1374
+ ),
1375
+ "\r\n".
1376
+ % non-200 replies without a body (e.g., 1xx, 204, 304)
1377
+ reply_header(status(Status), HdrExtra, Code) -->
1378
+ vstatus(Status, Code),
1379
+ header_fields(HdrExtra, Clen),
1380
+ { Clen = 0 },
1381
+ "\r\n".
1382
+ % non-200 replies with a body
1383
+ reply_header(Data, HdrExtra, Code) -->
1384
+ { status_reply_headers(Data,
1385
+ body(Type, Encoding, Content),
1386
+ ReplyHeaders),
1387
+ http_join_headers(ReplyHeaders, HdrExtra, Headers),
1388
+ functor(Data, CodeName, _)
1389
+ },
1390
+ vstatus(CodeName, Code, Headers),
1391
+ date(now),
1392
+ header_fields(Headers, CLen),
1393
+ content_length(codes(Content, Encoding), CLen),
1394
+ content_type(Type, Encoding),
1395
+ "\r\n".
1396
+
1397
+ status_reply_headers(created(Location, Body), Body,
1398
+ [ location(Location) ]).
1399
+ status_reply_headers(moved(To, Body), Body,
1400
+ [ location(To) ]).
1401
+ status_reply_headers(moved_temporary(To, Body), Body,
1402
+ [ location(To) ]).
1403
+ status_reply_headers(gone(_URL, Body), Body, []).
1404
+ status_reply_headers(see_other(To, Body), Body,
1405
+ [ location(To) ]).
1406
+ status_reply_headers(authorise(Method, Body), Body,
1407
+ [ www_authenticate(Method) ]).
1408
+ status_reply_headers(not_found(_URL, Body), Body, []).
1409
+ status_reply_headers(forbidden(_URL, Body), Body, []).
1410
+ status_reply_headers(method_not_allowed(_Method, _URL, Body), Body, []).
1411
+ status_reply_headers(server_error(_Error, Body), Body, []).
1412
+ status_reply_headers(service_unavailable(_Why, Body), Body, []).
1413
+ status_reply_headers(not_acceptable(_Why, Body), Body, []).
1414
+ status_reply_headers(bad_request(_Error, Body), Body, []).
1415
+
1416
+
1417
+ %! vstatus(+Status, -Code)// is det.
1418
+ %! vstatus(+Status, -Code, +HdrExtra)// is det.
1419
+ %
1420
+ % Emit the HTTP header for Status
1421
+
1422
+ vstatus(_Status, Code, HdrExtra) -->
1423
+ {memberchk(status(Code), HdrExtra)},
1424
+ !,
1425
+ vstatus(_NewStatus, Code).
1426
+ vstatus(Status, Code, _) -->
1427
+ vstatus(Status, Code).
1428
+
1429
+ vstatus(Status, Code) -->
1430
+ "HTTP/1.1 ",
1431
+ status_number(Status, Code),
1432
+ " ",
1433
+ status_comment(Status),
1434
+ "\r\n".
1435
+
1436
+ %! status_number(?Status, ?Code)// is semidet.
1437
+ %
1438
+ % Parse/generate the HTTP status numbers and map them to the
1439
+ % proper name.
1440
+ %
1441
+ % @see See the source code for supported status names and codes.
1442
+
1443
+ status_number(Status, Code) -->
1444
+ { var(Status) },
1445
+ !,
1446
+ integer(Code),
1447
+ { status_number(Status, Code) },
1448
+ !.
1449
+ status_number(Status, Code) -->
1450
+ { status_number(Status, Code) },
1451
+ integer(Code).
1452
+
1453
+ %! status_number(+Status:atom, -Code:nonneg) is det.
1454
+ %! status_number(-Status:atom, +Code:nonneg) is det.
1455
+ %
1456
+ % Relates a symbolic HTTP status names to their integer Code.
1457
+ % Each code also needs a rule for status_comment//1.
1458
+ %
1459
+ % @throws type_error If Code is instantiated with something other than
1460
+ % an integer.
1461
+ % @throws domain_error If Code is instantiated with an integer
1462
+ % outside of the range [100-599] of defined
1463
+ % HTTP status codes.
1464
+
1465
+ % Unrecognized status codes that are within a defined code class.
1466
+ % RFC 7231 states:
1467
+ % "[...] a client MUST understand the class of any status code,
1468
+ % as indicated by the first digit, and treat an unrecognized status code
1469
+ % as being equivalent to the `x00` status code of that class [...]
1470
+ % "
1471
+ % @see http://tools.ietf.org/html/rfc7231#section-6
1472
+
1473
+ status_number(Status, Code) :-
1474
+ nonvar(Status),
1475
+ !,
1476
+ status_number_fact(Status, Code).
1477
+ status_number(Status, Code) :-
1478
+ nonvar(Code),
1479
+ !,
1480
+ ( between(100, 599, Code)
1481
+ -> ( status_number_fact(Status, Code)
1482
+ -> true
1483
+ ; ClassCode is Code // 100 * 100,
1484
+ status_number_fact(Status, ClassCode)
1485
+ )
1486
+ ; domain_error(http_code, Code)
1487
+ ).
1488
+
1489
+ status_number_fact(continue, 100).
1490
+ status_number_fact(switching_protocols, 101).
1491
+ status_number_fact(ok, 200).
1492
+ status_number_fact(created, 201).
1493
+ status_number_fact(accepted, 202).
1494
+ status_number_fact(non_authoritative_info, 203).
1495
+ status_number_fact(no_content, 204).
1496
+ status_number_fact(reset_content, 205).
1497
+ status_number_fact(partial_content, 206).
1498
+ status_number_fact(multiple_choices, 300).
1499
+ status_number_fact(moved, 301).
1500
+ status_number_fact(moved_temporary, 302).
1501
+ status_number_fact(see_other, 303).
1502
+ status_number_fact(not_modified, 304).
1503
+ status_number_fact(use_proxy, 305).
1504
+ status_number_fact(unused, 306).
1505
+ status_number_fact(temporary_redirect, 307).
1506
+ status_number_fact(bad_request, 400).
1507
+ status_number_fact(authorise, 401).
1508
+ status_number_fact(payment_required, 402).
1509
+ status_number_fact(forbidden, 403).
1510
+ status_number_fact(not_found, 404).
1511
+ status_number_fact(method_not_allowed, 405).
1512
+ status_number_fact(not_acceptable, 406).
1513
+ status_number_fact(request_timeout, 408).
1514
+ status_number_fact(conflict, 409).
1515
+ status_number_fact(gone, 410).
1516
+ status_number_fact(length_required, 411).
1517
+ status_number_fact(payload_too_large, 413).
1518
+ status_number_fact(uri_too_long, 414).
1519
+ status_number_fact(unsupported_media_type, 415).
1520
+ status_number_fact(expectation_failed, 417).
1521
+ status_number_fact(upgrade_required, 426).
1522
+ status_number_fact(server_error, 500).
1523
+ status_number_fact(not_implemented, 501).
1524
+ status_number_fact(bad_gateway, 502).
1525
+ status_number_fact(service_unavailable, 503).
1526
+ status_number_fact(gateway_timeout, 504).
1527
+ status_number_fact(http_version_not_supported, 505).
1528
+
1529
+
1530
+ %! status_comment(+Code:atom)// is det.
1531
+ %
1532
+ % Emit standard HTTP human-readable comment on the reply-status.
1533
+
1534
+ status_comment(continue) -->
1535
+ "Continue".
1536
+ status_comment(switching_protocols) -->
1537
+ "Switching Protocols".
1538
+ status_comment(ok) -->
1539
+ "OK".
1540
+ status_comment(created) -->
1541
+ "Created".
1542
+ status_comment(accepted) -->
1543
+ "Accepted".
1544
+ status_comment(non_authoritative_info) -->
1545
+ "Non-Authoritative Information".
1546
+ status_comment(no_content) -->
1547
+ "No Content".
1548
+ status_comment(reset_content) -->
1549
+ "Reset Content".
1550
+ status_comment(created) -->
1551
+ "Created".
1552
+ status_comment(partial_content) -->
1553
+ "Partial content".
1554
+ status_comment(multiple_choices) -->
1555
+ "Multiple Choices".
1556
+ status_comment(moved) -->
1557
+ "Moved Permanently".
1558
+ status_comment(moved_temporary) -->
1559
+ "Moved Temporary".
1560
+ status_comment(see_other) -->
1561
+ "See Other".
1562
+ status_comment(not_modified) -->
1563
+ "Not Modified".
1564
+ status_comment(use_proxy) -->
1565
+ "Use Proxy".
1566
+ status_comment(unused) -->
1567
+ "Unused".
1568
+ status_comment(temporary_redirect) -->
1569
+ "Temporary Redirect".
1570
+ status_comment(bad_request) -->
1571
+ "Bad Request".
1572
+ status_comment(authorise) -->
1573
+ "Authorization Required".
1574
+ status_comment(payment_required) -->
1575
+ "Payment Required".
1576
+ status_comment(forbidden) -->
1577
+ "Forbidden".
1578
+ status_comment(not_found) -->
1579
+ "Not Found".
1580
+ status_comment(method_not_allowed) -->
1581
+ "Method Not Allowed".
1582
+ status_comment(not_acceptable) -->
1583
+ "Not Acceptable".
1584
+ status_comment(request_timeout) -->
1585
+ "Request Timeout".
1586
+ status_comment(conflict) -->
1587
+ "Conflict".
1588
+ status_comment(gone) -->
1589
+ "Gone".
1590
+ status_comment(length_required) -->
1591
+ "Length Required".
1592
+ status_comment(payload_too_large) -->
1593
+ "Payload Too Large".
1594
+ status_comment(uri_too_long) -->
1595
+ "URI Too Long".
1596
+ status_comment(unsupported_media_type) -->
1597
+ "Unsupported Media Type".
1598
+ status_comment(expectation_failed) -->
1599
+ "Expectation Failed".
1600
+ status_comment(upgrade_required) -->
1601
+ "Upgrade Required".
1602
+ status_comment(server_error) -->
1603
+ "Internal Server Error".
1604
+ status_comment(not_implemented) -->
1605
+ "Not Implemented".
1606
+ status_comment(bad_gateway) -->
1607
+ "Bad Gateway".
1608
+ status_comment(service_unavailable) -->
1609
+ "Service Unavailable".
1610
+ status_comment(gateway_timeout) -->
1611
+ "Gateway Timeout".
1612
+ status_comment(http_version_not_supported) -->
1613
+ "HTTP Version Not Supported".
1614
+
1615
+ date(Time) -->
1616
+ "Date: ",
1617
+ ( { Time == now }
1618
+ -> now
1619
+ ; rfc_date(Time)
1620
+ ),
1621
+ "\r\n".
1622
+
1623
+ modified(file(File)) -->
1624
+ !,
1625
+ { time_file(File, Time)
1626
+ },
1627
+ modified(Time).
1628
+ modified(Time) -->
1629
+ "Last-modified: ",
1630
+ ( { Time == now }
1631
+ -> now
1632
+ ; rfc_date(Time)
1633
+ ),
1634
+ "\r\n".
1635
+
1636
+
1637
+ %! content_length(+Object, ?Len)// is det.
1638
+ %
1639
+ % Emit the content-length field and (optionally) the content-range
1640
+ % field.
1641
+ %
1642
+ % @param Len Number of bytes specified
1643
+
1644
+ content_length(file(File, bytes(From, To)), Len) -->
1645
+ !,
1646
+ { size_file(File, Size),
1647
+ ( To == end
1648
+ -> Len is Size - From,
1649
+ RangeEnd is Size - 1
1650
+ ; Len is To+1 - From, % To is index of last byte
1651
+ RangeEnd = To
1652
+ )
1653
+ },
1654
+ content_range(bytes, From, RangeEnd, Size),
1655
+ content_length(Len, Len).
1656
+ content_length(Reply, Len) -->
1657
+ { length_of(Reply, Len)
1658
+ },
1659
+ "Content-Length: ", integer(Len),
1660
+ "\r\n".
1661
+
1662
+ :- meta_predicate
1663
+ print_length(0, -, +, -).
1664
+
1665
+ :- det(length_of/2).
1666
+ length_of(_, Len), integer(Len) => true.
1667
+ length_of(string(String, Encoding), Len) =>
1668
+ length_of(codes(String, Encoding), Len).
1669
+ length_of(codes(String, Encoding), Len) =>
1670
+ print_length(format(Out, '~s', [String]), Out, Encoding, Len).
1671
+ length_of(atom(Atom, Encoding), Len) =>
1672
+ print_length(format(Out, '~a', [Atom]), Out, Encoding, Len).
1673
+ length_of(file(File), Len) =>
1674
+ size_file(File, Len).
1675
+ length_of(memory_file(Handle), Len) =>
1676
+ size_memory_file(Handle, Len, octet).
1677
+ length_of(html_tokens(Tokens), Len) =>
1678
+ html_print_length(Tokens, Len).
1679
+ length_of(html(Tokens, Encoding), Len) =>
1680
+ print_length(print_html(Out, Tokens), Out, Encoding, Len).
1681
+ length_of(bytes(Bytes), Len) =>
1682
+ print_length(format(Out, '~s', [Bytes]), Out, octet, Len).
1683
+ length_of(Num, Len), integer(Num) =>
1684
+ Len = Num.
1685
+
1686
+ print_length(Goal, Out, Encoding, Len) :-
1687
+ setup_call_cleanup(
1688
+ open_null_stream(Out),
1689
+ ( set_stream(Out, encoding(Encoding)),
1690
+ call(Goal),
1691
+ byte_count(Out, Len)
1692
+ ),
1693
+ close(Out)).
1694
+
1695
+ %! content_range(+Unit:atom, +From:int, +RangeEnd:int, +Size:int)// is det
1696
+ %
1697
+ % Emit the =|Content-Range|= header for partial content (206)
1698
+ % replies.
1699
+
1700
+ content_range(Unit, From, RangeEnd, Size) -->
1701
+ "Content-Range: ", atom(Unit), " ",
1702
+ integer(From), "-", integer(RangeEnd), "/", integer(Size),
1703
+ "\r\n".
1704
+
1705
+ content_encoding(Encoding) -->
1706
+ "Content-Encoding: ", atom(Encoding), "\r\n".
1707
+
1708
+ transfer_encoding(Encoding) -->
1709
+ "Transfer-Encoding: ", atom(Encoding), "\r\n".
1710
+
1711
+ content_type(Type) -->
1712
+ content_type(Type, _).
1713
+
1714
+ content_type(Type, Charset) -->
1715
+ ctype(Type),
1716
+ charset(Charset),
1717
+ "\r\n".
1718
+
1719
+ ctype(Main/Sub) -->
1720
+ !,
1721
+ "Content-Type: ",
1722
+ atom(Main),
1723
+ "/",
1724
+ atom(Sub).
1725
+ ctype(Type) -->
1726
+ !,
1727
+ "Content-Type: ",
1728
+ atom(Type).
1729
+
1730
+ charset(Var) -->
1731
+ { var(Var) },
1732
+ !.
1733
+ charset(utf8) -->
1734
+ !,
1735
+ "; charset=UTF-8".
1736
+ charset(CharSet) -->
1737
+ "; charset=",
1738
+ atom(CharSet).
1739
+
1740
+ %! header_field(-Name, -Value)// is det.
1741
+ %! header_field(+Name, +Value) is det.
1742
+ %
1743
+ % Process an HTTP request property. Request properties appear as a
1744
+ % single line in an HTTP header.
1745
+
1746
+ header_field(Name, Value) -->
1747
+ { var(Name) }, % parsing
1748
+ !,
1749
+ field_name(Name),
1750
+ ":",
1751
+ whites,
1752
+ read_field_value(ValueChars),
1753
+ blanks_to_nl,
1754
+ !,
1755
+ { field_to_prolog(Name, ValueChars, Value)
1756
+ -> true
1757
+ ; atom_codes(Value, ValueChars),
1758
+ domain_error(Name, Value)
1759
+ }.
1760
+ header_field(Name, Value) -->
1761
+ field_name(Name),
1762
+ ": ",
1763
+ field_value(Name, Value),
1764
+ "\r\n".
1765
+
1766
+ %! read_field_value(-Codes)//
1767
+ %
1768
+ % Read a field eagerly up to the next whitespace
1769
+
1770
+ read_field_value([H|T]) -->
1771
+ [H],
1772
+ { \+ code_type(H, space) },
1773
+ !,
1774
+ read_field_value(T).
1775
+ read_field_value([]) -->
1776
+ "".
1777
+ read_field_value([H|T]) -->
1778
+ [H],
1779
+ read_field_value(T).
1780
+
1781
+ %! send_reply_header(+Out, +String) is det.
1782
+ %! send_request_header(+Out, +String) is det.
1783
+ %
1784
+ % Low level routines to send a single HTTP request or reply line.
1785
+
1786
+ send_reply_header(Out, String) :-
1787
+ debug(http(send_reply), "< ~s", [String]),
1788
+ format(Out, '~s', [String]).
1789
+
1790
+ send_request_header(Out, String) :-
1791
+ debug(http(send_request), "> ~s", [String]),
1792
+ format(Out, '~s', [String]).
1793
+
1794
+ %! http_parse_header_value(+Field, +Value, -Prolog) is semidet.
1795
+ %
1796
+ % Translate Value in a meaningful Prolog term. Field denotes the
1797
+ % HTTP request field for which we do the translation. Supported
1798
+ % fields are:
1799
+ %
1800
+ % * content_length
1801
+ % Converted into an integer
1802
+ % * status
1803
+ % Converted into an integer
1804
+ % * cookie
1805
+ % Converted into a list with Name=Value by cookies//1.
1806
+ % * set_cookie
1807
+ % Converted into a term set_cookie(Name, Value, Options).
1808
+ % Options is a list consisting of Name=Value or a single
1809
+ % atom (e.g., =secure=)
1810
+ % * host
1811
+ % Converted to HostName:Port if applicable.
1812
+ % * range
1813
+ % Converted into bytes(From, To), where From is an integer
1814
+ % and To is either an integer or the atom =end=.
1815
+ % * accept
1816
+ % Parsed to a list of media descriptions. Each media is a term
1817
+ % media(Type, TypeParams, Quality, AcceptExts). The list is
1818
+ % sorted according to preference.
1819
+ % * content_disposition
1820
+ % Parsed into disposition(Name, Attributes), where Attributes is
1821
+ % a list of Name=Value pairs.
1822
+ % * content_type
1823
+ % Parsed into media(Type/SubType, Attributes), where Attributes
1824
+ % is a list of Name=Value pairs.
1825
+ % * expires
1826
+ % Parsed into a time stamp using http_timestamp/2.
1827
+ %
1828
+ % As some fields are already parsed in the `Request`, this predicate
1829
+ % is a no-op when called on an already parsed field.
1830
+ %
1831
+ % @arg Value is either an atom, a list of codes or an already parsed
1832
+ % header value.
1833
+
1834
+ http_parse_header_value(Field, Value, Prolog) :-
1835
+ known_field(Field, _, Type),
1836
+ ( already_parsed(Type, Value)
1837
+ -> Prolog = Value
1838
+ ; parse_header_value_atom(Field, Value, Prolog)
1839
+ -> true
1840
+ ; to_codes(Value, Codes),
1841
+ parse_header_value(Field, Codes, Prolog)
1842
+ ).
1843
+
1844
+ already_parsed(integer, V) :- !, integer(V).
1845
+ already_parsed(list(Type), L) :- !, is_list(L), maplist(already_parsed(Type), L).
1846
+ already_parsed(Term, V) :- subsumes_term(Term, V).
1847
+
1848
+
1849
+ %! known_field(?FieldName, ?AutoConvert, -Type)
1850
+ %
1851
+ % True if the value of FieldName is by default translated into a
1852
+ % Prolog data structure.
1853
+
1854
+ known_field(content_length, true, integer).
1855
+ known_field(status, true, integer).
1856
+ known_field(expires, false, number).
1857
+ known_field(cookie, true, list(_=_)).
1858
+ known_field(set_cookie, true, list(set_cookie(_Name,_Value,_Options))).
1859
+ known_field(host, true, _Host:_Port).
1860
+ known_field(range, maybe, bytes(_,_)).
1861
+ known_field(accept, maybe, list(media(_Type, _Parms, _Q, _Exts))).
1862
+ known_field(content_disposition, maybe, disposition(_Name, _Attributes)).
1863
+ known_field(content_type, false, media(_Type/_Sub, _Attributes)).
1864
+
1865
+ to_codes(In, Codes) :-
1866
+ ( is_list(In)
1867
+ -> Codes = In
1868
+ ; atom_codes(In, Codes)
1869
+ ).
1870
+
1871
+ %! field_to_prolog(+Field, +ValueCodes, -Prolog) is semidet.
1872
+ %
1873
+ % Translate the value string into a sensible Prolog term. For
1874
+ % known_fields(_,true), this must succeed. For =maybe=, we just
1875
+ % return the atom if the translation fails.
1876
+
1877
+ field_to_prolog(Field, Codes, Prolog) :-
1878
+ known_field(Field, true, _Type),
1879
+ !,
1880
+ ( parse_header_value(Field, Codes, Prolog0)
1881
+ -> Prolog = Prolog0
1882
+ ).
1883
+ field_to_prolog(Field, Codes, Prolog) :-
1884
+ known_field(Field, maybe, _Type),
1885
+ parse_header_value(Field, Codes, Prolog0),
1886
+ !,
1887
+ Prolog = Prolog0.
1888
+ field_to_prolog(_, Codes, Atom) :-
1889
+ atom_codes(Atom, Codes).
1890
+
1891
+ %! parse_header_value_atom(+Field, +ValueAtom, -Value) is semidet.
1892
+ %
1893
+ % As parse_header_value/3, but avoid translation to codes.
1894
+
1895
+ parse_header_value_atom(content_length, Atom, ContentLength) :-
1896
+ atomic(Atom),
1897
+ atom_number(Atom, ContentLength).
1898
+ parse_header_value_atom(expires, Atom, Stamp) :-
1899
+ http_timestamp(Stamp, Atom).
1900
+
1901
+ %! parse_header_value(+Field, +ValueCodes, -Value) is semidet.
1902
+ %
1903
+ % Parse the value text of an HTTP field into a meaningful Prolog
1904
+ % representation.
1905
+
1906
+ parse_header_value(content_length, ValueChars, ContentLength) :-
1907
+ number_codes(ContentLength, ValueChars).
1908
+ parse_header_value(expires, ValueCodes, Stamp) :-
1909
+ http_timestamp(Stamp, ValueCodes).
1910
+ parse_header_value(status, ValueChars, Code) :-
1911
+ ( phrase(" ", L, _),
1912
+ append(Pre, L, ValueChars)
1913
+ -> number_codes(Code, Pre)
1914
+ ; number_codes(Code, ValueChars)
1915
+ ).
1916
+ parse_header_value(cookie, ValueChars, Cookies) :-
1917
+ debug(cookie, 'Cookie: ~s', [ValueChars]),
1918
+ phrase(cookies(Cookies), ValueChars).
1919
+ parse_header_value(set_cookie, ValueChars, SetCookie) :-
1920
+ debug(cookie, 'SetCookie: ~s', [ValueChars]),
1921
+ phrase(set_cookie(SetCookie), ValueChars).
1922
+ parse_header_value(host, ValueChars, Host) :-
1923
+ ( append(HostChars, [0':|PortChars], ValueChars),
1924
+ catch(number_codes(Port, PortChars), _, fail)
1925
+ -> atom_codes(HostName, HostChars),
1926
+ Host = HostName:Port
1927
+ ; atom_codes(Host, ValueChars)
1928
+ ).
1929
+ parse_header_value(range, ValueChars, Range) :-
1930
+ phrase(range(Range), ValueChars).
1931
+ parse_header_value(accept, ValueChars, Media) :-
1932
+ parse_accept(ValueChars, Media).
1933
+ parse_header_value(content_disposition, ValueChars, Disposition) :-
1934
+ phrase(content_disposition(Disposition), ValueChars).
1935
+ parse_header_value(content_type, ValueChars, Type) :-
1936
+ phrase(parse_content_type(Type), ValueChars).
1937
+
1938
+ %! field_value(+Name, +Value)//
1939
+
1940
+ field_value(_, set_cookie(Name, Value, Options)) -->
1941
+ !,
1942
+ atom(Name), "=", atom(Value),
1943
+ value_options(Options, cookie).
1944
+ field_value(_, disposition(Disposition, Options)) -->
1945
+ !,
1946
+ atom(Disposition), value_options(Options, disposition).
1947
+ field_value(www_authenticate, Auth) -->
1948
+ auth_field_value(Auth).
1949
+ field_value(_, Atomic) -->
1950
+ atom(Atomic).
1951
+
1952
+ %! auth_field_value(+AuthValue)//
1953
+ %
1954
+ % Emit the authentication requirements (WWW-Authenticate field).
1955
+
1956
+ auth_field_value(negotiate(Data)) -->
1957
+ "Negotiate ",
1958
+ { base64(Data, DataBase64),
1959
+ atom_codes(DataBase64, Codes)
1960
+ },
1961
+ string(Codes).
1962
+ auth_field_value(negotiate) -->
1963
+ "Negotiate".
1964
+ auth_field_value(basic) -->
1965
+ !,
1966
+ "Basic".
1967
+ auth_field_value(basic(Realm)) -->
1968
+ "Basic Realm=\"", atom(Realm), "\"".
1969
+ auth_field_value(digest) -->
1970
+ !,
1971
+ "Digest".
1972
+ auth_field_value(digest(Details)) -->
1973
+ "Digest ", atom(Details).
1974
+
1975
+ %! value_options(+List, +Field)//
1976
+ %
1977
+ % Emit field parameters such as =|; charset=UTF-8|=. There
1978
+ % are three versions: a plain _key_ (`secure`), _token_ values
1979
+ % and _quoted string_ values. Seems we cannot deduce that from
1980
+ % the actual value.
1981
+
1982
+ value_options([], _) --> [].
1983
+ value_options([H|T], Field) -->
1984
+ "; ", value_option(H, Field),
1985
+ value_options(T, Field).
1986
+
1987
+ value_option(secure=true, cookie) -->
1988
+ !,
1989
+ "secure".
1990
+ value_option(Name=Value, Type) -->
1991
+ { string_option(Name, Type) },
1992
+ !,
1993
+ atom(Name), "=",
1994
+ qstring(Value).
1995
+ value_option(Name=Value, Type) -->
1996
+ { token_option(Name, Type) },
1997
+ !,
1998
+ atom(Name), "=", atom(Value).
1999
+ value_option(Name=Value, _Type) -->
2000
+ atom(Name), "=",
2001
+ option_value(Value).
2002
+
2003
+ string_option(filename, disposition).
2004
+
2005
+ token_option(path, cookie).
2006
+
2007
+ option_value(Value) -->
2008
+ { number(Value) },
2009
+ !,
2010
+ number(Value).
2011
+ option_value(Value) -->
2012
+ { ( atom(Value)
2013
+ -> true
2014
+ ; string(Value)
2015
+ ),
2016
+ forall(string_code(_, Value, C),
2017
+ token_char(C))
2018
+ },
2019
+ !,
2020
+ atom(Value).
2021
+ option_value(Atomic) -->
2022
+ qstring(Atomic).
2023
+
2024
+ qstring(Atomic) -->
2025
+ { string_codes(Atomic, Codes) },
2026
+ "\"",
2027
+ qstring_codes(Codes),
2028
+ "\"".
2029
+
2030
+ qstring_codes([]) --> [].
2031
+ qstring_codes([H|T]) --> qstring_code(H), qstring_codes(T).
2032
+
2033
+ qstring_code(C) --> {qstring_esc(C)}, !, "\\", [C].
2034
+ qstring_code(C) --> [C].
2035
+
2036
+ qstring_esc(0'").
2037
+ qstring_esc(C) :- ctl(C).
2038
+
2039
+
2040
+ /*******************************
2041
+ * ACCEPT HEADERS *
2042
+ *******************************/
2043
+
2044
+ :- dynamic accept_cache/2.
2045
+ :- volatile accept_cache/2.
2046
+
2047
+ parse_accept(Codes, Media) :-
2048
+ atom_codes(Atom, Codes),
2049
+ ( accept_cache(Atom, Media0)
2050
+ -> Media = Media0
2051
+ ; phrase(accept(Media0), Codes),
2052
+ keysort(Media0, Media1),
2053
+ pairs_values(Media1, Media2),
2054
+ assertz(accept_cache(Atom, Media2)),
2055
+ Media = Media2
2056
+ ).
2057
+
2058
+ %! accept(-Media)// is semidet.
2059
+ %
2060
+ % Parse an HTTP Accept: header
2061
+
2062
+ accept([H|T]) -->
2063
+ blanks,
2064
+ media_range(H),
2065
+ blanks,
2066
+ ( ","
2067
+ -> accept(T)
2068
+ ; {T=[]}
2069
+ ).
2070
+
2071
+ media_range(s(SortQuality,Spec)-media(Type, TypeParams, Quality, AcceptExts)) -->
2072
+ media_type(Type),
2073
+ blanks,
2074
+ ( ";"
2075
+ -> blanks,
2076
+ parameters_and_quality(TypeParams, Quality, AcceptExts)
2077
+ ; { TypeParams = [],
2078
+ Quality = 1.0,
2079
+ AcceptExts = []
2080
+ }
2081
+ ),
2082
+ { SortQuality is float(-Quality),
2083
+ rank_specialised(Type, TypeParams, Spec)
2084
+ }.
2085
+
2086
+
2087
+ %! content_disposition(-Disposition)//
2088
+ %
2089
+ % Parse Content-Disposition value
2090
+
2091
+ content_disposition(disposition(Disposition, Options)) -->
2092
+ token(Disposition), blanks,
2093
+ value_parameters(Options).
2094
+
2095
+ %! parse_content_type(-Type)//
2096
+ %
2097
+ % Parse Content-Type value into a term media(Type/SubType,
2098
+ % Parameters).
2099
+
2100
+ parse_content_type(media(Type, Parameters)) -->
2101
+ media_type(Type), blanks,
2102
+ value_parameters(Parameters).
2103
+
2104
+
2105
+ %! rank_specialised(+Type, +TypeParam, -Key) is det.
2106
+ %
2107
+ % Although the specification linked above is unclear, it seems
2108
+ % that more specialised types must be preferred over less
2109
+ % specialized ones.
2110
+ %
2111
+ % @tbd Is there an official specification of this?
2112
+
2113
+ rank_specialised(Type/SubType, TypeParams, v(VT, VS, SortVP)) :-
2114
+ var_or_given(Type, VT),
2115
+ var_or_given(SubType, VS),
2116
+ length(TypeParams, VP),
2117
+ SortVP is -VP.
2118
+
2119
+ var_or_given(V, Val) :-
2120
+ ( var(V)
2121
+ -> Val = 0
2122
+ ; Val = -1
2123
+ ).
2124
+
2125
+ media_type(Type/SubType) -->
2126
+ type(Type), "/", type(SubType).
2127
+
2128
+ type(_) -->
2129
+ "*",
2130
+ !.
2131
+ type(Type) -->
2132
+ token(Type).
2133
+
2134
+ parameters_and_quality(Params, Quality, AcceptExts) -->
2135
+ token(Name),
2136
+ blanks, "=", blanks,
2137
+ ( { Name == q }
2138
+ -> float(Quality), blanks,
2139
+ value_parameters(AcceptExts),
2140
+ { Params = [] }
2141
+ ; { Params = [Name=Value|T] },
2142
+ parameter_value(Value),
2143
+ blanks,
2144
+ ( ";"
2145
+ -> blanks,
2146
+ parameters_and_quality(T, Quality, AcceptExts)
2147
+ ; { T = [],
2148
+ Quality = 1.0,
2149
+ AcceptExts = []
2150
+ }
2151
+ )
2152
+ ).
2153
+
2154
+ %! value_parameters(-Params:list) is det.
2155
+ %
2156
+ % Accept (";" <parameter>)*, returning a list of Name=Value, where
2157
+ % both Name and Value are atoms.
2158
+
2159
+ value_parameters([H|T]) -->
2160
+ ";",
2161
+ !,
2162
+ blanks, token(Name), blanks,
2163
+ ( "="
2164
+ -> blanks,
2165
+ ( token(Value)
2166
+ -> []
2167
+ ; quoted_string(Value)
2168
+ ),
2169
+ { H = (Name=Value) }
2170
+ ; { H = Name }
2171
+ ),
2172
+ blanks,
2173
+ value_parameters(T).
2174
+ value_parameters([]) -->
2175
+ [].
2176
+
2177
+ parameter_value(Value) --> token(Value), !.
2178
+ parameter_value(Value) --> quoted_string(Value).
2179
+
2180
+
2181
+ %! token(-Name)// is semidet.
2182
+ %
2183
+ % Process an HTTP header token from the input.
2184
+
2185
+ token(Name) -->
2186
+ token_char(C1),
2187
+ token_chars(Cs),
2188
+ { atom_codes(Name, [C1|Cs]) }.
2189
+
2190
+ token_chars([H|T]) -->
2191
+ token_char(H),
2192
+ !,
2193
+ token_chars(T).
2194
+ token_chars([]) --> [].
2195
+
2196
+ token_char(C) :-
2197
+ \+ ctl(C),
2198
+ \+ separator_code(C).
2199
+
2200
+ ctl(C) :- between(0,31,C), !.
2201
+ ctl(127).
2202
+
2203
+ separator_code(0'().
2204
+ separator_code(0')).
2205
+ separator_code(0'<).
2206
+ separator_code(0'>).
2207
+ separator_code(0'@).
2208
+ separator_code(0',).
2209
+ separator_code(0';).
2210
+ separator_code(0':).
2211
+ separator_code(0'\\).
2212
+ separator_code(0'").
2213
+ separator_code(0'/).
2214
+ separator_code(0'[).
2215
+ separator_code(0']).
2216
+ separator_code(0'?).
2217
+ separator_code(0'=).
2218
+ separator_code(0'{).
2219
+ separator_code(0'}).
2220
+ separator_code(0'\s).
2221
+ separator_code(0'\t).
2222
+
2223
+ term_expansion(token_char(x) --> [x], Clauses) :-
2224
+ findall((token_char(C)-->[C]),
2225
+ ( between(0, 255, C),
2226
+ token_char(C)
2227
+ ),
2228
+ Clauses).
2229
+
2230
+ token_char(x) --> [x].
2231
+
2232
+ %! quoted_string(-Text)// is semidet.
2233
+ %
2234
+ % True if input starts with a quoted string representing Text.
2235
+
2236
+ quoted_string(Text) -->
2237
+ "\"",
2238
+ quoted_text(Codes),
2239
+ { atom_codes(Text, Codes) }.
2240
+
2241
+ quoted_text([]) -->
2242
+ "\"",
2243
+ !.
2244
+ quoted_text([H|T]) -->
2245
+ "\\", !, [H],
2246
+ quoted_text(T).
2247
+ quoted_text([H|T]) -->
2248
+ [H],
2249
+ !,
2250
+ quoted_text(T).
2251
+
2252
+
2253
+ %! header_fields(+Fields, ?ContentLength)// is det.
2254
+ %
2255
+ % Process a sequence of [Name(Value), ...] attributes for the
2256
+ % header. A term content_length(Len) is special. If instantiated
2257
+ % it emits the header. If not it just unifies ContentLength with
2258
+ % the argument of the content_length(Len) term. This allows for
2259
+ % both sending and retrieving the content-length.
2260
+
2261
+ header_fields([], _) --> [].
2262
+ header_fields([content_length(CLen)|T], CLen) -->
2263
+ !,
2264
+ ( { var(CLen) }
2265
+ -> ""
2266
+ ; header_field(content_length, CLen)
2267
+ ),
2268
+ header_fields(T, CLen). % Continue or return first only?
2269
+ header_fields([status(_)|T], CLen) --> % handled by vstatus//3.
2270
+ !,
2271
+ header_fields(T, CLen).
2272
+ header_fields([H|T], CLen) -->
2273
+ { H =.. [Name, Value] },
2274
+ header_field(Name, Value),
2275
+ header_fields(T, CLen).
2276
+
2277
+
2278
+ %! field_name(?PrologName)
2279
+ %
2280
+ % Convert between prolog_name and HttpName. Field names are,
2281
+ % according to RFC 2616, considered tokens and covered by the
2282
+ % following definition:
2283
+ %
2284
+ % ==
2285
+ % token = 1*<any CHAR except CTLs or separators>
2286
+ % separators = "(" | ")" | "<" | ">" | "@"
2287
+ % | "," | ";" | ":" | "\" | <">
2288
+ % | "/" | "[" | "]" | "?" | "="
2289
+ % | "{" | "}" | SP | HT
2290
+ % ==
2291
+
2292
+ :- public
2293
+ field_name//1.
2294
+
2295
+ field_name(Name) -->
2296
+ { var(Name) },
2297
+ !,
2298
+ rd_field_chars(Chars),
2299
+ { atom_codes(Name, Chars) }.
2300
+ field_name(mime_version) -->
2301
+ !,
2302
+ "MIME-Version".
2303
+ field_name(www_authenticate) -->
2304
+ !,
2305
+ "WWW-Authenticate".
2306
+ field_name(Name) -->
2307
+ { atom_codes(Name, Chars) },
2308
+ wr_field_chars(Chars).
2309
+
2310
+ rd_field_chars_no_fold([C|T]) -->
2311
+ [C],
2312
+ { rd_field_char(C, _) },
2313
+ !,
2314
+ rd_field_chars_no_fold(T).
2315
+ rd_field_chars_no_fold([]) -->
2316
+ [].
2317
+
2318
+ rd_field_chars([C0|T]) -->
2319
+ [C],
2320
+ { rd_field_char(C, C0) },
2321
+ !,
2322
+ rd_field_chars(T).
2323
+ rd_field_chars([]) -->
2324
+ [].
2325
+
2326
+ %! separators(-CharCodes) is det.
2327
+ %
2328
+ % CharCodes is a list of separators according to RFC2616
2329
+
2330
+ separators("()<>@,;:\\\"/[]?={} \t").
2331
+
2332
+ term_expansion(rd_field_char('expand me',_), Clauses) :-
2333
+
2334
+ Clauses = [ rd_field_char(0'-, 0'_)
2335
+ | Cls
2336
+ ],
2337
+ separators(SepString),
2338
+ string_codes(SepString, Seps),
2339
+ findall(rd_field_char(In, Out),
2340
+ ( between(32, 127, In),
2341
+ \+ memberchk(In, Seps),
2342
+ In \== 0'-, % 0'
2343
+ code_type(Out, to_lower(In))),
2344
+ Cls).
2345
+
2346
+ rd_field_char('expand me', _). % avoid recursion
2347
+
2348
+ wr_field_chars([C|T]) -->
2349
+ !,
2350
+ { code_type(C, to_lower(U)) },
2351
+ [U],
2352
+ wr_field_chars2(T).
2353
+ wr_field_chars([]) -->
2354
+ [].
2355
+
2356
+ wr_field_chars2([]) --> [].
2357
+ wr_field_chars2([C|T]) --> % 0'
2358
+ ( { C == 0'_ }
2359
+ -> "-",
2360
+ wr_field_chars(T)
2361
+ ; [C],
2362
+ wr_field_chars2(T)
2363
+ ).
2364
+
2365
+ %! now//
2366
+ %
2367
+ % Current time using rfc_date//1.
2368
+
2369
+ now -->
2370
+ { get_time(Time)
2371
+ },
2372
+ rfc_date(Time).
2373
+
2374
+ %! rfc_date(+Time)// is det.
2375
+ %
2376
+ % Write time according to RFC1123 specification as required by the
2377
+ % RFC2616 HTTP protocol specs.
2378
+
2379
+ rfc_date(Time, String, Tail) :-
2380
+ stamp_date_time(Time, Date, 'UTC'),
2381
+ format_time(codes(String, Tail),
2382
+ '%a, %d %b %Y %T GMT',
2383
+ Date, posix).
2384
+
2385
+ %! http_timestamp(?Time:timestamp, ?Text:atom) is det.
2386
+ %
2387
+ % Convert between a SWI-Prolog time stamp and a string in HTTP format
2388
+ % (RFC1123). When parsing, it accepts RFC1123, RFC1036 and ASCTIME
2389
+ % formats. See parse_time/3.
2390
+ %
2391
+ % @error syntax_error(http_timestamp(Text)) if the string cannot
2392
+ % be parsed.
2393
+
2394
+ http_timestamp(Time, Text), nonvar(Text) =>
2395
+ ( parse_time(Text, _Format, Time0)
2396
+ -> ( var(Time)
2397
+ -> Time = Time0
2398
+ ; Time =:= Time0
2399
+ )
2400
+ ; syntax_error(http_timestamp(Text))
2401
+ ).
2402
+ http_timestamp(Time, Atom), number(Time) =>
2403
+ stamp_date_time(Time, Date, 'UTC'),
2404
+ format_time(atom(Atom),
2405
+ '%a, %d %b %Y %T GMT',
2406
+ Date, posix).
2407
+
2408
+
2409
+ /*******************************
2410
+ * REQUEST DCG *
2411
+ *******************************/
2412
+
2413
+ request(Fd, [method(Method),request_uri(ReqURI)|Header]) -->
2414
+ method(Method),
2415
+ blanks,
2416
+ nonblanks(Query),
2417
+ { atom_codes(ReqURI, Query),
2418
+ request_uri_parts(ReqURI, Header, Rest)
2419
+ },
2420
+ request_header(Fd, Rest),
2421
+ !.
2422
+ request(Fd, [unknown(What)|Header]) -->
2423
+ string(What),
2424
+ eos,
2425
+ !,
2426
+ { http_read_header(Fd, Header)
2427
+ -> true
2428
+ ; Header = []
2429
+ }.
2430
+
2431
+ method(get) --> "GET", !.
2432
+ method(put) --> "PUT", !.
2433
+ method(head) --> "HEAD", !.
2434
+ method(post) --> "POST", !.
2435
+ method(delete) --> "DELETE", !.
2436
+ method(patch) --> "PATCH", !.
2437
+ method(options) --> "OPTIONS", !.
2438
+ method(trace) --> "TRACE", !.
2439
+
2440
+ %! request_uri_parts(+RequestURI, -Parts, ?Tail) is det.
2441
+ %
2442
+ % Process the request-uri, producing the following parts:
2443
+ %
2444
+ % * path(-Path)
2445
+ % Decode path information (always present)
2446
+ % * search(-QueryParams)
2447
+ % Present if there is a ?name=value&... part of the request uri.
2448
+ % QueryParams is a Name=Value list.
2449
+ % * fragment(-Fragment)
2450
+ % Present if there is a #Fragment.
2451
+
2452
+ request_uri_parts(ReqURI, [path(Path)|Parts], Rest) :-
2453
+ uri_components(ReqURI, Components),
2454
+ uri_data(path, Components, PathText),
2455
+ uri_encoded(path, Path, PathText),
2456
+ phrase(uri_parts(Components), Parts, Rest).
2457
+
2458
+ uri_parts(Components) -->
2459
+ uri_search(Components),
2460
+ uri_fragment(Components).
2461
+
2462
+ uri_search(Components) -->
2463
+ { uri_data(search, Components, Search),
2464
+ nonvar(Search),
2465
+ catch(uri_query_components(Search, Query),
2466
+ error(syntax_error(_),_),
2467
+ fail)
2468
+ },
2469
+ !,
2470
+ [ search(Query) ].
2471
+ uri_search(_) --> [].
2472
+
2473
+ uri_fragment(Components) -->
2474
+ { uri_data(fragment, Components, String),
2475
+ nonvar(String),
2476
+ !,
2477
+ uri_encoded(fragment, Fragment, String)
2478
+ },
2479
+ [ fragment(Fragment) ].
2480
+ uri_fragment(_) --> [].
2481
+
2482
+ %! request_header(+In:stream, -Header:list) is det.
2483
+ %
2484
+ % Read the remainder (after the request-uri) of the HTTP header
2485
+ % and return it as a Name(Value) list.
2486
+
2487
+ request_header(_, []) --> % Old-style non-version header
2488
+ blanks,
2489
+ eos,
2490
+ !.
2491
+ request_header(Fd, [http_version(Version)|Header]) -->
2492
+ http_version(Version),
2493
+ blanks,
2494
+ eos,
2495
+ !,
2496
+ { Version = 1-_
2497
+ -> http_read_header(Fd, Header)
2498
+ ; Header = []
2499
+ }.
2500
+
2501
+ http_version(Version) -->
2502
+ blanks,
2503
+ "HTTP/",
2504
+ http_version_number(Version).
2505
+
2506
+ http_version_number(Major-Minor) -->
2507
+ integer(Major),
2508
+ ".",
2509
+ integer(Minor).
2510
+
2511
+
2512
+ /*******************************
2513
+ * COOKIES *
2514
+ *******************************/
2515
+
2516
+ %! cookies(-List)// is semidet.
2517
+ %
2518
+ % Translate a cookie description into a list Name=Value.
2519
+
2520
+ cookies([Name=Value|T]) -->
2521
+ blanks,
2522
+ cookie(Name, Value),
2523
+ !,
2524
+ blanks,
2525
+ ( ";"
2526
+ -> cookies(T)
2527
+ ; { T = [] }
2528
+ ).
2529
+ cookies(List) -->
2530
+ string(Skipped),
2531
+ ";",
2532
+ !,
2533
+ { print_message(warning, http(skipped_cookie(Skipped))) },
2534
+ cookies(List).
2535
+ cookies([]) -->
2536
+ blanks.
2537
+
2538
+ cookie(Name, Value) -->
2539
+ cookie_name(Name),
2540
+ blanks, "=", blanks,
2541
+ cookie_value(Value).
2542
+
2543
+ cookie_name(Name) -->
2544
+ { var(Name) },
2545
+ !,
2546
+ rd_field_chars_no_fold(Chars),
2547
+ { atom_codes(Name, Chars) }.
2548
+
2549
+ cookie_value(Value) -->
2550
+ quoted_string(Value),
2551
+ !.
2552
+ cookie_value(Value) -->
2553
+ chars_to_semicolon_or_blank(Chars),
2554
+ { atom_codes(Value, Chars)
2555
+ }.
2556
+
2557
+ chars_to_semicolon_or_blank([]), ";" -->
2558
+ ";",
2559
+ !.
2560
+ chars_to_semicolon_or_blank([]) -->
2561
+ " ",
2562
+ blanks,
2563
+ eos,
2564
+ !.
2565
+ chars_to_semicolon_or_blank([H|T]) -->
2566
+ [H],
2567
+ !,
2568
+ chars_to_semicolon_or_blank(T).
2569
+ chars_to_semicolon_or_blank([]) -->
2570
+ [].
2571
+
2572
+ set_cookie(set_cookie(Name, Value, Options)) -->
2573
+ ws,
2574
+ cookie(Name, Value),
2575
+ cookie_options(Options).
2576
+
2577
+ cookie_options([H|T]) -->
2578
+ ws,
2579
+ ";",
2580
+ ws,
2581
+ cookie_option(H),
2582
+ !,
2583
+ cookie_options(T).
2584
+ cookie_options([]) -->
2585
+ ws.
2586
+
2587
+ ws --> " ", !, ws.
2588
+ ws --> [].
2589
+
2590
+
2591
+ %! cookie_option(-Option)// is semidet.
2592
+ %
2593
+ % True if input represents a valid Cookie option. Officially, all
2594
+ % cookie options use the syntax <name>=<value>, except for
2595
+ % =Secure= and =HttpOnly=.
2596
+ %
2597
+ % @param Option Term of the form Name=Value
2598
+ % @bug Incorrectly accepts options without = for M$ compatibility.
2599
+
2600
+ cookie_option(Name=Value) -->
2601
+ rd_field_chars(NameChars), ws,
2602
+ { atom_codes(Name, NameChars) },
2603
+ ( "="
2604
+ -> ws,
2605
+ chars_to_semicolon(ValueChars),
2606
+ { atom_codes(Value, ValueChars)
2607
+ }
2608
+ ; { Value = true }
2609
+ ).
2610
+
2611
+ chars_to_semicolon([H|T]) -->
2612
+ [H],
2613
+ { H \== 32, H \== 0'; },
2614
+ !,
2615
+ chars_to_semicolon(T).
2616
+ chars_to_semicolon([]), ";" -->
2617
+ ws, ";",
2618
+ !.
2619
+ chars_to_semicolon([H|T]) -->
2620
+ [H],
2621
+ chars_to_semicolon(T).
2622
+ chars_to_semicolon([]) -->
2623
+ [].
2624
+
2625
+ %! range(-Range)// is semidet.
2626
+ %
2627
+ % Process the range header value. Range is currently defined as:
2628
+ %
2629
+ % * bytes(From, To)
2630
+ % Where From is an integer and To is either an integer or
2631
+ % the atom =end=.
2632
+
2633
+ range(bytes(From, To)) -->
2634
+ "bytes", whites, "=", whites, integer(From), "-",
2635
+ ( integer(To)
2636
+ -> ""
2637
+ ; { To = end }
2638
+ ).
2639
+
2640
+
2641
+ /*******************************
2642
+ * REPLY DCG *
2643
+ *******************************/
2644
+
2645
+ %! reply(+In, -Reply:list)// is semidet.
2646
+ %
2647
+ % Process the first line of an HTTP reply. After that, read the
2648
+ % remainder of the header and parse it. After successful
2649
+ % completion, Reply contains the following fields, followed by the
2650
+ % fields produced by http_read_header/2.
2651
+ %
2652
+ % * http_version(Major-Minor)
2653
+ % * status(Code, Status, Comment)
2654
+ % `Code` is an integer between 100 and 599.
2655
+ % `Status` is a Prolog internal name.
2656
+ % `Comment` is the comment following the code
2657
+ % as it appears in the reply's HTTP status line.
2658
+ % @see status_number//2.
2659
+
2660
+ reply(Fd, [http_version(HttpVersion), status(Code, Status, Comment)|Header]) -->
2661
+ http_version(HttpVersion),
2662
+ blanks,
2663
+ ( status_number(Status, Code)
2664
+ -> []
2665
+ ; integer(Status)
2666
+ ),
2667
+ blanks,
2668
+ string(CommentCodes),
2669
+ blanks_to_nl,
2670
+ !,
2671
+ blanks,
2672
+ { atom_codes(Comment, CommentCodes),
2673
+ http_read_header(Fd, Header)
2674
+ }.
2675
+
2676
+
2677
+ /*******************************
2678
+ * READ HEADER *
2679
+ *******************************/
2680
+
2681
+ %! http_read_header(+Fd, -Header) is det.
2682
+ %
2683
+ % Read Name: Value lines from FD until an empty line is encountered.
2684
+ % Field-name are converted to Prolog conventions (all lower, _ instead
2685
+ % of -): Content-Type: text/html --> content_type(text/html)
2686
+
2687
+ http_read_header(Fd, Header) :-
2688
+ read_header_data(Fd, Text),
2689
+ http_parse_header(Text, Header).
2690
+
2691
+ read_header_data(Fd, Header) :-
2692
+ read_line_to_codes(Fd, Header, Tail),
2693
+ read_header_data(Header, Fd, Tail),
2694
+ debug(http(header), 'Header = ~n~s~n', [Header]).
2695
+
2696
+ read_header_data([0'\r,0'\n], _, _) :- !.
2697
+ read_header_data([0'\n], _, _) :- !.
2698
+ read_header_data([], _, _) :- !.
2699
+ read_header_data(_, Fd, Tail) :-
2700
+ read_line_to_codes(Fd, Tail, NewTail),
2701
+ read_header_data(Tail, Fd, NewTail).
2702
+
2703
+ %! http_parse_header(+Text:codes, -Header:list) is det.
2704
+ %
2705
+ % Header is a list of Name(Value)-terms representing the structure
2706
+ % of the HTTP header in Text.
2707
+ %
2708
+ % @error domain_error(http_request_line, Line)
2709
+
2710
+ http_parse_header(Text, Header) :-
2711
+ phrase(header(Header), Text),
2712
+ debug(http(header), 'Field: ~p', [Header]).
2713
+
2714
+ header(List) -->
2715
+ header_field(Name, Value),
2716
+ !,
2717
+ { mkfield(Name, Value, List, Tail)
2718
+ },
2719
+ blanks,
2720
+ header(Tail).
2721
+ header([]) -->
2722
+ blanks,
2723
+ eos,
2724
+ !.
2725
+ header(_) -->
2726
+ string(S), blanks_to_nl,
2727
+ !,
2728
+ { string_codes(Line, S),
2729
+ syntax_error(http_parameter(Line))
2730
+ }.
2731
+
2732
+ %! address//
2733
+ %
2734
+ % Emit the HTML for the server address on behalf of error and
2735
+ % status messages (non-200 replies). Default is
2736
+ %
2737
+ % ==
2738
+ % SWI-Prolog httpd at <hostname>
2739
+ % ==
2740
+ %
2741
+ % The address can be modified by providing a definition for the
2742
+ % multifile predicate http:http_address//0.
2743
+
2744
+ :- multifile
2745
+ http:http_address//0.
2746
+
2747
+ address -->
2748
+ http:http_address,
2749
+ !.
2750
+ address -->
2751
+ { gethostname(Host) },
2752
+ html(address([ a(href('http://www.swi-prolog.org'), 'SWI-Prolog'),
2753
+ ' httpd at ', Host
2754
+ ])).
2755
+
2756
+ mkfield(host, Host:Port, [host(Host),port(Port)|Tail], Tail) :- !.
2757
+ mkfield(Name, Value, [Att|Tail], Tail) :-
2758
+ Att =.. [Name, Value].
2759
+
2760
+ %! http:http_address// is det.
2761
+ %
2762
+ % HTML-rule that emits the location of the HTTP server. This hook
2763
+ % is called from address//0 to customise the server address. The
2764
+ % server address is emitted on non-200-ok replies.
2765
+
2766
+ %! http:status_page(+Status, +Context, -HTMLTokens) is semidet.
2767
+ %
2768
+ % Hook called by http_status_reply/4 and http_status_reply/5 that
2769
+ % allows for emitting custom error pages for the following HTTP
2770
+ % page types:
2771
+ %
2772
+ % - 201 - created(Location)
2773
+ % - 301 - moved(To)
2774
+ % - 302 - moved_temporary(To)
2775
+ % - 303 - see_other(To)
2776
+ % - 400 - bad_request(ErrorTerm)
2777
+ % - 401 - authorise(AuthMethod)
2778
+ % - 403 - forbidden(URL)
2779
+ % - 404 - not_found(URL)
2780
+ % - 405 - method_not_allowed(Method,URL)
2781
+ % - 406 - not_acceptable(Why)
2782
+ % - 500 - server_error(ErrorTerm)
2783
+ % - 503 - unavailable(Why)
2784
+ %
2785
+ % The hook is tried twice, first using the status term, e.g.,
2786
+ % not_found(URL) and than with the code, e.g. `404`. The second
2787
+ % call is deprecated and only exists for compatibility.
2788
+ %
2789
+ % @arg Context is the 4th argument of http_status_reply/5, which
2790
+ % is invoked after raising an exception of the format
2791
+ % http_reply(Status, HeaderExtra, Context). The default
2792
+ % context is `[]` (the empty list).
2793
+ % @arg HTMLTokens is a list of tokens as produced by html//1.
2794
+ % It is passed to print_html/2.
2795
+
2796
+
2797
+ /*******************************
2798
+ * MESSAGES *
2799
+ *******************************/
2800
+
2801
+ :- multifile
2802
+ prolog:message//1,
2803
+ prolog:error_message//1.
2804
+
2805
+ prolog:error_message(http_write_short(Data, Sent)) -->
2806
+ data(Data),
2807
+ [ ': remote hangup after ~D bytes'-[Sent] ].
2808
+ prolog:error_message(syntax_error(http_request(Request))) -->
2809
+ [ 'Illegal HTTP request: ~s'-[Request] ].
2810
+ prolog:error_message(syntax_error(http_parameter(Line))) -->
2811
+ [ 'Illegal HTTP parameter: ~s'-[Line] ].
2812
+
2813
+ prolog:message(http(skipped_cookie(S))) -->
2814
+ [ 'Skipped illegal cookie: ~s'-[S] ].
2815
+
2816
+ data(bytes(MimeType, _Bytes)) -->
2817
+ !,
2818
+ [ 'bytes(~p, ...)'-[MimeType] ].
2819
+ data(Data) -->
2820
+ [ '~p'-[Data] ].