← Архив: Common Lisp

Простой http клиент

Author: · 02.01.2010 17:57
· original author: once
Всем привет!
Я хотел сделать простой http клиент на sbcl, но не получается: при использовании socket-make-stream даже файл не создаётся, то же самое при использовании send/receive. Вот код:
[code](in-package :common-lisp-user)
(require :sb-bsd-sockets)
(defpackage :http-client
  (:use :common-lisp :sb-bsd-sockets)
  (:export :wget-text))
(in-package :http-client)
(defun wget-text (host page file &optional (port 80))
  (let ((socket (make-inet-socket :stream :tcp)))
    (socket-connect socket
            (host-ent-address (get-host-by-name host))
            port)
    (let ((stream (socket-make-stream socket
                      :input t
                      :output t
                      :buffering :full
                      :element-type :default
                      :external-format :iso-8859-1)))
      (format stream "GET ~A HTTP/1.0~2%" page)
;     (socket-send socket (format nil "GET ~A HTTP/1.0~2%" page) nil)
      (with-open-file (out file :direction :output)
;       (write-sequence (socket-receive socket nil 1024) out)
    (loop :for line = (read-line stream nil nil) :while line
       :do (write-line line out))))
    (socket-close socket)))[/code]
· original author: dmitry_vk
Было бы неплохо еще finish-output вызвать после format. Это должно сбросить буфер.
· original author: marsijanin
Тут парой тем ниже есть набросок решения с использованием iolib. Есть готовое решение: trivial-http. Использование специфичных для конкретной иплементации api крайне не рекомендуется.
· original author: once
Чё-то он (iolib) у меня  требует пакет :net.sockets, странно...
· original author: marsijanin
>Чё-то он (iolib) у меня  требует пакет :net.sockets, странно...
Было бы ещё неплохо привести листинг... а так, применив cl-astral, могу лишь предпологать, что версия iolib'а не совподает с версией еще чего нибудь (cffi, sbcl...).  У меня такое было когда пытася поставить iolib через asdf-install. Если использовать clbulid, то он будет тянуть всё с git/darcs/etc и проблемы синхронизации версий быть не должно.
· original author: dmitry_vk
Советую использовать drakma для работы через http.
Собственно, в этом коде проблема в буферизации. Вывод текста в поток сокета не приводит к отправке данных по сети до тех пор, пока не будет забит буфер. Так как удаленный сервер так и не получает текста запроса, то он ничего и не отправляет, поэтому и чтение из потока сокета все время блокируется. Если же буфер сбросить с помощью finish-output, то удаленный сервер получает запрос и начинает отправку данных.
http://lisper.ru/apps/format/61 — вот исправленный код.
· original author: once
А я брал всё с cliki и с оф сайта iolib. Вот листинг:
component :NET.SOCKETS not found, required by
#<SYSTEM "iolib" {AC8FEE1}>
   [Condition of type ASDF:MISSING-DEPENDENCY]
