/lib/web.arc

http://github.com/alimoeeny/arc · Unknown · 129 lines · 110 code · 19 blank · 0 comment · 0 complexity · e590a8613ef9557f7fd06fe03c670916 MD5 · raw file

  1. ; written by Mark Huetsch
  2. ; same license as Arc
  3. ($ (require openssl))
  4. ($ (xdef ssl-connect (lambda (host port)
  5. (ar-init-socket
  6. (lambda () (ssl-connect host port))))))
  7. (load "lib/re.arc")
  8. (load "lib/util.arc")
  9. (def parse-server-cookies (s)
  10. (map [map trim _]
  11. (map [matchsplit "=" _]
  12. (tokens s #\;))))
  13. (def read-headers ((o s (stdin)))
  14. (accum a
  15. (whiler line (readline s) blank
  16. (a line))))
  17. (def parse-server-headers (lines)
  18. (let http-response (tokens car.lines)
  19. (list
  20. (map http-response '(0 1 2))
  21. (some [aand (begins-rest "Set-Cookie:" _) parse-server-cookies.it]
  22. cdr.lines))))
  23. (def args->query-string (args)
  24. (if args
  25. (let equals-list (map [joinstr _ "="] (pair (map [coerce _ 'string] args)))
  26. (joinstr equals-list "&"))
  27. ""))
  28. (def parse-url (url)
  29. (withs ((resource url) (split-by "://" (ensure-resource:strip-after url "#"))
  30. (host+port path+query) (split-by "/" url)
  31. (host portstr) (split-by ":" host+port)
  32. (path query) (split-by "?" path+query))
  33. (obj resource resource
  34. host host
  35. port (or (only.int portstr) default-port.resource)
  36. filename path
  37. query query)))
  38. ; TODO: only handles https for now
  39. (def default-port(resource)
  40. (if (is resource "https")
  41. 443
  42. 80))
  43. (def encode-cookie (o)
  44. (let joined-list (map [joinstr _ #\=] (tablist o))
  45. (+ "Cookie: "
  46. (if (len> joined-list 1)
  47. (reduce [+ _1 "; " _2] joined-list)
  48. (car joined-list))
  49. ";")))
  50. ; TODO this isn't very pretty
  51. (def get-or-post-url (url (o args) (o method "GET") (o cookie))
  52. (withs (method (upcase method)
  53. parsed-url (parse-url url)
  54. args-query-string (args->query-string args)
  55. full-args (joinstr (list args-query-string (parsed-url 'query)) "&")
  56. request-path (+ "/" (parsed-url 'filename)
  57. (if (and (is method "GET") (> (len full-args) 0))
  58. (+ "?" full-args)))
  59. header-components (list (+ method " " request-path " HTTP/1.0")
  60. (+ "Host: " (parsed-url 'host))
  61. "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; uk; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2"))
  62. (when (is method "POST")
  63. (pushend (+ "Content-Length: "
  64. (len (utf-8-bytes full-args)))
  65. header-components)
  66. (pushend "Content-Type: application/x-www-form-urlencoded"
  67. header-components))
  68. (when cookie
  69. (push (encode-cookie cookie) header-components))
  70. (withs (header (reduce [+ _1 "\r\n" _2] header-components)
  71. body (if (is method "POST") (+ full-args "\r\n"))
  72. request-message (+ header "\r\n\r\n" body))
  73. (let (in out) (if (is "https" (parsed-url 'resource))
  74. (ssl-connect (parsed-url 'host) (parsed-url 'port))
  75. (socket-connect (parsed-url 'host) (parsed-url 'port)))
  76. (disp request-message out)
  77. (with (header (parse-server-headers (read-headers in))
  78. body (tostring (whilet line (readline in) (prn line))))
  79. (close in out)
  80. (list header body))))))
  81. (def get-url (url)
  82. ((get-or-post-url url) 1))
  83. (def post-url (url args)
  84. ((get-or-post-url url args "POST") 1))
  85. (def split-by(delim s)
  86. (iflet idx (posmatch delim s)
  87. (list (cut s 0 idx) (cut s (+ idx len.delim)))
  88. (list s nil)))
  89. (def strip-after(s delim)
  90. ((split-by delim s) 0))
  91. (def ensure-resource(url)
  92. (if (posmatch "://" url)
  93. url
  94. (+ "http://" url)))
  95. (def google (q)
  96. (get-url (+ "www.google.com/search?q=" (urlencode q))))
  97. ; just some preliminary hacking
  98. (mac w/browser body
  99. `(withs (cookies* (table)
  100. get-url
  101. (fn (url) (let (parsed-header html) (get-or-post-url url '() "GET" cookies*)
  102. (= cookies* (fill-table cookies* (flat (parsed-header 1))))
  103. html))
  104. post-url
  105. (fn (url args) (let (parsed-header html) (get-or-post-url url args "POST" cookies*)
  106. (= cookies* (fill-table cookies* (flat (parsed-header 1))))
  107. html)))
  108. (do ,@body)))