cl-rw

Layered streams for Common Lisp
git clone https://logand.com/git/cl-rw.git/
Log | Files | Refs

commit 716a88e1e80ed8c5673d3588108d05e7e2d27c20
parent 6c6504e9313ac0d19cca34338ebb6f05910dcaee
Author: Tomas Hlavaty <tom@logand.com>
Date:   Sun, 10 Jan 2016 20:12:59 +0100

better proxy

Diffstat:
Mdemo-webserver.lisp | 44++++++++++++++++++++++++++++++++------------
Mhttp.lisp | 88++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
2 files changed, 88 insertions(+), 44 deletions(-)

diff --git a/demo-webserver.lisp b/demo-webserver.lisp @@ -99,18 +99,38 @@ (defun serve-proxy (host port method query protocol headers body) (or (ignore-errors - (let ((host "ondoc.logand.com") - (port 80)) - (with-open-stream (s (rw.socket:make-tcp-client-socket host port)) - #+nil - (rw.http::%client1 s host port query #+nil path query-string - (update-headers headers - `(("Host" . ,host))))))) + (with-open-stream (s (rw.socket:make-tcp-client-socket host port)) + (multiple-value-bind (protocol code message headers2 body) + ;; TODO forward upload body + (rw.http::%client1 s host port method protocol query nil ;;TODO split into path query-string + (update-headers headers + `(("Host" . ,host)))) + (declare (ignore protocol)) + ;;(print (list :@@@ headers2)) + (list + :http-1.0 + :code code + :message message + :headers (flet ((allow (k) + (let ((x (find k headers2 + :test #'equalp :key #'car))) + (when x + (list x))))) + (append + '(("Connection" . "close")) + ;;(allow "Cache-Control") + (allow "Content-Disposition") + (allow "Content-Length") + (allow "Content-Type") + ;;(allow "Last-Modified") + (allow "Location") + (allow "Set-Cookie"))) + :body body)))) '(:http-1.0 - :code 200 + :code #1=502 :headers (("Connection" . "close") ("Content-Type" . "text/plain;charset=UTF-8")) - :body "TODO SERVE-PROXY"))) + :body #.(format nil "~a ~a" #1# (cdr (assoc #1# rw.http:*http-codes*)))))) (defun serve-not-found () '(:http-1.0 @@ -129,7 +149,7 @@ (rw:next-z0 r))))) (defun webserver-handler (msg stream method query protocol headers &optional body) - (declare (ignore stream #+nil protocol #+nil body)) + (declare (ignore stream protocol #+nil body)) (ecase msg ;;(:read (rw:till (rw:peek-reader stream))) (:write @@ -140,8 +160,8 @@ #+nil(equalp "127.0.0.1" host)) ;; proxy_set_header Host $host; ;; proxy_set_header Gate "$scheme $remote_addr"; - ;; proxy_pass http://127.0.0.1:1431; - (serve-proxy host port method query protocol headers body)) + (assert (not body)) + (serve-proxy "ondoc.logand.com" 1431 method query :http-1.0 headers body)) ;; TODO ?rewrite ondoc.logand.com$ -> http://ondoc.logand.com/$1 ((or (not host) (equalp "logand.com" host) diff --git a/http.lisp b/http.lisp @@ -196,18 +196,38 @@ (next-eol reader))) (defun next-body (reader) ;; TODO better, respect content-length! - (coerce (rw:till reader) 'string)) + (coerce (rw:till reader) '(vector (unsigned-byte 8)))) (defun write-crlf (writer) (rw:write-octets writer '(13 10))) -(defun write-headers (writer headers) - (dolist (x headers) - (rw:write-utf8-string writer (car x)) +(defun write-header (writer k v) + (when v + (rw:write-utf8-string writer k) (rw:write-octets writer #.(rw.string:string-to-octets ": " :utf-8)) - (rw:write-utf8-string writer (cdr x)) + (etypecase v + (string (rw:write-utf8-string writer v)) + (integer (rw:write-utf8-string writer (princ-to-string v))) + (cons + (loop + for part in v + for i from 0 + do (progn + (when (plusp i) + (rw:write-octets writer #.(rw.string:string-to-octets ";" :utf-8))) + (etypecase part + (string (rw:write-utf8-string writer part)) + (cons + (rw:write-utf8-string writer (car part)) + (when (cdr part) + (rw:write-octets writer #.(rw.string:string-to-octets "=" :utf-8)) + (rw:write-utf8-string writer (cdr part))))))))) (write-crlf writer))) +(defun write-headers (writer headers) + (dolist (x headers) + (write-header writer (car x) (cdr x)))) + (defun write-protocol (writer protocol) (rw:write-octets writer @@ -215,32 +235,35 @@ (:http-1.0 #.(rw.string:string-to-octets "HTTP/1.0" :utf-8)) (:http-1.1 #.(rw.string:string-to-octets "HTTP/1.1" :utf-8))))) -(defun write-query (stream method protocol path query-string) - (write-string (ecase method - (:get "GET") - (:post "POST")) - stream) - (write-char #\space stream) - (write-string (or path "/") stream) +(defun write-query (writer method protocol path query-string) + (rw:write-utf8-string writer (ecase method + (:get "GET") + (:head "HEAD") + (:post "POST") + (:put "PUT"))) + (rw:write-utf8-char writer #\space) + (rw:write-utf8-string writer (or path "/")) (when query-string - (write-char #\? stream) - (write-string query-string stream)) - (write-char #\space stream) - (write-protocol stream protocol) - (write-crlf stream)) - -(defun %client1 (stream host port path query-string headers) - (write-query stream :get :http-1.0 path query-string) - (write-headers (or headers - `(("Host" . ,(if port - (format nil "~a:~a" host port) - host)))) - stream) - (write-crlf stream) + (rw:write-utf8-char writer #\?) + (rw:write-utf8-string writer query-string)) + (rw:write-utf8-char writer #\space) + (write-protocol writer protocol) + (write-crlf writer)) + +(defun %client1 (stream host port method protocol path query-string headers) + (let ((w (rw:byte-writer stream))) + (write-query w method protocol path query-string) + (write-headers w (or headers + `(("Host" . ,(if port + (format nil "~a:~a" host port) + host))))) + (write-crlf w)) (finish-output stream) - (let ((r (rw:peek-reader (rw:char-reader stream)))) - (multiple-value-bind (protocol code message) (next-status r) - (values protocol code message (next-headers r) (next-body r))))) + (assert (eq :http-1.0 protocol)) ;; TODO chunked encoding with http-1.1 + (let* ((rb (rw:peek-reader (rw:byte-reader stream))) + (rc (rw:peek-reader (rw:utf8-reader rb :charp t)))) + (multiple-value-bind (protocol code message) (next-status rc) + (values protocol code message (next-headers rc) (next-body rb))))) (defun client1 (url headers) (destructuring-bind (&key scheme host port path query-string fragment) @@ -250,7 +273,7 @@ (declare (ignore fragment)) (assert (equal "http" scheme)) (with-open-stream (s (rw.socket:make-tcp-client-socket host (or port 80))) - (%client1 s host port path query-string headers)))) + (%client1 s host port :get :http-1.0 path query-string headers)))) (defun client (url &key headers (redirect 5)) (do (protocol code message headers2 body) @@ -261,9 +284,8 @@ (setq url (cdr (assoc "Location" headers2 :test #'equalp))) ;; TODO update "Host" header (return-from client (values protocol code message headers2 body))))) -;;(client "http://127.0.0.1:1234/") +;;(client "http://127.0.0.1:2341") ;;(client "http://logand.com") -;;(client "http://logand.com:2234") @@ -382,6 +404,8 @@ (pathname (with-open-file (s body :element-type '(unsigned-byte 8)) (rw:copy (rw:byte-reader s) writer))) + ((vector (unsigned-byte 8)) + (rw:copy (rw:reader body) writer)) (cons (rw:write-utf8-string writer (with-output-to-string (*standard-output*)