Restarts:
 0: [ABORT] Return to SLIME's top level.
 1: [TERMINATE-THREAD] Terminate this thread (#<THREAD "repl-thread" RUNNING {A8800C1}>)
Backtrace:
  0: ((LABELS ASDF::DO-ONE-DEP) ASDF:COMPILE-OP :NET.SOCKETS NIL)
  1: ((LABELS ASDF::DO-DEP) ASDF:COMPILE-OP (:NET.SOCKETS :IO.STREAMS :IO.MULTIPLEX :IOLIB.BASE))
  2: ((SB-PCL::FAST-METHOD ASDF::TRAVERSE (ASDF:OPERATION ASDF:COMPONENT)) #(3 NIL) #<unavailable argument> #<ASDF:COMPILE-OP NIL {BA59D19}> #<ASDF:SYSTEM "iolib" {AC8FEE1}>)
  3: ((LABELS ASDF::DO-DEP) ASDF:COMPILE-OP ("iolib"))
  4: ((SB-PCL::FAST-METHOD ASDF::TRAVERSE (ASDF:OPERATION ASDF:COMPONENT)) #(3 NIL) #<unavailable argument> #<ASDF:LOAD-OP NIL {B94A479}> #<ASDF:SYSTEM "iolib" {AC8FEE1}>)
  5: (ASDF:OPERATE ASDF:LOAD-OP :IOLIB)[:EXTERNAL]
  6: (SB-INT:SIMPLE-EVAL-IN-LEXENV (ASDF:OOS (QUOTE ASDF:LOAD-OP) :IOLIB) #<NULL-LEXENV>)
  7: (SB-FASL::LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /home/antoon/lisp/install-script.lisp" {AE59D21}> NIL NIL)
  8: ((FLET SB-FASL::LOAD-STREAM) #<SB-SYS:FD-STREAM for "file /home/antoon/lisp/install-script.lisp" {AE59D21}>)
  9: (LOAD "/home/antoon/lisp/install-script.lisp")[:EXTERNAL]
 10: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LOAD "/home/antoon/lisp/install-script.lisp") #<NULL-LEXENV>)
 11: (SWANK::EVAL-REGION "(load \"/home/antoon/lisp/install-script.lisp\")
     ")
 12: ((LAMBDA NIL))
 13: (SWANK::TRACK-PACKAGE #<CLOSURE (LAMBDA NIL) {AE52A6D}>)
 14: ((LAMBDA (SWANK-BACKEND::FN)) #<CLOSURE (LAMBDA NIL) {AE52A55}>)
 15: (SWANK::CALL-WITH-BUFFER-SYNTAX #<CLOSURE (LAMBDA NIL) {AE52A55}>)
 16: (SWANK::REPL-EVAL "(load \"/home/antoon/lisp/install-script.lisp\")
     ")
 17: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK:LISTENER-EVAL "(load \"/home/antoon/lisp/install-script.lisp\")
     ") #<NULL-LEXENV>)
 18: ((LAMBDA NIL))
 19: ((FLET #:FORM-FUN1570))
 20: ((FLET #:FORM-FUN1570))
 21: ((LAMBDA (SWANK-BACKEND::HOOK SWANK-BACKEND::FUN)) #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL) {AE52205}>)
 22: ((LAMBDA NIL))
 23: ((FLET #:FORM-FUN1570))
 24: ((FLET #:FORM-FUN1570))
 25: ((LAMBDA (SWANK-BACKEND::HOOK SWANK-BACKEND::FUN)) #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<FUNCTION (LAMBDA NIL) {B98E96D}>)
 26: (SWANK::CALL-WITH-REDIRECTED-IO #<SWANK::CONNECTION {B38DF41}> #<CLOSURE (LAMBDA NIL) {AE52175}>)
 27: (SWANK::CALL-WITH-CONNECTION #<SWANK::CONNECTION {B38DF41}> #<FUNCTION (LAMBDA NIL) {B98E96D}>)
 28: (SWANK::HANDLE-REQUEST #<SWANK::CONNECTION {B38DF41}>)
 29: (SWANK::REPL-LOOP #<SWANK::CONNECTION {B38DF41}>)
 30: (SWANK::REPL-LOOP #<SWANK::CONNECTION {B38DF41}>)[:EXTERNAL]
 31: (SWANK::CALL-WITH-BINDINGS NIL #<CLOSURE (LAMBDA NIL) {A88507D}>)
 32: ((FLET SB-THREAD::WITH-MUTEX-THUNK))
 33: ((FLET #:WITHOUT-INTERRUPTS-BODY-[CALL-WITH-MUTEX]477))
 34: (SB-THREAD::CALL-WITH-MUTEX #<CLOSURE (FLET SB-THREAD::WITH-MUTEX-THUNK) {B6443215}> #S(SB-THREAD:MUTEX :NAME "thread result lock" :%OWNER #<SB-THREAD:THREAD "repl-thread" RUNNING {A8800C1}> :STATE 1) #<SB-THREAD:THREAD "repl-thread" RUNNING {A8800C1}> T)
 35: ((LAMBDA NIL))
 36: ("foreign function: #x80645AC")
 37: ("foreign function: #x8052AE1")
 38: ("foreign function: #x805BAED")
 39: ("foreign function: #xB7FC34C0")
Вот установочный скрипт:
(in-package :asdf)
(load "/home/antoon/lisp/alexandria/alexandria.asd")
(load "/home/antoon/lisp/babel_0.3.0/babel.asd")
(load "/home/antoon/lisp/trivial-features_0.6/trivial-features.asd")
(load "/home/antoon/lisp/cffi_0.10.5/cffi.asd")
(load "/home/antoon/lisp/cl-ppcre-2.0.3/cl-ppcre.asd")
(load "/home/antoon/lisp/trivial-garbage_0.18/trivial-garbage.asd")
(load "/home/antoon/lisp/bordeaux-threads-0.7.0/bordeaux-threads.asd")
(load "/home/antoon/lisp/iolib-0.6.0/iolib.asd")
(oos 'load-op :alexandria)
(oos 'load-op :babel)
(oos 'load-op :trivial-features)
(oos 'load-op :cffi)
(oos 'load-op :cl-ppcre)
(oos 'load-op :trivial-garbage)
(oos 'load-op :bordeaux-threads)
(oos 'load-op :iolib)
· original author: marsijanin
А версия sbcl какая? В любом случае я повторюсь -- для того что бы не жанглировать версиями библиотек есть clbuild.
З.Ы. Обчно создают одну дирекорию systems, куда кидают симлинки на все asd файлы (find <где то> -name '*.asd' -exec ln -s {} . \;), которую пихают в asdf:*central-registry*. После того, как asdf знает где искать asd файлы достаточно только (asdf:oos 'asdf:load-op :iolib), а все зависимости будут загружены автоматически.
· original author: once
Всем спасибо!
· original author: once
А clbuild сам вытягивает зависимости или их надо вручную устанавливать?
· original author: marsijanin
>А clbuild сам вытягивает зависимости или их надо вручную устанавливать?
Сам. ИМХО clbuild -- самы простой способ без лишнего гемороя собрать все нужные cl библиотеки (конечно gentoo users со мной не согласятся).
· original author: dmitry_vk
>(конечно gentoo users со мной не согласятся).
Gentoo lisp-overlay — самый простой способ собрать нужные либы. ;)