シェルスクリプトで HTTP リクエストを行う最も原始的な方法

  • 33
    Like
  • 2
    Comment
More than 1 year has passed since last update.

可搬性を最大限に保たなくてはいけないという要求のもとシェルスクリプトを書くとき、「curl はあるか?wget はあるか?」といったことすら考えることもあります。色々悩むことになるのであれば、bash で HTTP をしゃべろうという発想も悪くないでしょう。

以下の例は、TCP 接続を抽象化したデバイスファイルを読み書きしています。

# -*- shell-script -*-

HTTP_GET_SH_UA="http_get.source.sh/1.0"

function http_get {
    local url=${1#http://}
    local host=${url%%/*}
    local path=/${url#*/}
    exec 3<> /dev/tcp/$host/80
    printf "GET %s HTTP/1.1\n" $path          >&3
    printf "Host: %s\n" $host                 >&3
    printf "User-Agent: %s\n" $HTTP_GET_SH_UA >&3
    printf "\n"                               >&3
    cat <&3
}

http_get http://example.jp/path/to といったコマンドを入力すると、ヘッダとボディが空行で区切られたレスポンスが標準入力に得られます。

当然ながら複雑なことはできませんが、既存の環境が想定できない場合にこれができれば、簡単な HTTP リクエストを一度だけ実行したり、やろうと思えば curl や wget 自体を取ってくるといったこともできるでしょう。

HTTPS はさすがにシェルスクリプトだけでやるわけにはいかないので、OpenSSL の存在を仮定するのが素直でしょう。

  • 最近の RHEL/CentOS 系の場合はほぼ標準と言える Postfix が libssl.so のために openssl パッケージを要求していて、これには openssl コマンドが同梱されています。なのでだいたい openssl コマンドは入っていると思っていいかも。
  • 最近の Debian の場合は libssl パッケージというものが libssl.so を提供していて、これは openssl コマンドとは別なのでどうすべきか…。万が一 openssl コマンドが無くて libssl.so があれば、それを使う curlwget を取ってきてしまった方が早いかも。

openssl コマンドでの HTTPS は man 1 s_client を参照ください

$ cat > header.txt
HEAD / HTTP/1.1
Host: www.openssl.org
Connection: close

$ cat header.txt | openssl s_client -connect www.openssl.org:443 -quiet
depth=1 /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
verify error:num=20:unable to get local issuer certificate
verify return:0
HTTP/1.1 200 OK
Date: Fri, 22 Apr 2016 14:01:39 GMT
Server: Apache/2.4.7 (Ubuntu)
Strict-Transport-Security: max-age=31536000; includeSubDomains
Accept-Ranges: bytes
Vary: Accept-Encoding
Connection: close
Content-Type: text/html; charset=UTF-8

read:errno=0

参考: