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.
- package/README.md +29 -0
- package/bin/swipl +0 -0
- package/bin/swipl-win +0 -0
- package/build-manifest.json +70 -0
- package/lib/cmake/swipl/SWIPLConfig.cmake +21 -0
- package/lib/cmake/swipl/SWIPLConfigVersion.cmake +65 -0
- package/lib/cmake/swipl/SWIPLTargets-release.cmake +28 -0
- package/lib/cmake/swipl/SWIPLTargets.cmake +109 -0
- package/lib/swipl/ABI +1 -0
- package/lib/swipl/LICENSE +41 -0
- package/lib/swipl/README.md +116 -0
- package/lib/swipl/app/README.md +25 -0
- package/lib/swipl/app/app.pl +87 -0
- package/lib/swipl/app/pack.pl +350 -0
- package/lib/swipl/app/qlf.pl +521 -0
- package/lib/swipl/app/splfr.pl +177 -0
- package/lib/swipl/app/sys.pl +310 -0
- package/lib/swipl/bin/swipl.home +1 -0
- package/lib/swipl/boot/apply.pl +73 -0
- package/lib/swipl/boot/attvar.pl +344 -0
- package/lib/swipl/boot/autoload.pl +1226 -0
- package/lib/swipl/boot/bags.pl +283 -0
- package/lib/swipl/boot/dcg.pl +388 -0
- package/lib/swipl/boot/dicts.pl +193 -0
- package/lib/swipl/boot/dwim.pl +485 -0
- package/lib/swipl/boot/engines.pl +152 -0
- package/lib/swipl/boot/expand.pl +1628 -0
- package/lib/swipl/boot/gc.pl +65 -0
- package/lib/swipl/boot/history.pl +304 -0
- package/lib/swipl/boot/init.pl +4682 -0
- package/lib/swipl/boot/iri.pl +64 -0
- package/lib/swipl/boot/license.pl +390 -0
- package/lib/swipl/boot/load.pl +81 -0
- package/lib/swipl/boot/messages.pl +2521 -0
- package/lib/swipl/boot/packs.pl +306 -0
- package/lib/swipl/boot/predopts.pl +154 -0
- package/lib/swipl/boot/qlf.pl +141 -0
- package/lib/swipl/boot/rc.pl +263 -0
- package/lib/swipl/boot/syspred.pl +1634 -0
- package/lib/swipl/boot/tabling.pl +2321 -0
- package/lib/swipl/boot/threads.pl +42 -0
- package/lib/swipl/boot/toplevel.pl +2248 -0
- package/lib/swipl/boot/topvars.pl +261 -0
- package/lib/swipl/boot.prc +0 -0
- package/lib/swipl/cmake/swipl.cmake +131 -0
- package/lib/swipl/customize/README.md +23 -0
- package/lib/swipl/customize/edit +101 -0
- package/lib/swipl/customize/init.pl +164 -0
- package/lib/swipl/demo/README.md +33 -0
- package/lib/swipl/demo/likes.pl +44 -0
- package/lib/swipl/doc/packages/examples/chr/bool.chr +281 -0
- package/lib/swipl/doc/packages/examples/chr/chrdif.chr +87 -0
- package/lib/swipl/doc/packages/examples/chr/chrfreeze.chr +6 -0
- package/lib/swipl/doc/packages/examples/chr/family.chr +116 -0
- package/lib/swipl/doc/packages/examples/chr/fib.chr +24 -0
- package/lib/swipl/doc/packages/examples/chr/fibonacci.chr +31 -0
- package/lib/swipl/doc/packages/examples/chr/gcd.chr +28 -0
- package/lib/swipl/doc/packages/examples/chr/leq.chr +34 -0
- package/lib/swipl/doc/packages/examples/chr/listdom.chr +138 -0
- package/lib/swipl/doc/packages/examples/chr/primes.chr +29 -0
- package/lib/swipl/doc/packages/examples/http/README.md +98 -0
- package/lib/swipl/doc/packages/examples/http/calc.pl +92 -0
- package/lib/swipl/doc/packages/examples/http/demo_body.pl +232 -0
- package/lib/swipl/doc/packages/examples/http/demo_client.pl +165 -0
- package/lib/swipl/doc/packages/examples/http/demo_daemon.pl +38 -0
- package/lib/swipl/doc/packages/examples/http/demo_files.pl +50 -0
- package/lib/swipl/doc/packages/examples/http/demo_hello.pl +15 -0
- package/lib/swipl/doc/packages/examples/http/demo_login.pl +280 -0
- package/lib/swipl/doc/packages/examples/http/demo_openid.pl +177 -0
- package/lib/swipl/doc/packages/examples/http/demo_pwp.pl +20 -0
- package/lib/swipl/doc/packages/examples/http/demo_rest.pl +56 -0
- package/lib/swipl/doc/packages/examples/http/demo_threads.pl +52 -0
- package/lib/swipl/doc/packages/examples/http/linux-init-script +126 -0
- package/lib/swipl/doc/packages/examples/http/pwp/context.pwp +23 -0
- package/lib/swipl/doc/packages/examples/http/pwp/index.pwp +25 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp1.pwp +12 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp2.pwp +10 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp3.pwp +25 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp4.pwp +23 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp5.pwp +11 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp6.pwp +3 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp7.pwp +3 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwp8.pwp +3 -0
- package/lib/swipl/doc/packages/examples/http/pwp/pwpdb.pl +12 -0
- package/lib/swipl/doc/packages/examples/http/systemd-script.service +18 -0
- package/lib/swipl/doc/packages/examples/http/upstart-script.conf +20 -0
- package/lib/swipl/doc/packages/examples/plunit/read.pl +17 -0
- package/lib/swipl/doc/packages/examples/plunit/simple.pl +33 -0
- package/lib/swipl/doc/packages/examples/ssl/client.pl +83 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/README +52 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.p12 +0 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.pem +85 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/client/client-key.pem +30 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/cacert.pem +82 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/careq.pem +18 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/crlnumber +1 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt +3 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt.attr +1 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7C.pem +85 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7D.pem +85 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/private/cakey.pem +30 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/serial +1 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/server/server-cert.pem +85 -0
- package/lib/swipl/doc/packages/examples/ssl/etc/server/server-key.pem +30 -0
- package/lib/swipl/doc/packages/examples/ssl/https.pl +267 -0
- package/lib/swipl/doc/packages/examples/ssl/server.pl +106 -0
- package/lib/swipl/include/SWI-Prolog.h +1544 -0
- package/lib/swipl/include/SWI-Stream.h +537 -0
- package/lib/swipl/include/Yap/YapInterface.h +283 -0
- package/lib/swipl/include/sicstus/sicstus.h +452 -0
- package/lib/swipl/lib/x86_64-linux/cgi.so +0 -0
- package/lib/swipl/lib/x86_64-linux/crypt.so +0 -0
- package/lib/swipl/lib/x86_64-linux/crypto4pl.so +0 -0
- package/lib/swipl/lib/x86_64-linux/double_metaphone.so +0 -0
- package/lib/swipl/lib/x86_64-linux/files.so +0 -0
- package/lib/swipl/lib/x86_64-linux/hashstream.so +0 -0
- package/lib/swipl/lib/x86_64-linux/http_stream.so +0 -0
- package/lib/swipl/lib/x86_64-linux/isub.so +0 -0
- package/lib/swipl/lib/x86_64-linux/json.so +0 -0
- package/lib/swipl/lib/x86_64-linux/libswipl.so.10 +0 -0
- package/lib/swipl/lib/x86_64-linux/mallocinfo.so +0 -0
- package/lib/swipl/lib/x86_64-linux/md54pl.so +0 -0
- package/lib/swipl/lib/x86_64-linux/memfile.so +0 -0
- package/lib/swipl/lib/x86_64-linux/ntriples.so +0 -0
- package/lib/swipl/lib/x86_64-linux/pcre4pl.so +0 -0
- package/lib/swipl/lib/x86_64-linux/porter_stem.so +0 -0
- package/lib/swipl/lib/x86_64-linux/process.so +0 -0
- package/lib/swipl/lib/x86_64-linux/prolog_stream.so +0 -0
- package/lib/swipl/lib/x86_64-linux/rdf_db.so +0 -0
- package/lib/swipl/lib/x86_64-linux/readutil.so +0 -0
- package/lib/swipl/lib/x86_64-linux/rlimit.so +0 -0
- package/lib/swipl/lib/x86_64-linux/sched.so +0 -0
- package/lib/swipl/lib/x86_64-linux/sgml2pl.so +0 -0
- package/lib/swipl/lib/x86_64-linux/sha4pl.so +0 -0
- package/lib/swipl/lib/x86_64-linux/snowball.so +0 -0
- package/lib/swipl/lib/x86_64-linux/socket.so +0 -0
- package/lib/swipl/lib/x86_64-linux/ssl4pl.so +0 -0
- package/lib/swipl/lib/x86_64-linux/streaminfo.so +0 -0
- package/lib/swipl/lib/x86_64-linux/syslog.so +0 -0
- package/lib/swipl/lib/x86_64-linux/time.so +0 -0
- package/lib/swipl/lib/x86_64-linux/turtle.so +0 -0
- package/lib/swipl/lib/x86_64-linux/uid.so +0 -0
- package/lib/swipl/lib/x86_64-linux/unix.so +0 -0
- package/lib/swipl/lib/x86_64-linux/uri.so +0 -0
- package/lib/swipl/lib/x86_64-linux/websocket.so +0 -0
- package/lib/swipl/lib/x86_64-linux/zlib4pl.so +0 -0
- package/lib/swipl/library/INDEX.pl +1020 -0
- package/lib/swipl/library/aggregate.pl +755 -0
- package/lib/swipl/library/ansi_term.pl +653 -0
- package/lib/swipl/library/apply.pl +426 -0
- package/lib/swipl/library/apply_macros.pl +497 -0
- package/lib/swipl/library/arithmetic.pl +320 -0
- package/lib/swipl/library/assoc.pl +522 -0
- package/lib/swipl/library/atom.pl +169 -0
- package/lib/swipl/library/backcomp.pl +688 -0
- package/lib/swipl/library/base32.pl +291 -0
- package/lib/swipl/library/base64.pl +441 -0
- package/lib/swipl/library/broadcast.pl +161 -0
- package/lib/swipl/library/build/cmake.pl +137 -0
- package/lib/swipl/library/build/conan.pl +232 -0
- package/lib/swipl/library/build/make.pl +168 -0
- package/lib/swipl/library/build/tools.pl +825 -0
- package/lib/swipl/library/charsio.pl +195 -0
- package/lib/swipl/library/check.pl +1097 -0
- package/lib/swipl/library/check_installation.pl +912 -0
- package/lib/swipl/library/clp/INDEX.pl +157 -0
- package/lib/swipl/library/clp/bounds.pl +2036 -0
- package/lib/swipl/library/clp/clp_distinct.pl +263 -0
- package/lib/swipl/library/clp/clp_events.pl +92 -0
- package/lib/swipl/library/clp/clpb.pl +1896 -0
- package/lib/swipl/library/clp/clpfd.pl +7906 -0
- package/lib/swipl/library/clp/simplex.pl +1580 -0
- package/lib/swipl/library/codesio.pl +182 -0
- package/lib/swipl/library/coinduction.pl +183 -0
- package/lib/swipl/library/console_input.pl +289 -0
- package/lib/swipl/library/csv.pl +611 -0
- package/lib/swipl/library/ctypes.pl +161 -0
- package/lib/swipl/library/date.pl +370 -0
- package/lib/swipl/library/dcg/INDEX.pl +35 -0
- package/lib/swipl/library/dcg/basics.pl +514 -0
- package/lib/swipl/library/dcg/high_order.pl +227 -0
- package/lib/swipl/library/debug.pl +499 -0
- package/lib/swipl/library/dialect/bim.pl +186 -0
- package/lib/swipl/library/dialect/commons.pl +77 -0
- package/lib/swipl/library/dialect/eclipse/test_util_iso.pl +355 -0
- package/lib/swipl/library/dialect/hprolog/format.pl +44 -0
- package/lib/swipl/library/dialect/hprolog.pl +314 -0
- package/lib/swipl/library/dialect/ifprolog.pl +1317 -0
- package/lib/swipl/library/dialect/iso/iso_predicates.pl +277 -0
- package/lib/swipl/library/dialect/sicstus/README.TXT +1 -0
- package/lib/swipl/library/dialect/sicstus/arrays.pl +121 -0
- package/lib/swipl/library/dialect/sicstus/block.pl +278 -0
- package/lib/swipl/library/dialect/sicstus/lists.pl +167 -0
- package/lib/swipl/library/dialect/sicstus/ordsets.pl +52 -0
- package/lib/swipl/library/dialect/sicstus/sockets.pl +174 -0
- package/lib/swipl/library/dialect/sicstus/system.pl +262 -0
- package/lib/swipl/library/dialect/sicstus/terms.pl +57 -0
- package/lib/swipl/library/dialect/sicstus/timeout.pl +92 -0
- package/lib/swipl/library/dialect/sicstus.pl +582 -0
- package/lib/swipl/library/dialect/sicstus4/aggregate.pl +60 -0
- package/lib/swipl/library/dialect/sicstus4/between.pl +62 -0
- package/lib/swipl/library/dialect/sicstus4/clpfd.pl +75 -0
- package/lib/swipl/library/dialect/sicstus4/file_systems.pl +413 -0
- package/lib/swipl/library/dialect/sicstus4/lists.pl +274 -0
- package/lib/swipl/library/dialect/sicstus4/ordsets.pl +54 -0
- package/lib/swipl/library/dialect/sicstus4/samsort.pl +86 -0
- package/lib/swipl/library/dialect/sicstus4/sets.pl +124 -0
- package/lib/swipl/library/dialect/sicstus4/sockets.pl +164 -0
- package/lib/swipl/library/dialect/sicstus4/system.pl +80 -0
- package/lib/swipl/library/dialect/sicstus4/terms.pl +89 -0
- package/lib/swipl/library/dialect/sicstus4/timeout.pl +43 -0
- package/lib/swipl/library/dialect/sicstus4/types.pl +103 -0
- package/lib/swipl/library/dialect/sicstus4.pl +200 -0
- package/lib/swipl/library/dialect/swi/syspred_options.pl +250 -0
- package/lib/swipl/library/dialect/xsb/README.md +20 -0
- package/lib/swipl/library/dialect/xsb/basics.pl +318 -0
- package/lib/swipl/library/dialect/xsb/consult.pl +87 -0
- package/lib/swipl/library/dialect/xsb/curr_sym.pl +57 -0
- package/lib/swipl/library/dialect/xsb/error_handler.pl +111 -0
- package/lib/swipl/library/dialect/xsb/gensym.pl +53 -0
- package/lib/swipl/library/dialect/xsb/gpp.pl +138 -0
- package/lib/swipl/library/dialect/xsb/intern.pl +48 -0
- package/lib/swipl/library/dialect/xsb/lists.pl +47 -0
- package/lib/swipl/library/dialect/xsb/machine.pl +295 -0
- package/lib/swipl/library/dialect/xsb/ordsets.pl +53 -0
- package/lib/swipl/library/dialect/xsb/setof.pl +54 -0
- package/lib/swipl/library/dialect/xsb/source.pl +308 -0
- package/lib/swipl/library/dialect/xsb/standard.pl +169 -0
- package/lib/swipl/library/dialect/xsb/storage.pl +70 -0
- package/lib/swipl/library/dialect/xsb/string.pl +76 -0
- package/lib/swipl/library/dialect/xsb/thread.pl +62 -0
- package/lib/swipl/library/dialect/xsb/timed_call.pl +158 -0
- package/lib/swipl/library/dialect/xsb.pl +688 -0
- package/lib/swipl/library/dialect/yap/README.TXT +8 -0
- package/lib/swipl/library/dialect/yap.pl +283 -0
- package/lib/swipl/library/dialect.pl +123 -0
- package/lib/swipl/library/dicts.pl +399 -0
- package/lib/swipl/library/dif.pl +426 -0
- package/lib/swipl/library/edinburgh.pl +138 -0
- package/lib/swipl/library/edit.pl +783 -0
- package/lib/swipl/library/error.pl +495 -0
- package/lib/swipl/library/exceptions.pl +231 -0
- package/lib/swipl/library/explain.pl +543 -0
- package/lib/swipl/library/ext/RDF/INDEX.pl +25 -0
- package/lib/swipl/library/ext/RDF/rdf.pl +450 -0
- package/lib/swipl/library/ext/RDF/rdf_parser.pl +649 -0
- package/lib/swipl/library/ext/RDF/rdf_triple.pl +448 -0
- package/lib/swipl/library/ext/RDF/rdf_write.pl +701 -0
- package/lib/swipl/library/ext/RDF/rewrite_term.pl +177 -0
- package/lib/swipl/library/ext/chr/chr/a_star.pl +80 -0
- package/lib/swipl/library/ext/chr/chr/binomialheap.pl +142 -0
- package/lib/swipl/library/ext/chr/chr/builtins.pl +629 -0
- package/lib/swipl/library/ext/chr/chr/chr_compiler_errors.pl +179 -0
- package/lib/swipl/library/ext/chr/chr/chr_compiler_options.pl +382 -0
- package/lib/swipl/library/ext/chr/chr/chr_compiler_utility.pl +338 -0
- package/lib/swipl/library/ext/chr/chr/chr_debug.pl +59 -0
- package/lib/swipl/library/ext/chr/chr/chr_hashtable_store.pl +425 -0
- package/lib/swipl/library/ext/chr/chr/chr_integertable_store.pl +139 -0
- package/lib/swipl/library/ext/chr/chr/chr_messages.pl +176 -0
- package/lib/swipl/library/ext/chr/chr/chr_op.pl +53 -0
- package/lib/swipl/library/ext/chr/chr/chr_runtime.pl +1016 -0
- package/lib/swipl/library/ext/chr/chr/chr_translate.pl +25424 -0
- package/lib/swipl/library/ext/chr/chr/clean_code.pl +252 -0
- package/lib/swipl/library/ext/chr/chr/find.pl +78 -0
- package/lib/swipl/library/ext/chr/chr/guard_entailment.pl +19432 -0
- package/lib/swipl/library/ext/chr/chr/listmap.pl +108 -0
- package/lib/swipl/library/ext/chr/chr/pairlist.pl +78 -0
- package/lib/swipl/library/ext/chr/chr.pl +686 -0
- package/lib/swipl/library/ext/clib/INDEX.pl +158 -0
- package/lib/swipl/library/ext/clib/cgi.pl +86 -0
- package/lib/swipl/library/ext/clib/crypt.pl +44 -0
- package/lib/swipl/library/ext/clib/filesex.pl +644 -0
- package/lib/swipl/library/ext/clib/hash_stream.pl +119 -0
- package/lib/swipl/library/ext/clib/mallocinfo.pl +138 -0
- package/lib/swipl/library/ext/clib/md5.pl +68 -0
- package/lib/swipl/library/ext/clib/memfile.pl +61 -0
- package/lib/swipl/library/ext/clib/process.pl +573 -0
- package/lib/swipl/library/ext/clib/prolog_server.pl +157 -0
- package/lib/swipl/library/ext/clib/prolog_stream.pl +80 -0
- package/lib/swipl/library/ext/clib/rlimit.pl +54 -0
- package/lib/swipl/library/ext/clib/sched.pl +85 -0
- package/lib/swipl/library/ext/clib/sha.pl +162 -0
- package/lib/swipl/library/ext/clib/socket.pl +1102 -0
- package/lib/swipl/library/ext/clib/streaminfo.pl +103 -0
- package/lib/swipl/library/ext/clib/streampool.pl +136 -0
- package/lib/swipl/library/ext/clib/syslog.pl +180 -0
- package/lib/swipl/library/ext/clib/time.pl +194 -0
- package/lib/swipl/library/ext/clib/udp_broadcast.pl +975 -0
- package/lib/swipl/library/ext/clib/uid.pl +220 -0
- package/lib/swipl/library/ext/clib/unix.pl +317 -0
- package/lib/swipl/library/ext/clib/uri.pl +530 -0
- package/lib/swipl/library/ext/clib/uuid.pl +207 -0
- package/lib/swipl/library/ext/http/http/INDEX.pl +266 -0
- package/lib/swipl/library/ext/http/http/README.md +34 -0
- package/lib/swipl/library/ext/http/http/ax.pl +189 -0
- package/lib/swipl/library/ext/http/http/dcg_basics.pl +49 -0
- package/lib/swipl/library/ext/http/http/graphql.pl +1367 -0
- package/lib/swipl/library/ext/http/http/html_decl.pl +416 -0
- package/lib/swipl/library/ext/http/http/html_head.pl +654 -0
- package/lib/swipl/library/ext/http/http/html_quasiquotations.pl +120 -0
- package/lib/swipl/library/ext/http/http/html_write.pl +1425 -0
- package/lib/swipl/library/ext/http/http/htmx.pl +139 -0
- package/lib/swipl/library/ext/http/http/http_authenticate.pl +364 -0
- package/lib/swipl/library/ext/http/http/http_client.pl +412 -0
- package/lib/swipl/library/ext/http/http/http_cookie.pl +245 -0
- package/lib/swipl/library/ext/http/http/http_cors.pl +176 -0
- package/lib/swipl/library/ext/http/http/http_digest.pl +820 -0
- package/lib/swipl/library/ext/http/http/http_dirindex.pl +296 -0
- package/lib/swipl/library/ext/http/http/http_dispatch.pl +1552 -0
- package/lib/swipl/library/ext/http/http/http_dyn_workers.pl +214 -0
- package/lib/swipl/library/ext/http/http/http_error.pl +126 -0
- package/lib/swipl/library/ext/http/http/http_exception.pl +225 -0
- package/lib/swipl/library/ext/http/http/http_files.pl +163 -0
- package/lib/swipl/library/ext/http/http/http_header.pl +2820 -0
- package/lib/swipl/library/ext/http/http/http_hook.pl +106 -0
- package/lib/swipl/library/ext/http/http/http_host.pl +204 -0
- package/lib/swipl/library/ext/http/http/http_load.pl +113 -0
- package/lib/swipl/library/ext/http/http/http_log.pl +738 -0
- package/lib/swipl/library/ext/http/http/http_multipart_plugin.pl +232 -0
- package/lib/swipl/library/ext/http/http/http_open.pl +1821 -0
- package/lib/swipl/library/ext/http/http/http_openid.pl +1561 -0
- package/lib/swipl/library/ext/http/http/http_parameters.pl +504 -0
- package/lib/swipl/library/ext/http/http/http_path.pl +380 -0
- package/lib/swipl/library/ext/http/http/http_proxy.pl +80 -0
- package/lib/swipl/library/ext/http/http/http_pwp.pl +351 -0
- package/lib/swipl/library/ext/http/http/http_redis_plugin.pl +332 -0
- package/lib/swipl/library/ext/http/http/http_server.pl +105 -0
- package/lib/swipl/library/ext/http/http/http_server_files.pl +126 -0
- package/lib/swipl/library/ext/http/http/http_server_health.pl +186 -0
- package/lib/swipl/library/ext/http/http/http_session.pl +1040 -0
- package/lib/swipl/library/ext/http/http/http_sgml_plugin.pl +137 -0
- package/lib/swipl/library/ext/http/http/http_stream.pl +376 -0
- package/lib/swipl/library/ext/http/http/http_unix_daemon.pl +989 -0
- package/lib/swipl/library/ext/http/http/http_wrapper.pl +559 -0
- package/lib/swipl/library/ext/http/http/hub.pl +600 -0
- package/lib/swipl/library/ext/http/http/js_write.pl +461 -0
- package/lib/swipl/library/ext/http/http/mimepack.pl +224 -0
- package/lib/swipl/library/ext/http/http/mimetype.pl +276 -0
- package/lib/swipl/library/ext/http/http/term_html.pl +797 -0
- package/lib/swipl/library/ext/http/http/thread_httpd.pl +1181 -0
- package/lib/swipl/library/ext/http/http/web/css/dirindex.css +24 -0
- package/lib/swipl/library/ext/http/http/web/css/openid.css +57 -0
- package/lib/swipl/library/ext/http/http/web/icons/back.png +0 -0
- package/lib/swipl/library/ext/http/http/web/icons/c.png +0 -0
- package/lib/swipl/library/ext/http/http/web/icons/compressed.png +0 -0
- package/lib/swipl/library/ext/http/http/web/icons/folder.png +0 -0
- package/lib/swipl/library/ext/http/http/web/icons/generic.png +0 -0
- package/lib/swipl/library/ext/http/http/web/icons/layout.png +0 -0
- package/lib/swipl/library/ext/http/http/web/icons/openid-logo-square.png +0 -0
- package/lib/swipl/library/ext/http/http/web/icons/openid-logo-tiny.png +0 -0
- package/lib/swipl/library/ext/http/http/websocket.pl +729 -0
- package/lib/swipl/library/ext/http/http/yadis.pl +205 -0
- package/lib/swipl/library/ext/json/INDEX.pl +36 -0
- package/lib/swipl/library/ext/json/http/INDEX.pl +16 -0
- package/lib/swipl/library/ext/json/http/http_json.pl +490 -0
- package/lib/swipl/library/ext/json/http/js_grammar.pl +53 -0
- package/lib/swipl/library/ext/json/http/json.pl +48 -0
- package/lib/swipl/library/ext/json/http/json_convert.pl +48 -0
- package/lib/swipl/library/ext/json/json.pl +1215 -0
- package/lib/swipl/library/ext/json/json_convert.pl +681 -0
- package/lib/swipl/library/ext/json/json_grammar.pl +349 -0
- package/lib/swipl/library/ext/json/json_rpc_client.pl +423 -0
- package/lib/swipl/library/ext/json/json_rpc_common.pl +104 -0
- package/lib/swipl/library/ext/json/json_rpc_server.pl +489 -0
- package/lib/swipl/library/ext/json/json_schema.pl +2111 -0
- package/lib/swipl/library/ext/nlp/INDEX.pl +15 -0
- package/lib/swipl/library/ext/nlp/double_metaphone.pl +86 -0
- package/lib/swipl/library/ext/nlp/isub.pl +162 -0
- package/lib/swipl/library/ext/nlp/porter_stem.pl +50 -0
- package/lib/swipl/library/ext/nlp/snowball.pl +97 -0
- package/lib/swipl/library/ext/pcre/INDEX.pl +17 -0
- package/lib/swipl/library/ext/pcre/pcre.pl +974 -0
- package/lib/swipl/library/ext/plunit/INDEX.pl +20 -0
- package/lib/swipl/library/ext/plunit/plunit.pl +2631 -0
- package/lib/swipl/library/ext/plunit/test_wizard.pl +249 -0
- package/lib/swipl/library/ext/semweb/semweb/INDEX.pl +288 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf11.pl +2106 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf11_containers.pl +299 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_cache.pl +278 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_compare.pl +132 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_db.pl +3468 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_edit.pl +1145 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_http_plugin.pl +274 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_library.pl +1021 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_litindex.pl +1035 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_ntriples.pl +422 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_persistency.pl +1576 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_portray.pl +182 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_prefixes.pl +681 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_sandbox.pl +153 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_turtle.pl +54 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_turtle_write.pl +1587 -0
- package/lib/swipl/library/ext/semweb/semweb/rdf_zlib_plugin.pl +52 -0
- package/lib/swipl/library/ext/semweb/semweb/rdfa.pl +1510 -0
- package/lib/swipl/library/ext/semweb/semweb/rdfs.pl +475 -0
- package/lib/swipl/library/ext/semweb/semweb/sparql_client.pl +643 -0
- package/lib/swipl/library/ext/semweb/semweb/turtle.pl +455 -0
- package/lib/swipl/library/ext/sgml/DTD/HTML4.dcl +88 -0
- package/lib/swipl/library/ext/sgml/DTD/HTML4.dtd +1092 -0
- package/lib/swipl/library/ext/sgml/DTD/HTML4.soc +6 -0
- package/lib/swipl/library/ext/sgml/DTD/HTML5.dtd +906 -0
- package/lib/swipl/library/ext/sgml/DTD/HTMLlat1.ent +195 -0
- package/lib/swipl/library/ext/sgml/DTD/HTMLspec.ent +77 -0
- package/lib/swipl/library/ext/sgml/DTD/HTMLsym.ent +241 -0
- package/lib/swipl/library/ext/sgml/INDEX.pl +64 -0
- package/lib/swipl/library/ext/sgml/c14n2.pl +203 -0
- package/lib/swipl/library/ext/sgml/iso_639.pl +631 -0
- package/lib/swipl/library/ext/sgml/pwp.pl +705 -0
- package/lib/swipl/library/ext/sgml/sgml.pl +815 -0
- package/lib/swipl/library/ext/sgml/sgml_write.pl +1003 -0
- package/lib/swipl/library/ext/sgml/xpath.pl +620 -0
- package/lib/swipl/library/ext/sgml/xsdp_types.pl +232 -0
- package/lib/swipl/library/ext/ssl/INDEX.pl +60 -0
- package/lib/swipl/library/ext/ssl/crypto.pl +1015 -0
- package/lib/swipl/library/ext/ssl/http/INDEX.pl +5 -0
- package/lib/swipl/library/ext/ssl/http/http_ssl_plugin.pl +296 -0
- package/lib/swipl/library/ext/ssl/saml.pl +609 -0
- package/lib/swipl/library/ext/ssl/ssl.pl +647 -0
- package/lib/swipl/library/ext/ssl/xmldsig.pl +391 -0
- package/lib/swipl/library/ext/ssl/xmlenc.pl +354 -0
- package/lib/swipl/library/ext/zlib/INDEX.pl +8 -0
- package/lib/swipl/library/ext/zlib/zlib.pl +112 -0
- package/lib/swipl/library/fastrw.pl +105 -0
- package/lib/swipl/library/files.pl +80 -0
- package/lib/swipl/library/gensym.pl +111 -0
- package/lib/swipl/library/git.pl +1027 -0
- package/lib/swipl/library/hashtable.pl +365 -0
- package/lib/swipl/library/heaps.pl +252 -0
- package/lib/swipl/library/hotfix.pl +239 -0
- package/lib/swipl/library/http/jquery.pl +103 -0
- package/lib/swipl/library/http/web/js/jquery-3.6.0.min.js +2 -0
- package/lib/swipl/library/increval.pl +217 -0
- package/lib/swipl/library/intercept.pl +263 -0
- package/lib/swipl/library/iostream.pl +237 -0
- package/lib/swipl/library/iri_scheme/file.pl +70 -0
- package/lib/swipl/library/lazy_lists.pl +481 -0
- package/lib/swipl/library/listing.pl +1362 -0
- package/lib/swipl/library/lists.pl +860 -0
- package/lib/swipl/library/lynx/INDEX.pl +14 -0
- package/lib/swipl/library/lynx/format.pl +380 -0
- package/lib/swipl/library/lynx/html_style.pl +129 -0
- package/lib/swipl/library/lynx/html_text.pl +865 -0
- package/lib/swipl/library/lynx/pldoc_style.pl +87 -0
- package/lib/swipl/library/macros.pl +451 -0
- package/lib/swipl/library/main.pl +1200 -0
- package/lib/swipl/library/make.pl +197 -0
- package/lib/swipl/library/modules.pl +129 -0
- package/lib/swipl/library/nb_rbtrees.pl +267 -0
- package/lib/swipl/library/nb_set.pl +203 -0
- package/lib/swipl/library/obfuscate.pl +115 -0
- package/lib/swipl/library/occurs.pl +241 -0
- package/lib/swipl/library/operators.pl +182 -0
- package/lib/swipl/library/option.pl +388 -0
- package/lib/swipl/library/optparse.pl +966 -0
- package/lib/swipl/library/ordsets.pl +518 -0
- package/lib/swipl/library/oset.pl +160 -0
- package/lib/swipl/library/pairs.pl +187 -0
- package/lib/swipl/library/persistency.pl +713 -0
- package/lib/swipl/library/pio.pl +47 -0
- package/lib/swipl/library/portray_text.pl +277 -0
- package/lib/swipl/library/pprint.pl +940 -0
- package/lib/swipl/library/predicate_options.pl +975 -0
- package/lib/swipl/library/prolog_autoload.pl +233 -0
- package/lib/swipl/library/prolog_breakpoints.pl +396 -0
- package/lib/swipl/library/prolog_clause.pl +1104 -0
- package/lib/swipl/library/prolog_code.pl +416 -0
- package/lib/swipl/library/prolog_codewalk.pl +1248 -0
- package/lib/swipl/library/prolog_colour.pl +3301 -0
- package/lib/swipl/library/prolog_colour.qlf +0 -0
- package/lib/swipl/library/prolog_config.pl +154 -0
- package/lib/swipl/library/prolog_coverage.pl +1333 -0
- package/lib/swipl/library/prolog_debug.pl +404 -0
- package/lib/swipl/library/prolog_deps.pl +675 -0
- package/lib/swipl/library/prolog_evaluable.pl +234 -0
- package/lib/swipl/library/prolog_format.pl +219 -0
- package/lib/swipl/library/prolog_history.pl +161 -0
- package/lib/swipl/library/prolog_jiti.pl +333 -0
- package/lib/swipl/library/prolog_locale.pl +61 -0
- package/lib/swipl/library/prolog_metainference.pl +307 -0
- package/lib/swipl/library/prolog_pack.pl +3906 -0
- package/lib/swipl/library/prolog_profile.pl +346 -0
- package/lib/swipl/library/prolog_qlfmake.pl +533 -0
- package/lib/swipl/library/prolog_source.pl +1188 -0
- package/lib/swipl/library/prolog_stack.pl +794 -0
- package/lib/swipl/library/prolog_trace.pl +365 -0
- package/lib/swipl/library/prolog_versions.pl +347 -0
- package/lib/swipl/library/prolog_wrap.pl +164 -0
- package/lib/swipl/library/prolog_xref.pl +3090 -0
- package/lib/swipl/library/prolog_xref.qlf +0 -0
- package/lib/swipl/library/pure_input.pl +266 -0
- package/lib/swipl/library/qpforeign.pl +695 -0
- package/lib/swipl/library/qsave.pl +1516 -0
- package/lib/swipl/library/quasi_quotations.pl +300 -0
- package/lib/swipl/library/quintus.pl +437 -0
- package/lib/swipl/library/random.pl +459 -0
- package/lib/swipl/library/rbtrees.pl +1153 -0
- package/lib/swipl/library/readln.pl +256 -0
- package/lib/swipl/library/readutil.pl +304 -0
- package/lib/swipl/library/record.pl +544 -0
- package/lib/swipl/library/rwlocks.pl +172 -0
- package/lib/swipl/library/sandbox.pl +1384 -0
- package/lib/swipl/library/semweb/dc.rdfs +198 -0
- package/lib/swipl/library/semweb/eor.rdfs +95 -0
- package/lib/swipl/library/semweb/owl.owl +287 -0
- package/lib/swipl/library/semweb/rdf_library.ttl +90 -0
- package/lib/swipl/library/semweb/rdfs.rdfs +251 -0
- package/lib/swipl/library/settings.pl +755 -0
- package/lib/swipl/library/shell.pl +462 -0
- package/lib/swipl/library/shlib.pl +670 -0
- package/lib/swipl/library/solution_sequences.pl +439 -0
- package/lib/swipl/library/sort.pl +119 -0
- package/lib/swipl/library/statistics.pl +541 -0
- package/lib/swipl/library/streams.pl +118 -0
- package/lib/swipl/library/strings.pl +469 -0
- package/lib/swipl/library/system.pl +95 -0
- package/lib/swipl/library/tables.pl +381 -0
- package/lib/swipl/library/tableutil.pl +771 -0
- package/lib/swipl/library/tabling.pl +42 -0
- package/lib/swipl/library/terms.pl +457 -0
- package/lib/swipl/library/theme/auto.pl +61 -0
- package/lib/swipl/library/theme/dark.pl +455 -0
- package/lib/swipl/library/theme/light.pl +45 -0
- package/lib/swipl/library/thread.pl +810 -0
- package/lib/swipl/library/thread_pool.pl +508 -0
- package/lib/swipl/library/threadutil.pl +574 -0
- package/lib/swipl/library/tty.pl +316 -0
- package/lib/swipl/library/ugraphs.pl +644 -0
- package/lib/swipl/library/unicode/INDEX.pl +6 -0
- package/lib/swipl/library/unicode/blocks.pl +414 -0
- package/lib/swipl/library/url.pl +1129 -0
- package/lib/swipl/library/utf8.pl +145 -0
- package/lib/swipl/library/varnumbers.pl +221 -0
- package/lib/swipl/library/vm.pl +251 -0
- package/lib/swipl/library/wfs.pl +205 -0
- package/lib/swipl/library/when.pl +263 -0
- package/lib/swipl/library/writef.pl +278 -0
- package/lib/swipl/library/www_browser.pl +251 -0
- package/lib/swipl/library/yall.pl +574 -0
- package/lib/swipl/library/zip.pl +211 -0
- package/lib/swipl/swipl.home +1 -0
- package/lib/vendor/libcrypto.so.3 +0 -0
- package/lib/vendor/libpcre2-8.so.0 +0 -0
- package/lib/vendor/libssl.so.3 +0 -0
- package/lib/vendor/libz.so.1 +0 -0
- package/licenses/OpenSSL-LICENSE.txt +177 -0
- package/licenses/PCRE2-COPYING +5 -0
- package/licenses/README.md +12 -0
- package/licenses/SWI-Prolog-LICENSE +41 -0
- package/licenses/zlib-LICENSE +22 -0
- 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.
|