Common Lisp의 Unix 소켓을 통한 HTTP

이 게시물에서는 Common Lisp에서 Unix 소켓을 사용하여 HTTP 요청을 보내는 방법에 대해 알아봅니다.

동기 부여



먼저 이 게시물의 동기를 분석해 보겠습니다. 이를 이해하려면 먼저 Common Lisp에서 유닉스 소켓을 사용하여 HTTP 요청을 어떻게 보내느냐는 질문에 대한 답을 찾아야 합니다.

나는 이미 Lisp Reddit Channel에서 같은 질문을 했습니다. 이 게시물은 당면한 문제에 대한 답을 깊이 파고들려는 시도일 뿐입니다.

기본적으로 내가 원하는 것은 Lisp에서 다음 curl 요청을 구현하는 것입니다.

curl --unix-socket /var/run/docker.sock http://localhost/v1.41/containers/json

cl-도커



처음에 왜 이런 요구 사항이 있습니까? 몇 주 전에 저는 Lisp REPL에서 Docker 명령 실행에 대한 게시물을 작성했습니다. 여기서 Docker용 Lisp SDK를 빌드하는 적절한 방법은 유닉스 소켓을 사용하여 HTTP 요청을 보내는 것과 관련이 있다고 언급했습니다. 따라서 이 게시물은 솔루션이 실제로 어떻게 작동하는지에 대한 정교한 설명입니다.





솔루션에 들어가기 전에 먼저 Unix 소켓에 대한 몇 가지 컨텍스트를 설정하고 처음부터 TCP/IP 소켓과 어떻게 다른지 살펴보겠습니다.

유닉스 소켓



UNIX 도메인 소켓이라고도 하는 UNIX 소켓은 동일한 시스템에서 실행 중인 프로세스 간에 양방향 데이터 교환을 허용하는 프로세스 간 통신 메커니즘입니다. UNIX 도메인 소켓은 동일한 시스템에서 실행되고 있음을 알고 있으므로 라우팅과 같은 일부 검사 및 작업을 피할 수 있습니다. IP 소켓보다 빠르고 가볍습니다. 따라서 동일한 호스트의 프로세스와 통신하려는 경우 IP 소켓보다 더 나은 옵션입니다.

UNIX 도메인 소켓은 파일 시스템 권한의 영향을 받는 반면 TCP 소켓은 패킷 필터 수준에서만 제어할 수 있습니다.

해결책



이것은 내 요구 사항에 완벽하게 맞는 사용자Aidenn0로부터 얻은 답변입니다.

(let ((socket (make-instance 'sb-bsd-sockets:local-socket :type :stream)))
  (sb-bsd-sockets:socket-connect socket "/var/run/docker.sock")
  (let ((stream (sb-bsd-sockets:socket-make-stream socket
                                                   :element-type '(unsigned-byte 8)
                                                   :input t
                                                   :output t
                                                   :buffering :none)))
    (let ((wrapped-stream (flexi-streams:make-flexi-stream (drakma::make-chunked-stream stream)
                                                           :external-format drakma::+latin-1+)))
      (drakma:http-request "http://localhost/v1.41/containers/json" :stream wrapped-stream))))


위의 코드가 어떻게 작동하는지 봅시다.



먼저 sb-bsd-sockets:local-socket 로 설정된 소켓 유형으로 :stream 의 새 인스턴스를 만들어 새 소켓을 만듭니다. 그런 다음 소켓을 /var/run/docker.sock에서 Docker의 로컬 유닉스 소켓에 연결합니다.

이제 새 소켓 스트림을 만들고 flexi-streams로 래핑합니다.

유연한 스트림



FLEXI-STREAMS는 실제 2진수 또는 2가 스트림 위에 계층화할 수 있고 즉시 변경할 수 있는 다양한 단일 또는 다중 옥텟 인코딩으로 문자 데이터를 읽고 쓰는 데 사용할 수 있는 "가상"2가 스트림을 구현합니다. 또한 문자열 스트림과 유사한 메모리 내 이진 스트림을 제공합니다.

마지막으로 이 스트림을 Drakma와 같은 HTTP 클라이언트 라이브러리에 전달하여 래핑된 스트림을 사용하여 HTTP 요청을 만듭니다.

드라크마



Drakma는 Common Lisp에서 구현된 모든 기능을 갖춘 HTTP 클라이언트입니다. HTTP/1.1 청킹, 영구 연결, 재사용 가능한 소켓, SSL, 지속적인 업로드, 파일 업로드, 쿠키 등을 처리하는 방법을 알고 있습니다.

이 요청은 yason 또는 cl-json과 같은 JSON 라이브러리를 사용하여 처리할 수 있는 JSON 응답을 반환합니다.

그리고 이것이 제가 솔루션을 사용하여 cl-docker 패키지를 통해 Common Lisp에서 Docker용 SDK를 빌드한 방법입니다.

(defun docker-api (url)
  (let ((socket (make-instance 'sb-bsd-sockets:local-socket :type :stream)))
    (sb-bsd-sockets:socket-connect socket "/var/run/docker.sock")
    (let ((stream (sb-bsd-sockets:socket-make-stream socket                     
        :element-type '(unsigned-byte 8)
    :input t
    :output t
    :buffering :none)))
    (let ((wrapped-stream (flexi-streams:make-flexi-stream (drakma::make-chunked-stream stream)
    :external-format :utf-8)))
    (yason:parse (dex:get (docker-url url) :stream wrapped-stream :want-stream t) :object-as :alist)))))


참고로 동일한 스트림 개체를 사용하여 HTTP 요청을 보내는 데 Drakma 대신 Dexador을 사용했습니다. Dexador의 API는 Drakma와 매우 유사합니다. 그리고 JSON 응답을 구문 분석하기 위해 yason을 사용하고 출력을 Alist(연결 목록)로 반환합니다.

참조:


  • Drakma
  • Flexi-streams
  • Unix domain socket
  • Unix sockets vs TCP/IP sockets

  • 당신이 게시물을 즐겼기를 바랍니다. 저는 이 문제를 해결하면서 소켓과 스트림, 특히 유닉스 소켓에 대해 많은 것을 배웠습니다. 의견 섹션에서 질문/피드백이 있으면 알려주십시오.

    좋은 웹페이지 즐겨찾기