git.fiddlerwoaroof.com
Browse code

Initial

fiddlerwoaroof authored on 31/05/2017 23:33:27
Showing 3 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,13 @@
1
+;;;; http-streams.asd
2
+
3
+(asdf:defsystem #:http-streams
4
+  :description "Describe http-streams here"
5
+  :author "Your Name <your.name@example.com>"
6
+  :license "Specify license here"
7
+  :depends-on (#:fwoar.lisputils
8
+               #:alexandria
9
+               #:serapeum)
10
+  :serial t
11
+  :components ((:file "package")
12
+               (:file "http-streams")))
13
+
0 14
new file mode 100644
... ...
@@ -0,0 +1,154 @@
1
+;;;; http-streams.lisp
2
+
3
+(in-package #:http-streams)
4
+
5
+;;; "http-streams" goes here. Hacks and glory await!
6
+
7
+(define-condition no-pipe (error)
8
+  ())
9
+
10
+(defclass piped-object ()
11
+  ((%pipe :reader pipe :initarg :pipe :initform (error 'no-pipe))))
12
+
13
+(defun end-line (stream)
14
+  (princ #. (coerce '(#\return #\newline) 'string) stream))
15
+
16
+(defmacro with-line ((stream) form)
17
+  `(prog1 ,form
18
+     (end-line ,stream)))
19
+
20
+(defun request (method path out-stream)
21
+  (with-line (out-stream)
22
+    (format out-stream "~a ~a HTTP/1.1" method path))
23
+  out-stream)
24
+
25
+(defun header (name value out-stream)
26
+  (with-line (out-stream)
27
+    (format out-stream "~a: ~a" name value))
28
+  out-stream)
29
+
30
+(defun body (value out-stream)
31
+  (end-line out-stream)
32
+  (etypecase value
33
+    (stream (loop with buffer = (make-string 1000)
34
+	       for end-read = (read-sequence buffer value)
35
+	       while (/= end-read 0)
36
+	       do (write-sequence buffer out-stream :end end-read)))
37
+    (string (write-sequence value out-stream))))
38
+
39
+(defmacro define-header-function (name header)
40
+  (alexandria:once-only (header)
41
+    `(defun ,name (value out-stream)
42
+       (header ,header value out-stream))))
43
+
44
+(defmacro define-header-functions (() &body definitions)
45
+  `(progn ,@(mapcar (fw.lu:destructuring-lambda ((name header))
46
+		      `(define-header-function ,name ,header))
47
+		    definitions)))
48
+
49
+(define-header-functions ()
50
+  (content-type "Content-Type")
51
+  (host "Host")
52
+  (accept "Accept")
53
+  (pragma "Pragma")
54
+  (user-agent "User-Agent")
55
+  (cache-control "Cache-Control")
56
+  (accept-encoding "Accept-Encoding")
57
+  (accept-language "Accept-Language"))
58
+
59
+(defmacro /> (name (&rest args)  &body funs)
60
+  (alexandria:with-gensyms (out)
61
+    `(defun ,name (,@args ,out)
62
+       ,@(mapcar (serapeum:op `(,@_ ,out))
63
+		 funs))))
64
+
65
+(/> build-subreddit-request (method subreddit)
66
+  (request method (format nil "/r/~a.rss" subreddit))
67
+  (accept "text/html")
68
+  (accept-language "en-US,en;q=0.8")
69
+  (cache-control "no-cache")
70
+  (host "www.reddit.com")
71
+  (pragma "no-cache")
72
+  (header "Upgrade-Insecure-Requests" "1")
73
+  (user-agent "foobar 0.0")
74
+  (body ""))
75
+
76
+(defun read-in-header (stream)
77
+  (values (read-line stream)
78
+	  (loop with headers = ()
79
+	     for line = (read-line stream nil)
80
+	     while line
81
+	     until (string= line "
")
82
+	     do
83
+	       (push (map 'list 'metatilities:strip-whitespace
84
+			  (fwoar.string-utils:split #\: (metatilities:strip-whitespace line) :count 2))
85
+		     headers)
86
+	     finally
87
+	       (return (nreverse headers)))))
88
+
89
+(defmacro with-subreddit-request ((subreddit stream) &body body)
90
+  (alexandria:with-gensyms (socket sock-stream ssl-stream)
91
+    `(usocket:with-client-socket (,socket ,sock-stream "reddit.com" 443 :element-type '(unsigned-byte 8))
92
+       (let* ((,ssl-stream (cl+ssl:make-ssl-client-stream ,sock-stream :hostname "reddit.com"))
93
+	      (,stream (flexi-streams:make-flexi-stream ,ssl-stream :external-format :iso-8859-1)))
94
+	 (build-subreddit-request :GET ,subreddit ,stream)
95
+	 (finish-output ,stream)
96
+	 (unwind-protect (progn ,@body)
97
+	   (close ,ssl-stream))))))
98
+
99
+(defun get-header (name headers)
100
+  (cadr (assoc name headers :test 'equalp)))
101
+
102
+(defun get-content-length (headers)
103
+  (parse-integer (get-header "Content-Length" headers)))
104
+
105
+(defmacro with-parsed-response (char-stream (result headers reply-buf content-length) &body body)
106
+  (alexandria:once-only (char-stream)
107
+    `(multiple-value-bind (,result ,headers) (read-in-header ,char-stream)
108
+       (let* ((,reply-buf (make-string (get-content-length ,headers)))
109
+	      (,content-length (read-sequence ,reply-buf ,char-stream)))
110
+	 ,@body))))
111
+
112
+(defun get-subreddit-feed (&optional (subreddit "lisp"))
113
+  (with-subreddit-request (subreddit char-stream)
114
+    (with-parsed-response char-stream (result headers reply-buf content-length)
115
+      (values reply-buf
116
+	      result
117
+	      headers
118
+	      content-length))))
119
+
120
+
121
+#|
122
+(defun get-subreddit-feed ()
123
+  (usocket:with-client-socket (so st "reddit.com" 443 :element-type '(unsigned-byte 8))
124
+    (let* ((ssl-stream (cl+ssl:make-ssl-client-stream st :hostname "reddit.com"))
125
+	   (char-stream (flexi-streams:make-flexi-stream ssl-stream :external-format :iso-8859-1)))
126
+      (unwind-protect (locally (declare (optimize (debug 3)))
127
+			(progn (build-subreddit-request :GET "lisp" char-stream)
128
+			       (finish-output char-stream)
129
+			       (multiple-value-bind (result headers) (read-in-header char-stream)
130
+				 (let ((reply-buf (make-string (parse-integer (cadr (assoc "Content-Length" headers
131
+											   :test 'equalp))))))
132
+				   (values reply-buf
133
+					   result
134
+					   headers
135
+					   (read-sequence reply-buf char-stream))))))
136
+	(close ssl-stream)))))
137
+
138
+(defparameter *old-readtable* (copy-readtable *readtable*))
139
+(set-macro-character #\] 'identity nil)
140
+(set-dispatch-macro-character
141
+ #\# #\[
142
+ (lambda (s c n)
143
+   (let ((list (read-delimited-list #\] s t)))
144
+     (fw.lu:rollup-list
145
+      (loop with result = ()
146
+	 for (arg op) on list by #'cddr
147
+	 while op
148
+	 do
149
+	   (push `(,op ,arg)
150
+		 result)
151
+	 finally
152
+	   (push arg result)
153
+	   (return (nreverse result)))))))
154
+|#
0 155
new file mode 100644
... ...
@@ -0,0 +1,5 @@
1
+;;;; package.lisp
2
+
3
+(defpackage #:http-streams
4
+  (:use #:cl))
5
+