CLISP 之 创建HTTP GET、POST、HEAD请求

5 篇文章 0 订阅
4 篇文章 0 订阅
;;;; WHJ.20180504
;;;; 创建HTTP GET、POST、HEAD测试

;;;; WHJ.20200220
;;;; 完善优化

(in-package :cl-user)

(defparameter +newline+ (coerce (list #\return #\newline ) 'string))

;当前与服务器的sockt连接
(defparameter *conn* nil)
;文档内容编码格式
(defvar *html-charset* charset:utf-8)

;创建 socket连接
(defun cli-init (host &optional (port 80))
    (handler-case
        (progn
            (socket-status *conn*)
            (close *conn*))
        (error (e) nil))
        (setf *conn* (socket-connect port  host :external-format :unix)))

(defun http-stream-mode (byte-flag)
	(setf (stream-element-type *conn*) (if byte-flag '(unsigned-byte 8) 'character)))
	
(defun string2bytes (string)
	(convert-string-to-bytes string *html-charset*))
	
(defun http-write-string (str)
    (write-string str *conn*))
    
(defun http-write (string &optional (newline t))
    ;(format *debug-io* "DEBUG:~a~%" string)
    
	(http-write-string string)
    (when newline
        (http-write-string +newline+)))

(defun http-write-bytes (vec)
    (write-sequence vec *conn*))
	
(defun http-read-byte ()
	(read-byte *conn* nil))
	
(defun http-read-line ()
	(read-line *conn* nil))
	
(defun urlencode (string)
	(reduce 
		(lambda (x y) (format nil "~a%~(~x~)" x y)) 
		(convert-string-to-bytes string *html-charset*)
		:initial-value ""))
	
;解析k=v,分别对k,v 调用urlencode得到 k1,v1 ,然后再合成k1=v1	
(defun encode-keyvalue-string (kv-string)
	(let ((index= (search "=" kv-string)))
		(format nil "~a=~a" (urlencode (subseq kv-string 0 index=)) (urlencode (subseq kv-string (1+ index=))))))
	

	
(defun get-content-length (html-header-list)
	(let* ((flag "Content-Length:")
		   (content-length-header (find-if (lambda (x) (search flag x)) html-header-list)))
		(if content-length-header
			(parse-integer (subseq content-length-header (+ (search flag content-length-header) (length flag))) :junk-allowed t)
			0)))
		
(defun decode-bytes (byte-sequence charset)
	(convert-string-from-bytes 
		(if (listp byte-sequence)
			(coerce byte-sequence 'vector)
			byte-sequence)
		charset))
        

(defun split-url (url)
    (let* ((s (+ 3 (search "://" url)))
			   (e (search "/" url :start2 s)))
               
               (unless e (setf e (length url)))
               
			(let ((host (subseq url s e))
                  (rel-url (subseq url e))
                  (port (if (search "http:" (string-downcase url)) 80 443)))
                  
                  (let ((p1 (position #\: host :from-end t)))
                       (when p1
                           (setf port (parse-integer (subseq host (1+ p1)))
                                 host (subseq host 0 p1))))
                  
                  (values host port (if (string= rel-url "") "/" rel-url)))))
;;;; for test
(defun save-to-file (vbuf file)
    (with-open-file (out file :direction :output :element-type '(unsigned-byte 8))
        (write-sequence vbuf out)))
(defun read-file-bytes (file)
	(with-open-file (in  file :direction :input  :element-type '(unsigned-byte 8))
        (let ((vbuf (make-array (file-length in))))
             (read-sequence vbuf in)
             vbuf)))
;http://www.lispworks.com/documentation/HyperSpec/Graphics/LWLarge.gif
(defun http-get-file (url)
    (multiple-value-bind (host port rel-url) (split-url url)
            (cli-init host port)

            (http-stream-mode nil)
            (http-write (format nil "GET ~a HTTP/1.1" rel-url))
            (http-write (format nil "Host: ~a" host))

            (http-write "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
     
            (http-write +newline+ t)
            
            (terpri *debug-io*)

            (when *conn*
                (http-stream-mode nil)
                (let* ((tmp-list (loop for x = (http-read-line)
                                        while (and x (plusp (length x)))
                                        collect x))
                       (content-length (get-content-length tmp-list)))
                
                (http-stream-mode t)
                ;读取返回内容
                (coerce (loop for i from 0 to (1- content-length) for x = (http-read-byte) while x collect x) 'vector)))))
(defun http-get (url)
	(multiple-value-bind (host port rel-url) (split-url url)
            (cli-init host port)

            (http-stream-mode nil)
            (http-write (format nil "GET ~a HTTP/1.1" rel-url))
            (http-write (format nil "Host: ~a" host))
            (http-write "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")

            (http-write +newline+ nil)

            (when *conn*
                (http-stream-mode nil)
                (let* ((tmp-list (loop for x = (http-read-line)
                                        while (and x (plusp (length x)))
                                        collect x))
                       (content-length (get-content-length tmp-list)))
                
                (http-stream-mode t)
                ;读取返回内容
                (format *debug-io* "DEBUG: content-length:~a~%" content-length)
                
                (let ((vbuf (make-array (1- content-length))))
                    (read-sequence vbuf *conn*)
                    (values
                        (decode-bytes 
                            ;(loop for i from 0 to (1- content-length) for x = (http-read-byte) while x collect x)
                                vbuf
                            *html-charset*)
                         tmp-list))))))
		
(defun http-post (url data)

	;转义 data 特殊字符
	(setf data 
			(reduce 
				(lambda (x y) (format nil "~a&~a" x y))
				(mapcar #'encode-keyvalue-string (split #\& data))))
				
	(multiple-value-bind (host port rel-url) (split-url url)
		(cli-init host port)

		(http-write (format nil "POST ~a HTTP/1.1" rel-url))
		(http-write (format nil "Host: ~a" host))
		(http-write "Content-Type: application/x-www-form-urlencoded")
		(http-write (format nil "Content-Length: ~d" (length data)))
		(http-write +newline+ nil)
		
		(http-write data nil)

		(when *conn*
			(http-stream-mode nil)
			(let* ((tmp-list (loop for x = (http-read-line) 
									while (and x (plusp (length x)))
									collect x))
				   (content-length (get-content-length tmp-list)))
			
			(http-stream-mode t)
			
			;读取返回内容
			(decode-bytes 
				(loop for i from 0 to (1- content-length) for x = (http-read-byte) while x collect x)
				*html-charset*)))))

(defun vector-append-vector-or-list (dst-vector src-vector-or-list)
	(map 'vector (lambda (x) (vector-push-extend x dst-vector)) src-vector-or-list)
	dst-vector)

(defun http-post-form-data-header (boundary)
	(string2bytes 
		(with-output-to-string (out)
		 (format out "Content-Type: multipart/form-data; boundary=~a" boundary)
		 (format out "~a~a" #\return #\newline)
		 (format out "~a~a" #\return #\newline))))
(defun http-post-form-data-text (boundary name text)
	(string2bytes (with-output-to-string (out)
		 (format out "--~a" boundary)
		 (format out "~a~a" #\return #\newline)
		 (format out "content-disposition: form-data; name=~s" name)
		 (format out "~a~a" #\return #\newline)
		 (format out "~a~a" #\return #\newline)
		 (format out text)
		 (format out "~a~a" #\return #\newline))))

(defun http-post-form-data-file (boundary name filepath fileName content-type)
	(let ((vtmp (make-array 4096 :fill-pointer 0 :adjustable t)))
		(vector-append-vector-or-list 
				vtmp 
				(string2bytes (with-output-to-string (out)
														 (format out "--~a" boundary)
														 (format out "~a~a" #\return #\newline)
														 (format out "content-disposition: form-data; name=~s; filename=~s" name fileName)
														 (format out "~a~a" #\return #\newline)
														 (format out "content-type: ~a" content-type)
														 (format out "~a~a" #\return #\newline)
														 (format out "~a~a" #\return #\newline)
														 )))
		(vector-append-vector-or-list vtmp (read-file-bytes filepath))
		
		(vector-append-vector-or-list vtmp (list 13 10))
		
		vtmp))
	
(defun http-post2 (url)
    (multiple-value-bind (host port rel-url) (split-url url)
          (cli-init host port)
  
          (let (vtmp 
                (len 0) 
                (boundary "AAAAA") 
                (vbuf (make-array 4096 :fill-pointer 0 :adjustable t)))
                
                (vector-append-vector-or-list vbuf (http-post-form-data-header boundary))
            
                (setf vtmp (http-post-form-data-text boundary "text1" "abcd"))
                (vector-append-vector-or-list vbuf vtmp)
                (incf len (length vtmp))
                
                (setf vtmp (http-post-form-data-text boundary "text2" "这是汉字部分"));
                (vector-append-vector-or-list vbuf vtmp)
                (incf len (length vtmp))
                
                (setf vtmp (http-post-form-data-file boundary "file1" "e:\\aa.png" "mytest1.png" "image/png"))
                (vector-append-vector-or-list vbuf vtmp)
                (incf len (length vtmp))
                
                (setf vtmp (http-post-form-data-file boundary "file2" "e:\\aa.png" "mytest2.png" "image/png"))
                (vector-append-vector-or-list vbuf vtmp)
                (incf len (length vtmp))
                
                (setf vtmp (http-post-form-data-file boundary "file3" "e:\\ChromeSetup.exe" "ch.exe" "application/octer-stream"))
                (vector-append-vector-or-list vbuf vtmp)
                (incf len (length vtmp))
                
                (setf vtmp (string2bytes (format nil "--~a--" boundary)))
                (vector-append-vector-or-list vbuf vtmp)
                (incf len (length vtmp))
            
                (http-write (format nil "POST ~a HTTP/1.1" rel-url))
                (http-write (format nil "Host: ~a" host))
                (http-write (format nil "Content-Length: ~a"  len))
                
                (http-write-bytes vbuf))
          
          
              (when *conn*
               (http-stream-mode nil)
               (let* ((tmp-list (loop for x = (http-read-line) 
                     while (and x (plusp (length x)))
                     collect x))
                   (content-length (get-content-length tmp-list)))
               
               (http-stream-mode t)
               
               ;读取返回内容
               (decode-bytes 
                (loop for i from 0 to (1- content-length) for x = (http-read-byte) while x collect x)
                *html-charset*)))))
                        
(defun http-head (url)
    (multiple-value-bind (host port rel-url) (split-url url)
        (cli-init host port)

		(http-write (format nil "HEAD ~a HTTP/1.1" rel-url))
		(http-write (format nil "HOST: ~a" host))
		(http-write +newline+ nil)

		(when *conn*
			(http-stream-mode nil)
			(let* ((tmp-list (loop for x = (http-read-line)
									while (and x (plusp (length x)))
									collect x))
				   (content-length (get-content-length tmp-list)))
				   
				(format nil "~%~{~a~^~%~}~%" tmp-list)))))
;;;; options 方法返回Allow头部表明服务器的实现方法集				
(defun http-options (url)
    (multiple-value-bind (host port rel-url) (split-url url)
        (cli-init host port)
        
		(http-write (format nil "OPTIONS ~a HTTP/1.1" rel-url))
		(http-write (format nil "Host: ~a" host))
		(http-write "Accept: *")
		(http-write +newline+ nil)

		(when *conn*
			(http-stream-mode nil)
			(let* ((tmp-list (loop for x = (http-read-line)
									while (and x (plusp (length x)))
									collect x))
				   (content-length (get-content-length tmp-list)))
				   
			(http-stream-mode t)
			;读取返回内容
			(format nil
				 "~{~a~%~}~a~%"
				tmp-list
				(decode-bytes (loop for i from 0 to (1- content-length) for x = (http-read-byte) while x collect x)
				*html-charset*))))))
;;;; (http-put "http://localhost:37212/data/test1.txt" "这是一个中文的put请求")				
(defun http-put (url data)
    (multiple-value-bind (host port rel-url) (split-url url)
        (cli-init host port)
        
		(http-write (format nil "PUT ~a HTTP/1.1" rel-url))
		(http-write (format nil "Host: ~a" host))
		(http-write "Content-Type: text/plain")
		(http-write (format nil "Content-Length: ~a" (length (convert-string-to-bytes data *html-charset*))))
		(http-write +newline+ nil)
		
		(http-write data nil)

		(when *conn*
			(http-stream-mode nil)
			(let* ((tmp-list (loop for x = (http-read-line)
									while (and x (plusp (length x)))
									collect x))
				   (content-length (get-content-length tmp-list)))
			
			(http-stream-mode t)
			;读取返回内容
			(decode-bytes 
				(loop for i from 0 to (1- content-length) for x = (http-read-byte) while x collect x)
				*html-charset*)))))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值