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,1821 @@
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-2024, University of Amsterdam
7
+ VU University Amsterdam
8
+ CWI, Amsterdam
9
+ SWI-Prolog Solutions b.v.
10
+ All rights reserved.
11
+
12
+ Redistribution and use in source and binary forms, with or without
13
+ modification, are permitted provided that the following conditions
14
+ are met:
15
+
16
+ 1. Redistributions of source code must retain the above copyright
17
+ notice, this list of conditions and the following disclaimer.
18
+
19
+ 2. Redistributions in binary form must reproduce the above copyright
20
+ notice, this list of conditions and the following disclaimer in
21
+ the documentation and/or other materials provided with the
22
+ distribution.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
+ POSSIBILITY OF SUCH DAMAGE.
36
+ */
37
+
38
+ :- module(http_open,
39
+ [ http_open/3, % +URL, -Stream, +Options
40
+ http_set_authorization/2, % +URL, +Authorization
41
+ http_close_keep_alive/1 % +Address
42
+ ]).
43
+ :- autoload(library(aggregate),[aggregate_all/3]).
44
+ :- autoload(library(apply),[foldl/4,include/3]).
45
+ :- autoload(library(base64),[base64/3]).
46
+ :- use_module(library(debug),[debug/3,debugging/1]).
47
+ :- autoload(library(error),
48
+ [ domain_error/2, must_be/2, existence_error/2, instantiation_error/1
49
+ ]).
50
+ :- autoload(library(lists),[last/2,member/2]).
51
+ :- autoload(library(option),
52
+ [ meta_options/3, option/2, select_option/4, merge_options/3,
53
+ option/3, select_option/3
54
+ ]).
55
+ :- autoload(library(readutil),[read_line_to_codes/2]).
56
+ :- autoload(library(uri),
57
+ [ uri_resolve/3, uri_components/2, uri_data/3,
58
+ uri_authority_components/2, uri_authority_data/3,
59
+ uri_encoded/3, uri_query_components/2, uri_is_global/1
60
+ ]).
61
+ :- autoload(library(http/http_header),
62
+ [ http_parse_header/2, http_post_data/3 ]).
63
+ :- autoload(library(http/http_stream),[stream_range_open/3]).
64
+ :- if(exists_source(library(ssl))).
65
+ :- autoload(library(ssl), [ssl_upgrade_legacy_options/2]).
66
+ :- endif.
67
+ :- use_module(library(socket)).
68
+ :- use_module(library(settings)).
69
+
70
+ :- setting(http:max_keep_alive_idle, number, 2,
71
+ "Time to keep idle keep alive connections around").
72
+ :- setting(http:max_keep_alive_connections, integer, 10,
73
+ "Maximum number of client keep alive connections").
74
+ :- setting(http:max_keep_alive_host_connections, integer, 2,
75
+ "Maximum number of client keep alive to a single host").
76
+
77
+ /** <module> HTTP client library
78
+
79
+ This library defines http_open/3, which opens an URL as a Prolog stream.
80
+ The functionality of the library can be extended by loading two
81
+ additional modules that act as plugins:
82
+
83
+ * library(http/http_ssl_plugin)
84
+ Loading this library causes http_open/3 to handle HTTPS connections.
85
+ Relevant options for SSL certificate handling are handed to
86
+ ssl_context/3. This plugin is loaded automatically if the scheme
87
+ `https` is requested using a default SSL context. See the plugin for
88
+ additional information regarding security.
89
+
90
+ * library(zlib)
91
+ Loading this library supports the `gzip` transfer encoding. This
92
+ plugin is lazily loaded if a connection is opened that claims this
93
+ transfer encoding.
94
+
95
+ * library(http/http_cookie)
96
+ Loading this library adds tracking cookies to http_open/3. Returned
97
+ cookies are collected in the Prolog database and supplied for
98
+ subsequent requests.
99
+
100
+ * library(http/http_stream)
101
+ This library adds support for _chunked_ encoding. It is lazily
102
+ loaded if the server sends a ``Transfer-encoding: chunked`` header.
103
+
104
+
105
+ Here is a simple example to fetch a web-page:
106
+
107
+ ```
108
+ ?- http_open('http://www.google.com/search?q=prolog', In, []),
109
+ copy_stream_data(In, user_output),
110
+ close(In).
111
+ <!doctype html><head><title>prolog - Google Search</title><script>
112
+ ...
113
+ ```
114
+
115
+ The example below fetches the modification time of a web-page. Note that
116
+ =|Modified|= is =|''|= (the empty atom) if the web-server does not provide a
117
+ time-stamp for the resource. See also parse_time/2.
118
+
119
+ ```
120
+ modified(URL, Stamp) :-
121
+ http_open(URL, In,
122
+ [ method(head),
123
+ header(last_modified, Modified)
124
+ ]),
125
+ close(In),
126
+ Modified \== '',
127
+ parse_time(Modified, Stamp).
128
+ ```
129
+
130
+ Then next example uses Google search. It exploits library(uri) to manage
131
+ URIs, library(sgml) to load an HTML document and library(xpath) to
132
+ navigate the parsed HTML. Note that you may need to adjust the XPath
133
+ queries if the data returned by Google changes (this example indeed
134
+ no longer works and currently fails at the first xpath/3 call)
135
+
136
+ ```
137
+ :- use_module(library(http/http_open)).
138
+ :- use_module(library(xpath)).
139
+ :- use_module(library(sgml)).
140
+ :- use_module(library(uri)).
141
+
142
+ google(For, Title, HREF) :-
143
+ uri_encoded(query_value, For, Encoded),
144
+ atom_concat('http://www.google.com/search?q=', Encoded, URL),
145
+ http_open(URL, In, []),
146
+ call_cleanup(
147
+ load_html(In, DOM, []),
148
+ close(In)),
149
+ xpath(DOM, //h3(@class=r), Result),
150
+ xpath(Result, //a(@href=HREF0, text), Title),
151
+ uri_components(HREF0, Components),
152
+ uri_data(search, Components, Query),
153
+ uri_query_components(Query, Parts),
154
+ memberchk(q=HREF, Parts).
155
+ ```
156
+
157
+ An example query is below:
158
+
159
+ ```
160
+ ?- google(prolog, Title, HREF).
161
+ Title = 'SWI-Prolog',
162
+ HREF = 'http://www.swi-prolog.org/' ;
163
+ Title = 'Prolog - Wikipedia',
164
+ HREF = 'https://nl.wikipedia.org/wiki/Prolog' ;
165
+ Title = 'Prolog - Wikipedia, the free encyclopedia',
166
+ HREF = 'https://en.wikipedia.org/wiki/Prolog' ;
167
+ Title = 'Pro-Log is logistiek dienstverlener m.b.t. vervoer over water.',
168
+ HREF = 'http://www.pro-log.nl/' ;
169
+ Title = 'Learn Prolog Now!',
170
+ HREF = 'http://www.learnprolognow.org/' ;
171
+ Title = 'Free Online Version - Learn Prolog
172
+ ...
173
+ ```
174
+
175
+ @see load_html/3 and xpath/3 can be used to parse and navigate HTML
176
+ documents.
177
+ @see http_get/3 and http_post/4 provide an alternative interface that
178
+ convert the reply depending on the =|Content-Type|= header.
179
+ */
180
+
181
+ :- multifile
182
+ http:encoding_filter/3, % +Encoding, +In0, -In
183
+ http:current_transfer_encoding/1, % ?Encoding
184
+ http:disable_encoding_filter/1, % +ContentType
185
+ http:http_protocol_hook/5, % +Protocol, +Parts, +StreamPair,
186
+ % -NewStreamPair, +Options
187
+ http:open_options/2, % +Parts, -Options
188
+ http:write_cookies/3, % +Out, +Parts, +Options
189
+ http:update_cookies/3, % +CookieLine, +Parts, +Options
190
+ http:authenticate_client/2, % +URL, +Action
191
+ http:http_connection_over_proxy/6.
192
+
193
+ :- meta_predicate
194
+ http_open(+,-,:).
195
+
196
+ :- predicate_options(http_open/3, 3,
197
+ [ authorization(compound),
198
+ final_url(-atom),
199
+ header(+atom, -atom),
200
+ headers(-list),
201
+ raw_headers(-list(string)),
202
+ connection(+atom),
203
+ method(oneof([delete,get,put,purge,head,
204
+ post,patch,options])),
205
+ size(-integer),
206
+ status_code(-integer),
207
+ output(-stream),
208
+ timeout(number),
209
+ unix_socket(+atom),
210
+ proxy(atom, integer),
211
+ proxy_authorization(compound),
212
+ bypass_proxy(boolean),
213
+ request_header(any),
214
+ user_agent(atom),
215
+ version(-compound),
216
+ % The option below applies if library(http/http_header) is loaded
217
+ post(any),
218
+ % The options below apply if library(http/http_ssl_plugin)) is loaded
219
+ pem_password_hook(callable),
220
+ cacert_file(atom),
221
+ cert_verify_hook(callable)
222
+ ]).
223
+
224
+ %! user_agent(-Agent) is det.
225
+ %
226
+ % Default value for =|User-Agent|=, can be overruled using the
227
+ % option user_agent(Agent) of http_open/3.
228
+
229
+ user_agent('SWI-Prolog').
230
+
231
+ %! http_open(+URL, -Stream, +Options) is det.
232
+ %
233
+ % Open the data at the HTTP server as a Prolog stream. URL is
234
+ % either an atom specifying a URL or a list representing a
235
+ % broken-down URL as specified below. After this predicate
236
+ % succeeds the data can be read from Stream. After completion this
237
+ % stream must be closed using the built-in Prolog predicate
238
+ % close/1. Options provides additional options:
239
+ %
240
+ % * authenticate(+Boolean)
241
+ % If `false` (default `true`), do _not_ try to automatically
242
+ % authenticate the client if a 401 (Unauthorized) status code
243
+ % is received.
244
+ %
245
+ % * authorization(+Term)
246
+ % Send authorization. See also http_set_authorization/2. Supported
247
+ % schemes:
248
+ %
249
+ % - basic(+User, +Password)
250
+ % HTTP Basic authentication.
251
+ % - bearer(+Token)
252
+ % HTTP Bearer authentication.
253
+ % - digest(+User, +Password)
254
+ % HTTP Digest authentication. This option is only provided
255
+ % if the plugin library(http/http_digest) is also loaded.
256
+ %
257
+ % * unix_socket(+Path)
258
+ % Connect to the given Unix domain socket. In this scenario
259
+ % the host name and port or ignored. If the server replies
260
+ % with a _redirect_ message and the host differs from the
261
+ % original host as normal TCP connection is used to handle
262
+ % the redirect. This option is inspired by curl(1)'s option
263
+ % `--unix-socket`.
264
+ %
265
+ % * connection(+Connection)
266
+ % Specify the =Connection= header. Default is =close=. The
267
+ % alternative is =|Keep-alive|=. This maintains a pool of
268
+ % available connections as determined by keep_connection/1.
269
+ % The library(http/websockets) uses =|Keep-alive, Upgrade|=.
270
+ % Keep-alive connections can be closed explicitly using
271
+ % http_close_keep_alive/1. Keep-alive connections may
272
+ % significantly improve repetitive requests on the same server,
273
+ % especially if the IP route is long, HTTPS is used or the
274
+ % connection uses a proxy.
275
+ %
276
+ % * final_url(-FinalURL)
277
+ % Unify FinalURL with the final destination. This differs from
278
+ % the original URL if the returned head of the original
279
+ % indicates an HTTP redirect (codes 301, 302 or 303). Without a
280
+ % redirect, FinalURL is the same as URL if URL is an atom, or a
281
+ % URL constructed from the parts.
282
+ %
283
+ % * header(Name, -AtomValue)
284
+ % If provided, AtomValue is unified with the value of the
285
+ % indicated field in the reply header. Name is matched
286
+ % case-insensitive and the underscore (_) matches the hyphen
287
+ % (-). Multiple of these options may be provided to extract
288
+ % multiple header fields. If the header is not available
289
+ % AtomValue is unified to the empty atom ('').
290
+ %
291
+ % * headers(-List)
292
+ % If provided, List is unified with a list of Name(Value) pairs
293
+ % corresponding to fields in the reply header. Name and Value
294
+ % follow the same conventions used by the header(Name,Value)
295
+ % option. A pseudo header status_code(Code) is added to provide
296
+ % the HTTP status as an integer. See also raw_headers(-List)
297
+ % which provides the entire HTTP reply header in unparsed
298
+ % representation.
299
+ %
300
+ % * method(+Method)
301
+ % One of =get= (default), =head=, =delete=, =post=, =put= or
302
+ % =patch=.
303
+ % The =head= message can be
304
+ % used in combination with the header(Name, Value) option to
305
+ % access information on the resource without actually fetching
306
+ % the resource itself. The returned stream must be closed
307
+ % immediately.
308
+ %
309
+ % If post(Data) is provided, the default is =post=.
310
+ %
311
+ % * size(-Size)
312
+ % Size is unified with the integer value of =|Content-Length|=
313
+ % in the reply header.
314
+ %
315
+ % * version(-Version)
316
+ % Version is a _pair_ `Major-Minor`, where `Major` and `Minor`
317
+ % are integers representing the HTTP version in the reply header.
318
+ %
319
+ % * range(+Range)
320
+ % Ask for partial content. Range is a term _|Unit(From,To)|_,
321
+ % where `From` is an integer and `To` is either an integer or
322
+ % the atom `end`. HTTP 1.1 only supports Unit = `bytes`. E.g.,
323
+ % to ask for bytes 1000-1999, use the option
324
+ % range(bytes(1000,1999))
325
+ %
326
+ % * raw_encoding(+Encoding)
327
+ % Do not install a decoding filter for Encoding. For example,
328
+ % using raw_encoding('applocation/gzip') the system will not
329
+ % decompress the stream if it is compressed using `gzip`.
330
+ %
331
+ % * raw_headers(-Lines)
332
+ % Unify Lines with a list of strings that represents the complete
333
+ % reply header returned by the server. See also headers(-List).
334
+ %
335
+ % * redirect(+Boolean)
336
+ % If `false` (default `true`), do _not_ automatically redirect
337
+ % if a 3XX code is received. Must be combined with
338
+ % status_code(Code) and one of the header options to read the
339
+ % redirect reply. In particular, without status_code(Code) a
340
+ % redirect is mapped to an exception.
341
+ %
342
+ % * status_code(-Code)
343
+ % If this option is present and Code unifies with the HTTP
344
+ % status code, do *not* translate errors (4xx, 5xx) into an
345
+ % exception. Instead, http_open/3 behaves as if 2xx (success) is
346
+ % returned, providing the application to read the error document
347
+ % from the returned stream.
348
+ %
349
+ % * output(-Out)
350
+ % Unify the output stream with Out and do not close it. This can
351
+ % be used to upgrade a connection.
352
+ %
353
+ % * timeout(+Timeout)
354
+ % If provided, set a timeout on the stream using set_stream/2.
355
+ % With this option if no new data arrives within Timeout seconds
356
+ % the stream raises an exception. Default is to wait forever
357
+ % (=infinite=).
358
+ %
359
+ % * post(+Data)
360
+ % Issue a =POST= request on the HTTP server. Data is
361
+ % handed to http_post_data/3.
362
+ %
363
+ % * proxy(+Host:Port)
364
+ % Use an HTTP proxy to connect to the outside world. See also
365
+ % socket:proxy_for_url/3. This option overrules the proxy
366
+ % specification defined by socket:proxy_for_url/3.
367
+ %
368
+ % * proxy(+Host, +Port)
369
+ % Synonym for proxy(+Host:Port). Deprecated.
370
+ %
371
+ % * proxy_authorization(+Authorization)
372
+ % Send authorization to the proxy. Otherwise the same as the
373
+ % =authorization= option.
374
+ %
375
+ % * bypass_proxy(+Boolean)
376
+ % If =true=, bypass proxy hooks. Default is =false=.
377
+ %
378
+ % * request_header(Name = Value)
379
+ % Additional name-value parts are added in the order of
380
+ % appearance to the HTTP request header. No interpretation is
381
+ % done.
382
+ %
383
+ % * max_redirect(+Max)
384
+ % Sets the maximum length of a redirection chain. This is needed
385
+ % for some IRIs that redirect indefinitely to other IRIs without
386
+ % looping (e.g., redirecting to IRIs with a random element in them).
387
+ % Max must be either a non-negative integer or the atom `infinite`.
388
+ % The default value is `10`.
389
+ %
390
+ % * user_agent(+Agent)
391
+ % Defines the value of the =|User-Agent|= field of the HTTP
392
+ % header. Default is =SWI-Prolog=.
393
+ %
394
+ % The hook http:open_options/2 can be used to provide default
395
+ % options based on the broken-down URL. The option
396
+ % status_code(-Code) is particularly useful to query *REST*
397
+ % interfaces that commonly return status codes other than `200`
398
+ % that need to be be processed by the client code.
399
+ %
400
+ % @param URL is either an atom or string (url) or a list of _parts_.
401
+ %
402
+ % When provided, this list may contain the fields
403
+ % =scheme=, =user=, =password=, =host=, =port=, =path=
404
+ % and either =query_string= (whose argument is an atom)
405
+ % or =search= (whose argument is a list of
406
+ % =|Name(Value)|= or =|Name=Value|= compound terms).
407
+ % Only =host= is mandatory. The example below opens the
408
+ % URL =|http://www.example.com/my/path?q=Hello%20World&lang=en|=.
409
+ % Note that values must *not* be quoted because the
410
+ % library inserts the required quotes.
411
+ %
412
+ % ```
413
+ % http_open([ host('www.example.com'),
414
+ % path('/my/path'),
415
+ % search([ q='Hello world',
416
+ % lang=en
417
+ % ])
418
+ % ])
419
+ % ```
420
+ %
421
+ % @throws error(existence_error(url, Id),Context) is raised if the
422
+ % HTTP result code is not in the range 200..299. Context has the
423
+ % shape context(Message, status(Code, TextCode)), where `Code` is the
424
+ % numeric HTTP code and `TextCode` is the textual description thereof
425
+ % provided by the server. `Message` may provide additional details or
426
+ % may be unbound.
427
+ %
428
+ % @see ssl_context/3 for SSL related options if
429
+ % library(http/http_ssl_plugin) is loaded.
430
+
431
+ :- multifile
432
+ socket:proxy_for_url/3. % +URL, +Host, -ProxyList
433
+
434
+ http_open(URL, Stream, QOptions) :-
435
+ meta_options(is_meta, QOptions, Options0),
436
+ ( atomic(URL)
437
+ -> parse_url_ex(URL, Parts)
438
+ ; Parts = URL
439
+ ),
440
+ autoload_https(Parts),
441
+ upgrade_ssl_options(Parts, Options0, Options),
442
+ add_authorization(Parts, Options, Options1),
443
+ findall(HostOptions, hooked_options(Parts, HostOptions), AllHostOptions),
444
+ foldl(merge_options_rev, AllHostOptions, Options1, Options2),
445
+ ( option(bypass_proxy(true), Options)
446
+ -> try_http_proxy(direct, Parts, Stream, Options2)
447
+ ; term_variables(Options2, Vars2),
448
+ findall(Result-Vars2,
449
+ try_a_proxy(Parts, Result, Options2),
450
+ ResultList),
451
+ last(ResultList, Status-Vars2)
452
+ -> ( Status = true(_Proxy, Stream)
453
+ -> true
454
+ ; throw(error(proxy_error(tried(ResultList)), _))
455
+ )
456
+ ; try_http_proxy(direct, Parts, Stream, Options2)
457
+ ).
458
+
459
+ try_a_proxy(Parts, Result, Options) :-
460
+ parts_uri(Parts, AtomicURL),
461
+ option(host(Host), Parts),
462
+ ( option(unix_socket(Path), Options)
463
+ -> Proxy = unix_socket(Path)
464
+ ; ( option(proxy(ProxyHost:ProxyPort), Options)
465
+ ; is_list(Options),
466
+ memberchk(proxy(ProxyHost,ProxyPort), Options)
467
+ )
468
+ -> Proxy = proxy(ProxyHost, ProxyPort)
469
+ ; socket:proxy_for_url(AtomicURL, Host, Proxy)
470
+ ),
471
+ debug(http(proxy),
472
+ 'http_open: Connecting via ~w to ~w', [Proxy, AtomicURL]),
473
+ ( catch(try_http_proxy(Proxy, Parts, Stream, Options), E, true)
474
+ -> ( var(E)
475
+ -> !, Result = true(Proxy, Stream)
476
+ ; Result = error(Proxy, E)
477
+ )
478
+ ; Result = false(Proxy)
479
+ ),
480
+ debug(http(proxy), 'http_open: ~w: ~p', [Proxy, Result]).
481
+
482
+ try_http_proxy(Method, Parts, Stream, Options0) :-
483
+ option(host(Host), Parts),
484
+ proxy_request_uri(Method, Parts, RequestURI),
485
+ select_option(visited(Visited0), Options0, OptionsV, []),
486
+ Options = [visited([Parts|Visited0])|OptionsV],
487
+ parts_scheme(Parts, Scheme),
488
+ default_port(Scheme, DefPort),
489
+ url_part(port(Port), Parts, DefPort),
490
+ host_and_port(Host, DefPort, Port, HostPort),
491
+ ( option(connection(Connection), Options0),
492
+ keep_alive(Connection),
493
+ get_from_pool(Host:Port, StreamPair),
494
+ debug(http(keep_alive), 'Trying Keep-alive to ~p using ~p',
495
+ [ Host:Port, StreamPair ]),
496
+ catch(send_rec_header(StreamPair, Stream, HostPort,
497
+ RequestURI, Parts, Options),
498
+ Error,
499
+ keep_alive_error(Error, StreamPair))
500
+ -> true
501
+ ; http:http_connection_over_proxy(Method, Parts, Host:Port,
502
+ SocketStreamPair, Options, Options1),
503
+ ( catch(http:http_protocol_hook(Scheme, Parts,
504
+ SocketStreamPair,
505
+ StreamPair, Options),
506
+ Error,
507
+ ( close(SocketStreamPair, [force(true)]),
508
+ throw(Error)))
509
+ -> true
510
+ ; StreamPair = SocketStreamPair
511
+ ),
512
+ send_rec_header(StreamPair, Stream, HostPort,
513
+ RequestURI, Parts, Options1)
514
+ ),
515
+ return_final_url(Options).
516
+
517
+ proxy_request_uri(direct, Parts, RequestURI) :-
518
+ !,
519
+ parts_request_uri(Parts, RequestURI).
520
+ proxy_request_uri(unix_socket(_), Parts, RequestURI) :-
521
+ !,
522
+ parts_request_uri(Parts, RequestURI).
523
+ proxy_request_uri(_, Parts, RequestURI) :-
524
+ parts_uri(Parts, RequestURI).
525
+
526
+ http:http_connection_over_proxy(unix_socket(Path), _, _,
527
+ StreamPair, Options, Options) :-
528
+ !,
529
+ unix_domain_socket(Socket),
530
+ tcp_connect(Socket, Path),
531
+ tcp_open_socket(Socket, In, Out),
532
+ stream_pair(StreamPair, In, Out).
533
+ http:http_connection_over_proxy(direct, _, Host:Port,
534
+ StreamPair, Options, Options) :-
535
+ !,
536
+ open_socket(Host:Port, StreamPair, Options).
537
+ http:http_connection_over_proxy(proxy(ProxyHost, ProxyPort), Parts, _,
538
+ StreamPair, Options, Options) :-
539
+ \+ ( memberchk(scheme(Scheme), Parts),
540
+ secure_scheme(Scheme)
541
+ ),
542
+ !,
543
+ % We do not want any /more/ proxy after this
544
+ open_socket(ProxyHost:ProxyPort, StreamPair,
545
+ [bypass_proxy(true)|Options]).
546
+ http:http_connection_over_proxy(socks(SocksHost, SocksPort), _Parts, Host:Port,
547
+ StreamPair, Options, Options) :-
548
+ !,
549
+ tcp_connect(SocksHost:SocksPort, StreamPair, [bypass_proxy(true)]),
550
+ catch(negotiate_socks_connection(Host:Port, StreamPair),
551
+ Error,
552
+ ( close(StreamPair, [force(true)]),
553
+ throw(Error)
554
+ )).
555
+
556
+ %! hooked_options(+Parts, -Options) is nondet.
557
+ %
558
+ % Calls http:open_options/2 and if necessary upgrades old SSL
559
+ % cacerts_file(File) option to a cacerts(List) option to ensure proper
560
+ % merging of options.
561
+
562
+ hooked_options(Parts, Options) :-
563
+ http:open_options(Parts, Options0),
564
+ upgrade_ssl_options(Parts, Options0, Options).
565
+
566
+ :- if(current_predicate(ssl_upgrade_legacy_options/2)).
567
+ upgrade_ssl_options(Parts, Options0, Options) :-
568
+ requires_ssl(Parts),
569
+ !,
570
+ ssl_upgrade_legacy_options(Options0, Options).
571
+ :- endif.
572
+ upgrade_ssl_options(_, Options, Options).
573
+
574
+ merge_options_rev(Old, New, Merged) :-
575
+ merge_options(New, Old, Merged).
576
+
577
+ is_meta(pem_password_hook). % SSL plugin callbacks
578
+ is_meta(cert_verify_hook).
579
+
580
+
581
+ http:http_protocol_hook(http, _, StreamPair, StreamPair, _).
582
+
583
+ default_port(https, 443) :- !.
584
+ default_port(wss, 443) :- !.
585
+ default_port(_, 80).
586
+
587
+ host_and_port(Host, DefPort, DefPort, Host) :- !.
588
+ host_and_port(Host, _, Port, Host:Port).
589
+
590
+ %! autoload_https(+Parts) is det.
591
+ %
592
+ % If the requested scheme is https or wss, load the HTTPS plugin.
593
+
594
+ autoload_https(Parts) :-
595
+ requires_ssl(Parts),
596
+ memberchk(scheme(S), Parts),
597
+ \+ clause(http:http_protocol_hook(S, _, StreamPair, StreamPair, _),_),
598
+ exists_source(library(http/http_ssl_plugin)),
599
+ !,
600
+ use_module(library(http/http_ssl_plugin)).
601
+ autoload_https(_).
602
+
603
+ requires_ssl(Parts) :-
604
+ memberchk(scheme(S), Parts),
605
+ secure_scheme(S).
606
+
607
+ secure_scheme(https).
608
+ secure_scheme(wss).
609
+
610
+ %! send_rec_header(+StreamPair, -Stream,
611
+ %! +Host, +RequestURI, +Parts, +Options) is det.
612
+ %
613
+ % Send header to Out and process reply. If there is an error or
614
+ % failure, close In and Out and return the error or failure.
615
+
616
+ send_rec_header(StreamPair, Stream, Host, RequestURI, Parts, Options) :-
617
+ ( catch(guarded_send_rec_header(StreamPair, Stream,
618
+ Host, RequestURI, Parts, Options),
619
+ E, true)
620
+ -> ( var(E)
621
+ -> ( option(output(StreamPair), Options)
622
+ -> true
623
+ ; true
624
+ )
625
+ ; close(StreamPair, [force(true)]),
626
+ throw(E)
627
+ )
628
+ ; close(StreamPair, [force(true)]),
629
+ fail
630
+ ).
631
+
632
+ guarded_send_rec_header(StreamPair, Stream, Host, RequestURI, Parts, Options) :-
633
+ user_agent(Agent, Options),
634
+ method(Options, MNAME),
635
+ http_version(Version),
636
+ option(connection(Connection), Options, close),
637
+ debug(http(send_request), "> ~w ~w HTTP/~w", [MNAME, RequestURI, Version]),
638
+ debug(http(send_request), "> Host: ~w", [Host]),
639
+ debug(http(send_request), "> User-Agent: ~w", [Agent]),
640
+ debug(http(send_request), "> Connection: ~w", [Connection]),
641
+ format(StreamPair,
642
+ '~w ~w HTTP/~w\r\n\c
643
+ Host: ~w\r\n\c
644
+ User-Agent: ~w\r\n\c
645
+ Connection: ~w\r\n',
646
+ [MNAME, RequestURI, Version, Host, Agent, Connection]),
647
+ parts_uri(Parts, URI),
648
+ x_headers(Options, URI, StreamPair),
649
+ write_cookies(StreamPair, Parts, Options),
650
+ ( option(post(PostData), Options)
651
+ -> http_post_data(PostData, StreamPair, [])
652
+ ; format(StreamPair, '\r\n', [])
653
+ ),
654
+ flush_output(StreamPair),
655
+ % read the reply header
656
+ read_header(StreamPair, Parts, ReplyVersion, Code, Comment, Lines),
657
+ update_cookies(Lines, Parts, Options),
658
+ reply_header(Lines, Options),
659
+ do_open(ReplyVersion, Code, Comment, Lines, Options, Parts, Host,
660
+ StreamPair, Stream).
661
+
662
+
663
+ %! http_version(-Version:atom) is det.
664
+ %
665
+ % HTTP version we publish. We can only use 1.1 if we support
666
+ % chunked encoding.
667
+
668
+ http_version('1.1') :-
669
+ http:current_transfer_encoding(chunked),
670
+ !.
671
+ http_version('1.1') :-
672
+ autoload_encoding(chunked),
673
+ !.
674
+ http_version('1.0').
675
+
676
+ method(Options, MNAME) :-
677
+ option(post(_), Options),
678
+ !,
679
+ option(method(M), Options, post),
680
+ ( map_method(M, MNAME0)
681
+ -> MNAME = MNAME0
682
+ ; domain_error(method, M)
683
+ ).
684
+ method(Options, MNAME) :-
685
+ option(method(M), Options, get),
686
+ ( map_method(M, MNAME0)
687
+ -> MNAME = MNAME0
688
+ ; map_method(_, M)
689
+ -> MNAME = M
690
+ ; domain_error(method, M)
691
+ ).
692
+
693
+ %! map_method(+MethodID, -Method)
694
+ %
695
+ % Support additional ``METHOD`` keywords. Default are the official
696
+ % HTTP methods as defined by the various RFCs.
697
+
698
+ :- multifile
699
+ map_method/2.
700
+
701
+ map_method(delete, 'DELETE').
702
+ map_method(get, 'GET').
703
+ map_method(head, 'HEAD').
704
+ map_method(post, 'POST').
705
+ map_method(put, 'PUT').
706
+ map_method(patch, 'PATCH').
707
+ map_method(options, 'OPTIONS').
708
+
709
+ %! x_headers(+Options, +URI, +Out) is det.
710
+ %
711
+ % Emit extra headers from request_header(Name=Value) options in
712
+ % Options.
713
+ %
714
+ % @tbd Use user/password fields
715
+
716
+ x_headers(Options, URI, Out) :-
717
+ x_headers_(Options, [url(URI)|Options], Out).
718
+
719
+ x_headers_([], _, _).
720
+ x_headers_([H|T], Options, Out) :-
721
+ x_header(H, Options, Out),
722
+ x_headers_(T, Options, Out).
723
+
724
+ x_header(request_header(Name=Value), _, Out) :-
725
+ !,
726
+ debug(http(send_request), "> ~w: ~w", [Name, Value]),
727
+ format(Out, '~w: ~w\r\n', [Name, Value]).
728
+ x_header(proxy_authorization(ProxyAuthorization), Options, Out) :-
729
+ !,
730
+ auth_header(ProxyAuthorization, Options, 'Proxy-Authorization', Out).
731
+ x_header(authorization(Authorization), Options, Out) :-
732
+ !,
733
+ auth_header(Authorization, Options, 'Authorization', Out).
734
+ x_header(range(Spec), _, Out) :-
735
+ !,
736
+ Spec =.. [Unit, From, To],
737
+ ( To == end
738
+ -> ToT = ''
739
+ ; must_be(integer, To),
740
+ ToT = To
741
+ ),
742
+ debug(http(send_request), "> Range: ~w=~d-~w", [Unit, From, ToT]),
743
+ format(Out, 'Range: ~w=~d-~w\r\n', [Unit, From, ToT]).
744
+ x_header(_, _, _).
745
+
746
+ %! auth_header(+AuthOption, +Options, +HeaderName, +Out)
747
+
748
+ auth_header(basic(User, Password), _, Header, Out) :-
749
+ !,
750
+ format(codes(Codes), '~w:~w', [User, Password]),
751
+ phrase(base64(Codes), Base64Codes),
752
+ debug(http(send_request), "> ~w: Basic ~s", [Header, Base64Codes]),
753
+ format(Out, '~w: Basic ~s\r\n', [Header, Base64Codes]).
754
+ auth_header(bearer(Token), _, Header, Out) :-
755
+ !,
756
+ debug(http(send_request), "> ~w: Bearer ~w", [Header,Token]),
757
+ format(Out, '~w: Bearer ~w\r\n', [Header, Token]).
758
+ auth_header(Auth, Options, _, Out) :-
759
+ option(url(URL), Options),
760
+ add_method(Options, Options1),
761
+ http:authenticate_client(URL, send_auth_header(Auth, Out, Options1)),
762
+ !.
763
+ auth_header(Auth, _, _, _) :-
764
+ domain_error(authorization, Auth).
765
+
766
+ user_agent(Agent, Options) :-
767
+ ( option(user_agent(Agent), Options)
768
+ -> true
769
+ ; user_agent(Agent)
770
+ ).
771
+
772
+ add_method(Options0, Options) :-
773
+ option(method(_), Options0),
774
+ !,
775
+ Options = Options0.
776
+ add_method(Options0, Options) :-
777
+ option(post(_), Options0),
778
+ !,
779
+ Options = [method(post)|Options0].
780
+ add_method(Options0, [method(get)|Options0]).
781
+
782
+ %! do_open(+HTTPVersion, +HTTPStatusCode, +HTTPStatusComment, +Header,
783
+ %! +Options, +Parts, +Host, +In, -FinalIn) is det.
784
+ %
785
+ % Handle the HTTP status once available. If 200-299, we are ok. If a
786
+ % redirect, redo the open, returning a new stream. Else issue an
787
+ % error.
788
+ %
789
+ % @error existence_error(url, URL)
790
+
791
+ % Redirections
792
+ do_open(_, Code, _, Lines, Options0, Parts, _, In, Stream) :-
793
+ redirect_code(Code),
794
+ option(redirect(true), Options0, true),
795
+ location(Lines, RequestURI),
796
+ !,
797
+ debug(http(redirect), 'http_open: redirecting to ~w', [RequestURI]),
798
+ close(In),
799
+ parts_uri(Parts, Base),
800
+ uri_resolve(RequestURI, Base, Redirected),
801
+ parse_url_ex(Redirected, RedirectedParts),
802
+ ( redirect_limit_exceeded(Options0, Max)
803
+ -> format(atom(Comment), 'max_redirect (~w) limit exceeded', [Max]),
804
+ throw(error(permission_error(redirect, http, Redirected),
805
+ context(_, Comment)))
806
+ ; redirect_loop(RedirectedParts, Options0)
807
+ -> throw(error(permission_error(redirect, http, Redirected),
808
+ context(_, 'Redirection loop')))
809
+ ; true
810
+ ),
811
+ redirect_options(Parts, RedirectedParts, Options0, Options),
812
+ http_open(RedirectedParts, Stream, Options).
813
+ % Need authentication
814
+ do_open(_Version, Code, _Comment, Lines, Options0, Parts, _Host, In0, Stream) :-
815
+ authenticate_code(Code),
816
+ option(authenticate(true), Options0, true),
817
+ parts_uri(Parts, URI),
818
+ parse_headers(Lines, Headers),
819
+ http:authenticate_client(
820
+ URI,
821
+ auth_reponse(Headers, Options0, Options)),
822
+ !,
823
+ close(In0),
824
+ http_open(Parts, Stream, Options).
825
+ % Accepted codes
826
+ do_open(Version, Code, _, Lines, Options, Parts, Host, In0, In) :-
827
+ ( option(status_code(Code), Options),
828
+ Lines \== []
829
+ -> true
830
+ ; successful_code(Code)
831
+ ),
832
+ !,
833
+ parts_uri(Parts, URI),
834
+ parse_headers(Lines, Headers),
835
+ return_version(Options, Version),
836
+ return_size(Options, Headers),
837
+ return_fields(Options, Headers),
838
+ return_headers(Options, [status_code(Code)|Headers]),
839
+ consider_keep_alive(Lines, Parts, Host, In0, In1, Options),
840
+ transfer_encoding_filter(Lines, In1, In, Options),
841
+ % properly re-initialise the stream
842
+ set_stream(In, file_name(URI)),
843
+ set_stream(In, record_position(true)).
844
+ do_open(_, _, _, [], Options, _, _, _, _) :-
845
+ option(connection(Connection), Options),
846
+ keep_alive(Connection),
847
+ !,
848
+ throw(error(keep_alive(closed),_)).
849
+ % report anything else as error
850
+ do_open(_Version, Code, Comment, _, _, Parts, _, _, _) :-
851
+ parts_uri(Parts, URI),
852
+ ( map_error_code(Code, Error)
853
+ -> Formal =.. [Error, url, URI]
854
+ ; Formal = existence_error(url, URI)
855
+ ),
856
+ throw(error(Formal, context(_, status(Code, Comment)))).
857
+
858
+
859
+ successful_code(Code) :-
860
+ between(200, 299, Code).
861
+
862
+ %! redirect_limit_exceeded(+Options:list(compound), -Max:nonneg) is semidet.
863
+ %
864
+ % True if we have exceeded the maximum redirection length (default 10).
865
+
866
+ redirect_limit_exceeded(Options, Max) :-
867
+ option(visited(Visited), Options, []),
868
+ length(Visited, N),
869
+ option(max_redirect(Max), Options, 10),
870
+ (Max == infinite -> fail ; N > Max).
871
+
872
+
873
+ %! redirect_loop(+Parts, +Options) is semidet.
874
+ %
875
+ % True if we are in a redirection loop. Note that some sites
876
+ % redirect once to the same place using cookies or similar, so we
877
+ % allow for two tries. In fact, we should probably test whether
878
+ % authorization or cookie headers have changed.
879
+
880
+ redirect_loop(Parts, Options) :-
881
+ option(visited(Visited), Options, []),
882
+ include(==(Parts), Visited, Same),
883
+ length(Same, Count),
884
+ Count > 2.
885
+
886
+
887
+ %! redirect_options(+Parts, +RedirectedParts, +Options0, -Options) is det.
888
+ %
889
+ % A redirect from a POST should do a GET on the returned URI. This
890
+ % means we must remove the method(post) and post(Data) options from
891
+ % the original option-list.
892
+ %
893
+ % If we are connecting over a Unix domain socket we drop this option
894
+ % if the redirect host does not match the initial host.
895
+
896
+ redirect_options(Parts, RedirectedParts, Options0, Options) :-
897
+ select_option(unix_socket(_), Options0, Options1),
898
+ memberchk(host(Host), Parts),
899
+ memberchk(host(RHost), RedirectedParts),
900
+ debug(http(redirect), 'http_open: redirecting AF_UNIX ~w to ~w',
901
+ [Host, RHost]),
902
+ Host \== RHost,
903
+ !,
904
+ redirect_options(Options1, Options).
905
+ redirect_options(_, _, Options0, Options) :-
906
+ redirect_options(Options0, Options).
907
+
908
+ redirect_options(Options0, Options) :-
909
+ ( select_option(post(_), Options0, Options1)
910
+ -> true
911
+ ; Options1 = Options0
912
+ ),
913
+ ( select_option(method(Method), Options1, Options),
914
+ \+ redirect_method(Method)
915
+ -> true
916
+ ; Options = Options1
917
+ ).
918
+
919
+ redirect_method(delete).
920
+ redirect_method(get).
921
+ redirect_method(head).
922
+
923
+
924
+ %! map_error_code(+HTTPCode, -PrologError) is semidet.
925
+ %
926
+ % Map HTTP error codes to Prolog errors.
927
+ %
928
+ % @tbd Many more maps. Unfortunately many have no sensible Prolog
929
+ % counterpart.
930
+
931
+ map_error_code(401, permission_error).
932
+ map_error_code(403, permission_error).
933
+ map_error_code(404, existence_error).
934
+ map_error_code(405, permission_error).
935
+ map_error_code(407, permission_error).
936
+ map_error_code(410, existence_error).
937
+
938
+ redirect_code(301). % Moved Permanently
939
+ redirect_code(302). % Found (previously "Moved Temporary")
940
+ redirect_code(303). % See Other
941
+ redirect_code(307). % Temporary Redirect
942
+
943
+ authenticate_code(401).
944
+
945
+ %! open_socket(+Address, -StreamPair, +Options) is det.
946
+ %
947
+ % Create and connect a client socket to Address. Options
948
+ %
949
+ % * timeout(+Timeout)
950
+ % Sets timeout on the stream, *after* connecting the
951
+ % socket.
952
+ %
953
+ % @tbd Make timeout also work on tcp_connect/4.
954
+ % @tbd This is the same as do_connect/4 in http_client.pl
955
+
956
+ open_socket(Address, StreamPair, Options) :-
957
+ debug(http(open), 'http_open: Connecting to ~p ...', [Address]),
958
+ tcp_connect(Address, StreamPair, Options),
959
+ stream_pair(StreamPair, In, Out),
960
+ debug(http(open), '\tok ~p ---> ~p', [In, Out]),
961
+ set_stream(In, record_position(false)),
962
+ ( option(timeout(Timeout), Options)
963
+ -> set_stream(In, timeout(Timeout))
964
+ ; true
965
+ ).
966
+
967
+
968
+ return_version(Options, Major-Minor) :-
969
+ option(version(Major-Minor), Options, _).
970
+
971
+ return_size(Options, Headers) :-
972
+ ( memberchk(content_length(Size), Headers)
973
+ -> option(size(Size), Options, _)
974
+ ; true
975
+ ).
976
+
977
+ return_fields([], _).
978
+ return_fields([header(Name, Value)|T], Headers) :-
979
+ !,
980
+ ( Term =.. [Name,Value],
981
+ memberchk(Term, Headers)
982
+ -> true
983
+ ; Value = ''
984
+ ),
985
+ return_fields(T, Headers).
986
+ return_fields([_|T], Lines) :-
987
+ return_fields(T, Lines).
988
+
989
+ return_headers(Options, Headers) :-
990
+ option(headers(Headers), Options, _).
991
+
992
+ %! parse_headers(+Lines, -Headers:list(compound)) is det.
993
+ %
994
+ % Parse the header lines for the headers(-List) option. Invalid
995
+ % header lines are skipped, printing a warning using
996
+ % print_message/2.
997
+
998
+ parse_headers([], []) :- !.
999
+ parse_headers([Line|Lines], Headers) :-
1000
+ catch(http_parse_header(Line, [Header]), Error, true),
1001
+ ( var(Error)
1002
+ -> Headers = [Header|More]
1003
+ ; print_message(warning, Error),
1004
+ Headers = More
1005
+ ),
1006
+ parse_headers(Lines, More).
1007
+
1008
+
1009
+ %! return_final_url(+Options) is semidet.
1010
+ %
1011
+ % If Options contains final_url(URL), unify URL with the final
1012
+ % URL after redirections.
1013
+
1014
+ return_final_url(Options) :-
1015
+ option(final_url(URL), Options),
1016
+ var(URL),
1017
+ !,
1018
+ option(visited([Parts|_]), Options),
1019
+ parts_uri(Parts, URL).
1020
+ return_final_url(_).
1021
+
1022
+
1023
+ %! transfer_encoding_filter(+Lines, +In0, -In, +Options) is det.
1024
+ %
1025
+ % Install filters depending on the transfer encoding. If In0 is a
1026
+ % stream-pair, we close the output side. If transfer-encoding is
1027
+ % not specified, the content-encoding is interpreted as a synonym
1028
+ % for transfer-encoding, because many servers incorrectly depend
1029
+ % on this. Exceptions to this are content-types for which
1030
+ % disable_encoding_filter/1 holds.
1031
+
1032
+ transfer_encoding_filter(Lines, In0, In, Options) :-
1033
+ transfer_encoding(Lines, Encoding),
1034
+ !,
1035
+ transfer_encoding_filter_(Encoding, In0, In, Options).
1036
+ transfer_encoding_filter(Lines, In0, In, Options) :-
1037
+ content_encoding(Lines, Encoding),
1038
+ content_type(Lines, Type),
1039
+ \+ http:disable_encoding_filter(Type),
1040
+ !,
1041
+ transfer_encoding_filter_(Encoding, In0, In, Options).
1042
+ transfer_encoding_filter(_, In, In, _Options).
1043
+
1044
+ transfer_encoding_filter_(Encoding, In0, In, Options) :-
1045
+ option(raw_encoding(Encoding), Options),
1046
+ !,
1047
+ In = In0.
1048
+ transfer_encoding_filter_(Encoding, In0, In, _Options) :-
1049
+ stream_pair(In0, In1, Out),
1050
+ ( http:encoding_filter(Encoding, In1, In2)
1051
+ -> true
1052
+ ; autoload_encoding(Encoding),
1053
+ http:encoding_filter(Encoding, In1, In2)
1054
+ -> true
1055
+ ; domain_error(http_encoding, Encoding)
1056
+ ),
1057
+ ( var(Out)
1058
+ -> In = In2
1059
+ ; stream_pair(In, In2, Out)
1060
+ ).
1061
+
1062
+ :- multifile
1063
+ autoload_encoding/1.
1064
+
1065
+ :- if(exists_source(library(zlib))).
1066
+ autoload_encoding(gzip) :-
1067
+ use_module(library(zlib)).
1068
+ :- endif.
1069
+ :- if(exists_source(library(http/http_stream))).
1070
+ autoload_encoding(chunked) :-
1071
+ use_module(library(http/http_stream)).
1072
+ :- endif.
1073
+
1074
+ content_type(Lines, Type) :-
1075
+ member(Line, Lines),
1076
+ phrase(field('content-type'), Line, Rest),
1077
+ !,
1078
+ atom_codes(Type, Rest).
1079
+
1080
+ %! http:disable_encoding_filter(+ContentType) is semidet.
1081
+ %
1082
+ % Do not use the =|Content-encoding|= as =|Transfer-encoding|=
1083
+ % encoding for specific values of ContentType. This predicate is
1084
+ % multifile and can thus be extended by the user.
1085
+
1086
+ http:disable_encoding_filter('application/x-gzip').
1087
+ http:disable_encoding_filter('application/x-tar').
1088
+ http:disable_encoding_filter('x-world/x-vrml').
1089
+ http:disable_encoding_filter('application/zip').
1090
+ http:disable_encoding_filter('application/x-gzip').
1091
+ http:disable_encoding_filter('application/x-zip-compressed').
1092
+ http:disable_encoding_filter('application/x-compress').
1093
+ http:disable_encoding_filter('application/x-compressed').
1094
+ http:disable_encoding_filter('application/x-spoon').
1095
+
1096
+ %! transfer_encoding(+Lines, -Encoding) is semidet.
1097
+ %
1098
+ % True if Encoding is the value of the =|Transfer-encoding|=
1099
+ % header.
1100
+
1101
+ transfer_encoding(Lines, Encoding) :-
1102
+ what_encoding(transfer_encoding, Lines, Encoding).
1103
+
1104
+ what_encoding(What, Lines, Encoding) :-
1105
+ member(Line, Lines),
1106
+ phrase(encoding_(What, Debug), Line, Rest),
1107
+ !,
1108
+ atom_codes(Encoding, Rest),
1109
+ debug(http(What), '~w: ~p', [Debug, Rest]).
1110
+
1111
+ encoding_(content_encoding, 'Content-encoding') -->
1112
+ field('content-encoding').
1113
+ encoding_(transfer_encoding, 'Transfer-encoding') -->
1114
+ field('transfer-encoding').
1115
+
1116
+ %! content_encoding(+Lines, -Encoding) is semidet.
1117
+ %
1118
+ % True if Encoding is the value of the =|Content-encoding|=
1119
+ % header.
1120
+
1121
+ content_encoding(Lines, Encoding) :-
1122
+ what_encoding(content_encoding, Lines, Encoding).
1123
+
1124
+ %! read_header(+In:stream, +Parts, -Version, -Code:int,
1125
+ %! -Comment:atom, -Lines:list) is det.
1126
+ %
1127
+ % Read the HTTP reply-header. If the reply is completely empty
1128
+ % an existence error is thrown. If the replied header is
1129
+ % otherwise invalid a 500 HTTP error is simulated, having the
1130
+ % comment =|Invalid reply header|=.
1131
+ %
1132
+ % @param Parts A list of compound terms that describe the
1133
+ % parsed request URI.
1134
+ % @param Version HTTP reply version as Major-Minor pair
1135
+ % @param Code Numeric HTTP reply-code
1136
+ % @param Comment Comment of reply-code as atom
1137
+ % @param Lines Remaining header lines as code-lists.
1138
+ %
1139
+ % @error existence_error(http_reply, Uri)
1140
+
1141
+ read_header(In, Parts, Major-Minor, Code, Comment, Lines) :-
1142
+ read_line_to_codes(In, Line),
1143
+ ( Line == end_of_file
1144
+ -> parts_uri(Parts, Uri),
1145
+ existence_error(http_reply,Uri)
1146
+ ; true
1147
+ ),
1148
+ Line \== end_of_file,
1149
+ phrase(first_line(Major-Minor, Code, Comment), Line),
1150
+ debug(http(open), 'HTTP/~d.~d ~w ~w', [Major, Minor, Code, Comment]),
1151
+ read_line_to_codes(In, Line2),
1152
+ rest_header(Line2, In, Lines),
1153
+ !,
1154
+ ( debugging(http(open))
1155
+ -> forall(member(HL, Lines),
1156
+ debug(http(open), '~s', [HL]))
1157
+ ; true
1158
+ ).
1159
+ read_header(_, _, 1-1, 500, 'Invalid reply header', []).
1160
+
1161
+ rest_header([], _, []) :- !. % blank line: end of header
1162
+ rest_header(L0, In, [L0|L]) :-
1163
+ read_line_to_codes(In, L1),
1164
+ rest_header(L1, In, L).
1165
+
1166
+ %! content_length(+Header, -Length:int) is semidet.
1167
+ %
1168
+ % Find the Content-Length in an HTTP reply-header.
1169
+
1170
+ content_length(Lines, Length) :-
1171
+ member(Line, Lines),
1172
+ phrase(content_length(Length0), Line),
1173
+ !,
1174
+ Length = Length0.
1175
+
1176
+ location(Lines, RequestURI) :-
1177
+ member(Line, Lines),
1178
+ phrase(atom_field(location, RequestURI), Line),
1179
+ !.
1180
+
1181
+ connection(Lines, Connection) :-
1182
+ member(Line, Lines),
1183
+ phrase(atom_field(connection, Connection0), Line),
1184
+ !,
1185
+ Connection = Connection0.
1186
+
1187
+ first_line(Major-Minor, Code, Comment) -->
1188
+ "HTTP/", integer(Major), ".", integer(Minor),
1189
+ skip_blanks,
1190
+ integer(Code),
1191
+ skip_blanks,
1192
+ rest(Comment).
1193
+
1194
+ atom_field(Name, Value) -->
1195
+ field(Name),
1196
+ rest(Value).
1197
+
1198
+ content_length(Len) -->
1199
+ field('content-length'),
1200
+ integer(Len).
1201
+
1202
+ field(Name) -->
1203
+ { atom_codes(Name, Codes) },
1204
+ field_codes(Codes).
1205
+
1206
+ field_codes([]) -->
1207
+ ":",
1208
+ skip_blanks.
1209
+ field_codes([H|T]) -->
1210
+ [C],
1211
+ { match_header_char(H, C)
1212
+ },
1213
+ field_codes(T).
1214
+
1215
+ match_header_char(C, C) :- !.
1216
+ match_header_char(C, U) :-
1217
+ code_type(C, to_lower(U)),
1218
+ !.
1219
+ match_header_char(0'_, 0'-).
1220
+
1221
+
1222
+ skip_blanks -->
1223
+ [C],
1224
+ { code_type(C, white)
1225
+ },
1226
+ !,
1227
+ skip_blanks.
1228
+ skip_blanks -->
1229
+ [].
1230
+
1231
+ %! integer(-Int)//
1232
+ %
1233
+ % Read 1 or more digits and return as integer.
1234
+
1235
+ integer(Code) -->
1236
+ digit(D0),
1237
+ digits(D),
1238
+ { number_codes(Code, [D0|D])
1239
+ }.
1240
+
1241
+ digit(C) -->
1242
+ [C],
1243
+ { code_type(C, digit)
1244
+ }.
1245
+
1246
+ digits([D0|D]) -->
1247
+ digit(D0),
1248
+ !,
1249
+ digits(D).
1250
+ digits([]) -->
1251
+ [].
1252
+
1253
+ %! rest(-Atom:atom)//
1254
+ %
1255
+ % Get rest of input as an atom.
1256
+
1257
+ rest(Atom) --> call(rest_(Atom)).
1258
+
1259
+ rest_(Atom, L, []) :-
1260
+ atom_codes(Atom, L).
1261
+
1262
+
1263
+ %! reply_header(+Lines, +Options) is det.
1264
+ %
1265
+ % Return the entire reply header as a list of strings to the option
1266
+ % raw_headers(-Headers).
1267
+
1268
+ reply_header(Lines, Options) :-
1269
+ option(raw_headers(Headers), Options),
1270
+ !,
1271
+ maplist(string_codes, Headers, Lines).
1272
+ reply_header(_, _).
1273
+
1274
+
1275
+ /*******************************
1276
+ * AUTHORIZATION MANAGEMENT *
1277
+ *******************************/
1278
+
1279
+ %! http_set_authorization(+URL, +Authorization) is det.
1280
+ %
1281
+ % Set user/password to supply with URLs that have URL as prefix.
1282
+ % If Authorization is the atom =|-|=, possibly defined
1283
+ % authorization is cleared. For example:
1284
+ %
1285
+ % ```
1286
+ % ?- http_set_authorization('http://www.example.com/private/',
1287
+ % basic('John', 'Secret'))
1288
+ % ```
1289
+ %
1290
+ % @tbd Move to a separate module, so http_get/3, etc. can use this
1291
+ % too.
1292
+
1293
+ :- dynamic
1294
+ stored_authorization/2,
1295
+ cached_authorization/2.
1296
+
1297
+ http_set_authorization(URL, Authorization) :-
1298
+ must_be(atom, URL),
1299
+ retractall(stored_authorization(URL, _)),
1300
+ ( Authorization = (-)
1301
+ -> true
1302
+ ; check_authorization(Authorization),
1303
+ assert(stored_authorization(URL, Authorization))
1304
+ ),
1305
+ retractall(cached_authorization(_,_)).
1306
+
1307
+ check_authorization(Var) :-
1308
+ var(Var),
1309
+ !,
1310
+ instantiation_error(Var).
1311
+ check_authorization(basic(User, Password)) :-
1312
+ must_be(atom, User),
1313
+ must_be(text, Password).
1314
+ check_authorization(digest(User, Password)) :-
1315
+ must_be(atom, User),
1316
+ must_be(text, Password).
1317
+
1318
+ %! authorization(+URL, -Authorization) is semidet.
1319
+ %
1320
+ % True if Authorization must be supplied for URL.
1321
+ %
1322
+ % @tbd Cleanup cache if it gets too big.
1323
+
1324
+ authorization(_, _) :-
1325
+ \+ stored_authorization(_, _),
1326
+ !,
1327
+ fail.
1328
+ authorization(URL, Authorization) :-
1329
+ cached_authorization(URL, Authorization),
1330
+ !,
1331
+ Authorization \== (-).
1332
+ authorization(URL, Authorization) :-
1333
+ ( stored_authorization(Prefix, Authorization),
1334
+ sub_atom(URL, 0, _, _, Prefix)
1335
+ -> assert(cached_authorization(URL, Authorization))
1336
+ ; assert(cached_authorization(URL, -)),
1337
+ fail
1338
+ ).
1339
+
1340
+ add_authorization(_, Options, Options) :-
1341
+ option(authorization(_), Options),
1342
+ !.
1343
+ add_authorization(Parts, Options0, Options) :-
1344
+ url_part(user(User), Parts),
1345
+ url_part(password(Passwd), Parts),
1346
+ !,
1347
+ Options = [authorization(basic(User,Passwd))|Options0].
1348
+ add_authorization(Parts, Options0, Options) :-
1349
+ stored_authorization(_, _) -> % quick test to avoid work
1350
+ parts_uri(Parts, URL),
1351
+ authorization(URL, Auth),
1352
+ !,
1353
+ Options = [authorization(Auth)|Options0].
1354
+ add_authorization(_, Options, Options).
1355
+
1356
+
1357
+ %! parse_url_ex(+URL, -Parts)
1358
+ %
1359
+ % Parts: Scheme, Host, Port, User:Password, RequestURI (no
1360
+ % fragment).
1361
+
1362
+ parse_url_ex(URL, [uri(URL)|Parts]) :-
1363
+ uri_components(URL, Components),
1364
+ phrase(components(Components), Parts),
1365
+ ( option(host(_), Parts)
1366
+ -> true
1367
+ ; domain_error(url, URL)
1368
+ ).
1369
+
1370
+ components(Components) -->
1371
+ uri_scheme(Components),
1372
+ uri_path(Components),
1373
+ uri_authority(Components),
1374
+ uri_request_uri(Components).
1375
+
1376
+ uri_scheme(Components) -->
1377
+ { uri_data(scheme, Components, Scheme), nonvar(Scheme) },
1378
+ !,
1379
+ [ scheme(Scheme)
1380
+ ].
1381
+ uri_scheme(_) --> [].
1382
+
1383
+ uri_path(Components) -->
1384
+ { uri_data(path, Components, Path0), nonvar(Path0),
1385
+ ( Path0 == ''
1386
+ -> Path = (/)
1387
+ ; Path = Path0
1388
+ )
1389
+ },
1390
+ !,
1391
+ [ path(Path)
1392
+ ].
1393
+ uri_path(_) --> [].
1394
+
1395
+ uri_authority(Components) -->
1396
+ { uri_data(authority, Components, Auth), nonvar(Auth),
1397
+ !,
1398
+ uri_authority_components(Auth, Data)
1399
+ },
1400
+ [ authority(Auth) ],
1401
+ auth_field(user, Data),
1402
+ auth_field(password, Data),
1403
+ auth_field(host, Data),
1404
+ auth_field(port, Data).
1405
+ uri_authority(_) --> [].
1406
+
1407
+ auth_field(Field, Data) -->
1408
+ { uri_authority_data(Field, Data, EncValue), nonvar(EncValue),
1409
+ !,
1410
+ ( atom(EncValue)
1411
+ -> uri_encoded(query_value, Value, EncValue)
1412
+ ; Value = EncValue
1413
+ ),
1414
+ Part =.. [Field,Value]
1415
+ },
1416
+ [ Part ].
1417
+ auth_field(_, _) --> [].
1418
+
1419
+ uri_request_uri(Components) -->
1420
+ { uri_data(path, Components, Path0),
1421
+ uri_data(search, Components, Search),
1422
+ ( Path0 == ''
1423
+ -> Path = (/)
1424
+ ; Path = Path0
1425
+ ),
1426
+ uri_data(path, Components2, Path),
1427
+ uri_data(search, Components2, Search),
1428
+ uri_components(RequestURI, Components2)
1429
+ },
1430
+ [ request_uri(RequestURI)
1431
+ ].
1432
+
1433
+ %! parts_scheme(+Parts, -Scheme) is det.
1434
+ %! parts_uri(+Parts, -URI) is det.
1435
+ %! parts_request_uri(+Parts, -RequestURI) is det.
1436
+ %! parts_search(+Parts, -Search) is det.
1437
+ %! parts_authority(+Parts, -Authority) is semidet.
1438
+
1439
+ parts_scheme(Parts, Scheme) :-
1440
+ url_part(scheme(Scheme), Parts),
1441
+ !.
1442
+ parts_scheme(Parts, Scheme) :- % compatibility with library(url)
1443
+ url_part(protocol(Scheme), Parts),
1444
+ !.
1445
+ parts_scheme(_, http).
1446
+
1447
+ parts_authority(Parts, Auth) :-
1448
+ url_part(authority(Auth), Parts),
1449
+ !.
1450
+ parts_authority(Parts, Auth) :-
1451
+ url_part(host(Host), Parts, _),
1452
+ url_part(port(Port), Parts, _),
1453
+ url_part(user(User), Parts, _),
1454
+ url_part(password(Password), Parts, _),
1455
+ uri_authority_components(Auth,
1456
+ uri_authority(User, Password, Host, Port)).
1457
+
1458
+ parts_request_uri(Parts, RequestURI) :-
1459
+ option(request_uri(RequestURI), Parts),
1460
+ !.
1461
+ parts_request_uri(Parts, RequestURI) :-
1462
+ url_part(path(Path), Parts, /),
1463
+ ignore(parts_search(Parts, Search)),
1464
+ uri_data(path, Data, Path),
1465
+ uri_data(search, Data, Search),
1466
+ uri_components(RequestURI, Data).
1467
+
1468
+ parts_search(Parts, Search) :-
1469
+ option(query_string(Search), Parts),
1470
+ !.
1471
+ parts_search(Parts, Search) :-
1472
+ option(search(Fields), Parts),
1473
+ !,
1474
+ uri_query_components(Search, Fields).
1475
+
1476
+
1477
+ parts_uri(Parts, URI) :-
1478
+ option(uri(URI), Parts),
1479
+ !.
1480
+ parts_uri(Parts, URI) :-
1481
+ parts_scheme(Parts, Scheme),
1482
+ ignore(parts_authority(Parts, Auth)),
1483
+ parts_request_uri(Parts, RequestURI),
1484
+ uri_components(RequestURI, Data),
1485
+ uri_data(scheme, Data, Scheme),
1486
+ uri_data(authority, Data, Auth),
1487
+ uri_components(URI, Data).
1488
+
1489
+ parts_port(Parts, Port) :-
1490
+ parts_scheme(Parts, Scheme),
1491
+ default_port(Scheme, DefPort),
1492
+ url_part(port(Port), Parts, DefPort).
1493
+
1494
+ url_part(Part, Parts) :-
1495
+ Part =.. [Name,Value],
1496
+ Gen =.. [Name,RawValue],
1497
+ option(Gen, Parts),
1498
+ !,
1499
+ Value = RawValue.
1500
+
1501
+ url_part(Part, Parts, Default) :-
1502
+ Part =.. [Name,Value],
1503
+ Gen =.. [Name,RawValue],
1504
+ ( option(Gen, Parts)
1505
+ -> Value = RawValue
1506
+ ; Value = Default
1507
+ ).
1508
+
1509
+
1510
+ /*******************************
1511
+ * COOKIES *
1512
+ *******************************/
1513
+
1514
+ write_cookies(Out, Parts, Options) :-
1515
+ http:write_cookies(Out, Parts, Options),
1516
+ !.
1517
+ write_cookies(_, _, _).
1518
+
1519
+ update_cookies(_, _, _) :-
1520
+ predicate_property(http:update_cookies(_,_,_), number_of_clauses(0)),
1521
+ !.
1522
+ update_cookies(Lines, Parts, Options) :-
1523
+ ( member(Line, Lines),
1524
+ phrase(atom_field('set_cookie', CookieData), Line),
1525
+ http:update_cookies(CookieData, Parts, Options),
1526
+ fail
1527
+ ; true
1528
+ ).
1529
+
1530
+
1531
+ /*******************************
1532
+ * OPEN ANY *
1533
+ *******************************/
1534
+
1535
+ :- multifile iostream:open_hook/6.
1536
+
1537
+ %! iostream:open_hook(+Spec, +Mode, -Stream, -Close,
1538
+ %! +Options0, -Options) is semidet.
1539
+ %
1540
+ % Hook implementation that makes open_any/5 support =http= and
1541
+ % =https= URLs for =|Mode == read|=.
1542
+
1543
+ iostream:open_hook(URL, read, Stream, Close, Options0, Options) :-
1544
+ (atom(URL) -> true ; string(URL)),
1545
+ uri_is_global(URL),
1546
+ uri_components(URL, Components),
1547
+ uri_data(scheme, Components, Scheme),
1548
+ http_scheme(Scheme),
1549
+ !,
1550
+ Options = Options0,
1551
+ Close = close(Stream),
1552
+ http_open(URL, Stream, Options0).
1553
+
1554
+ http_scheme(http).
1555
+ http_scheme(https).
1556
+
1557
+
1558
+ /*******************************
1559
+ * KEEP-ALIVE *
1560
+ *******************************/
1561
+
1562
+ %! consider_keep_alive(+HeaderLines, +Parts, +Host,
1563
+ %! +Stream0, -Stream,
1564
+ %! +Options) is det.
1565
+ %
1566
+ % If we have agree on a Keep-alive connection, return a range stream
1567
+ % rather than the original stream. We also use the content length and
1568
+ % a range stream if we are dealing with an HTTPS connection. This is
1569
+ % because not all servers seem to complete the TLS closing handshake.
1570
+ % If the server does not complete this we receive a TLS handshake
1571
+ % error on end-of-file, causing the read to fail.
1572
+
1573
+ consider_keep_alive(Lines, Parts, Host, StreamPair, In, Options) :-
1574
+ option(connection(Asked), Options),
1575
+ keep_alive(Asked),
1576
+ connection(Lines, Given),
1577
+ keep_alive(Given),
1578
+ content_length(Lines, Bytes),
1579
+ !,
1580
+ stream_pair(StreamPair, In0, _),
1581
+ connection_address(Host, Parts, HostPort),
1582
+ debug(http(keep_alive),
1583
+ 'Keep-alive to ~w (~D bytes)', [HostPort, Bytes]),
1584
+ stream_range_open(In0, In,
1585
+ [ size(Bytes),
1586
+ onclose(keep_alive(StreamPair, HostPort))
1587
+ ]).
1588
+ consider_keep_alive(Lines, Parts, _Host, StreamPair, In, _Options) :-
1589
+ memberchk(scheme(https), Parts),
1590
+ content_length(Lines, Bytes),
1591
+ !,
1592
+ stream_pair(StreamPair, In0, _),
1593
+ stream_range_open(In0, In,
1594
+ [ size(Bytes),
1595
+ onclose(close_range(StreamPair))
1596
+ ]).
1597
+ consider_keep_alive(_, _, _, Stream, Stream, _).
1598
+
1599
+ connection_address(Host, _, Host) :-
1600
+ Host = _:_,
1601
+ !.
1602
+ connection_address(Host, Parts, Host:Port) :-
1603
+ parts_port(Parts, Port).
1604
+
1605
+ keep_alive(keep_alive) :- !.
1606
+ keep_alive(Connection) :-
1607
+ downcase_atom(Connection, 'keep-alive').
1608
+
1609
+ %! keep_alive(+StreamPair, +Host, +In, -Left) is det.
1610
+ %
1611
+ % Callback when closing the range stream used to process the content
1612
+ % of the reply. This callback makes the stream available for future
1613
+ % keep-alive connections or closes the stream. The stream is closed if
1614
+ %
1615
+ % - There are too many bytes left unprocessed in the range stream.
1616
+ % - There are too many pooled connections.
1617
+
1618
+ :- public keep_alive/4.
1619
+ :- det(keep_alive/4).
1620
+
1621
+ keep_alive(StreamPair, Host, _In, 0) :-
1622
+ !,
1623
+ add_to_pool_or_close(Host, StreamPair).
1624
+ keep_alive(StreamPair, Host, In, Left) :-
1625
+ ( Left < 100,
1626
+ debug(http(connection), 'Reading ~D left bytes', [Left]),
1627
+ read_incomplete(In, Left)
1628
+ -> add_to_pool_or_close(Host, StreamPair)
1629
+ ; debug(http(connection),
1630
+ 'Closing connection due to excessive unprocessed input', []),
1631
+ close_keep_alive(StreamPair)
1632
+ ).
1633
+
1634
+ add_to_pool_or_close(Host, StreamPair) :-
1635
+ add_to_pool(Host, StreamPair),
1636
+ !,
1637
+ debug(http(connection), 'Added connection to ~p to pool', [Host]).
1638
+ add_to_pool_or_close(Host, StreamPair) :-
1639
+ close_keep_alive(StreamPair),
1640
+ debug(http(connection), 'Closed connection to ~p [pool full]', [Host]).
1641
+
1642
+ close_keep_alive(StreamPair) :-
1643
+ ( debugging(http(connection))
1644
+ -> catch(close(StreamPair), E,
1645
+ print_message(warning, E))
1646
+ ; close(StreamPair, [force(true)])
1647
+ ).
1648
+
1649
+ :- public close_range/3.
1650
+ close_range(StreamPair, _Raw, _BytesLeft) :-
1651
+ close(StreamPair, [force(true)]).
1652
+
1653
+ %! read_incomplete(+In, +Left) is semidet.
1654
+ %
1655
+ % If we have not all input from a Keep-alive connection, read the
1656
+ % remainder if it is short. Else, we fail and close the stream.
1657
+
1658
+ read_incomplete(In, Left) :-
1659
+ catch(setup_call_cleanup(
1660
+ open_null_stream(Null),
1661
+ copy_stream_data(In, Null, Left),
1662
+ close(Null)),
1663
+ error(_,_),
1664
+ fail).
1665
+
1666
+ :- dynamic
1667
+ connection_pool/4, % Hash, Address, Stream, Time
1668
+ connection_gc_time/1.
1669
+
1670
+ %! add_to_pool(+Address, +StreamPair) is semidet.
1671
+ %
1672
+ % Add a connection to the keep-alive pool after completing the
1673
+ % interaction. Fails if there are already too many connections in the
1674
+ % pool.
1675
+
1676
+ add_to_pool(Address, StreamPair) :-
1677
+ keep_connection(Address),
1678
+ get_time(Now),
1679
+ term_hash(Address, Hash),
1680
+ assertz(connection_pool(Hash, Address, StreamPair, Now)).
1681
+
1682
+ %! get_from_pool(+Address, -StreamPair) is nondet.
1683
+ %
1684
+ % Get an existing Keep-Alive connection to Address as StreamPair. The
1685
+ % caller relies on non-determinism of this predicate to try another
1686
+ % connection if the returned one is already closed by the peer. We
1687
+ % cannot rely on the non-determinism of retract/1 as that respects the
1688
+ % _logical update view_. Therefore, we must use retract/1 and commit
1689
+ % as retract/1 guarantees that the first retracted clause is not
1690
+ % already retracted.
1691
+
1692
+ get_from_pool(Address, StreamPair) :-
1693
+ term_hash(Address, Hash),
1694
+ repeat,
1695
+ ( retract(connection_pool(Hash, Address, StreamPair, _))
1696
+ -> true
1697
+ ; !,
1698
+ fail
1699
+ ).
1700
+
1701
+ %! keep_connection(+Address) is semidet.
1702
+ %
1703
+ % Succeeds if we want to keep the connection open. We currently keep a
1704
+ % maximum of `http:max_keep_alive_connections` connections waiting and
1705
+ % a maximum of `http:max_keep_alive_host_connections` waiting for the
1706
+ % same address. Connections older than `http:max_keep_alive_idle`
1707
+ % seconds are closed.
1708
+
1709
+ keep_connection(Address) :-
1710
+ setting(http:max_keep_alive_idle, Time),
1711
+ close_old_connections(Time),
1712
+ predicate_property(connection_pool(_,_,_,_), number_of_clauses(C)),
1713
+ setting(http:max_keep_alive_connections, MaxConnections),
1714
+ C =< MaxConnections,
1715
+ term_hash(Address, Hash),
1716
+ aggregate_all(count, connection_pool(Hash, Address, _, _), Count),
1717
+ setting(http:max_keep_alive_host_connections, MaxHostConnections),
1718
+ Count =< MaxHostConnections.
1719
+
1720
+ close_old_connections(Timeout) :-
1721
+ get_time(Now),
1722
+ Before is Now - Timeout,
1723
+ ( connection_gc_time(GC),
1724
+ GC > Before
1725
+ -> true
1726
+ ; ( retractall(connection_gc_time(_)),
1727
+ asserta(connection_gc_time(Now)),
1728
+ connection_pool(Hash, Address, StreamPair, Added),
1729
+ Added < Before,
1730
+ retract(connection_pool(Hash, Address, StreamPair, Added)),
1731
+ debug(http(connection),
1732
+ 'Closing inactive keep-alive to ~p', [Address]),
1733
+ close(StreamPair, [force(true)]),
1734
+ fail
1735
+ ; true
1736
+ )
1737
+ ).
1738
+
1739
+
1740
+ %! http_close_keep_alive(+Address) is det.
1741
+ %
1742
+ % Close all keep-alive connections matching Address. Address is of
1743
+ % the form Host:Port. In particular, http_close_keep_alive(_)
1744
+ % closes all currently known keep-alive connections.
1745
+
1746
+ http_close_keep_alive(Address) :-
1747
+ forall(get_from_pool(Address, StreamPair),
1748
+ close(StreamPair, [force(true)])).
1749
+
1750
+ %! keep_alive_error(+Error, +StreamPair)
1751
+ %
1752
+ % Deal with an error from reusing a keep-alive connection. If the
1753
+ % error is due to an I/O error or end-of-file, fail to backtrack over
1754
+ % get_from_pool/2. Otherwise it is a real error and we thus re-raise
1755
+ % it. In all cases we close StreamPair rather than returning it to the
1756
+ % pool as we may have done a partial read and thus be out of sync wrt.
1757
+ % the HTTP protocol.
1758
+
1759
+ keep_alive_error(error(keep_alive(closed), _), _) :-
1760
+ !,
1761
+ debug(http(connection), 'Keep-alive connection was closed', []),
1762
+ fail.
1763
+ keep_alive_error(error(io_error(_,_), _), StreamPair) :-
1764
+ !,
1765
+ close(StreamPair, [force(true)]),
1766
+ debug(http(connection), 'IO error on Keep-alive connection', []),
1767
+ fail.
1768
+ keep_alive_error(error(existence_error(http_reply, _URL), _), _) :-
1769
+ !,
1770
+ debug(http(connection), 'Got empty reply on Keep-alive connection', []),
1771
+ fail.
1772
+ keep_alive_error(Error, StreamPair) :-
1773
+ close(StreamPair, [force(true)]),
1774
+ throw(Error).
1775
+
1776
+
1777
+ /*******************************
1778
+ * HOOK DOCUMENTATION *
1779
+ *******************************/
1780
+
1781
+ %! http:open_options(+Parts, -Options) is nondet.
1782
+ %
1783
+ % This hook is used by the HTTP client library to define default
1784
+ % options based on the the broken-down request-URL. The following
1785
+ % example redirects all traffic, except for localhost over a proxy:
1786
+ %
1787
+ % ```
1788
+ % :- multifile
1789
+ % http:open_options/2.
1790
+ %
1791
+ % http:open_options(Parts, Options) :-
1792
+ % option(host(Host), Parts),
1793
+ % Host \== localhost,
1794
+ % Options = [proxy('proxy.local', 3128)].
1795
+ % ```
1796
+ %
1797
+ % This hook may return multiple solutions. The returned options
1798
+ % are combined using merge_options/3 where earlier solutions
1799
+ % overrule later solutions.
1800
+
1801
+ %! http:write_cookies(+Out, +Parts, +Options) is semidet.
1802
+ %
1803
+ % Emit a =|Cookie:|= header for the current connection. Out is an
1804
+ % open stream to the HTTP server, Parts is the broken-down request
1805
+ % (see uri_components/2) and Options is the list of options passed
1806
+ % to http_open. The predicate is called as if using ignore/1.
1807
+ %
1808
+ % @see complements http:update_cookies/3.
1809
+ % @see library(http/http_cookie) implements cookie handling on
1810
+ % top of these hooks.
1811
+
1812
+ %! http:update_cookies(+CookieData, +Parts, +Options) is semidet.
1813
+ %
1814
+ % Update the cookie database. CookieData is the value of the
1815
+ % =|Set-Cookie|= field, Parts is the broken-down request (see
1816
+ % uri_components/2) and Options is the list of options passed to
1817
+ % http_open.
1818
+ %
1819
+ % @see complements http:write_cookies
1820
+ % @see library(http/http_cookies) implements cookie handling on
1821
+ % top of these hooks.