yunomuのブログ

趣味のこと

CGIを書こう

 タイトル「インターネットになろう」にしようと思ったけどやめた。

 今回はちょっとWebをやってみます。「そろそろ何をやりたいか考えてみよう」と言いたいところですが、そんな簡単に見つかるなら幸せなことで、それはそれで良いのですが、普通はとりあえずできることを増やしながら「これ面白いな」と思うことを見つけていくというのが良いでしょう。

準備(Windows使いのプログラマ向け)

 まずはサンプルを動かしてみてほしかったのですが、Windowsだと肝心な部分がどうしても動かなかったのでちょっと下準備をします。

 WindowsのPCを使っていて、Rubyのインストールで「RubyInstaller」を使っていて、現在はコマンドプロンプト(Rubyといっしょにインストールされている"Start Command Prompt for Ruby"みたいなやつを含む)を使ってRubyを書いている人向けの情報です。

 おそらく最初に以下のようなページからrubyinstaller-devkit-2.7.2-1-x64.exerubyinstaller-2.7.2-1-x64.exeをダウンロードしてインストールしたのではないでしょうか。

rubyinstaller.org

 名前に"devkit"と入っている方のファイルがある人はそのまま、無い方のファイルの人はdevkitのファイルをダウンロードしてください。ファイルをなくした人もダウンロードしなおしてください。

 そして再度インストールしてください。ただし、今度は途中のモジュール選択の部分で"MSYS2"をインストールするようにチェックを入れてください。最初にインストールした時と同様にコマンドプロンプトのウィンドウが開いて色々質問されると思いますが、全部そのままEnterを押しておけば大丈夫です。

 インストールが終わったら、今までと同じようにコマンドプロンプトを立ち上げてください。そのあと、bashコマンドを実行してください。ちょっと見た目が変りましたね。bashは、おおむね今までのコマンドプロンプトと似たようなものですが、色んな機能があったりなかったりします。でも今までどおりcddirも使えるので安心してください。dirの代わりにlsも使えますし、もちろんrubyirbも使えます。

 では、ここからはbashを使っているという前提でいきます。

WEBrickを使う

 ここではWEBrickというWebサーバを使ってみます。Webサーバとはなんぞやというのはまあやってみればなんとなくわかるでしょう。マニュアルはこれまでとは違い「標準添付ライブラリ All libraries」のページにあります。ページには大量のライブラリがありますが、ネットワークの節の"webrick"のページを見てください。

library webrick (Ruby 2.7.0 リファレンスマニュアル)

 概要にサンプルコードが載っています。「以下は Web サーバとして完全に動作するスクリプトです。」とのことなので、とりあえずこれを適当なファイル名(ここではweb.rbとします)のファイルに打ち込むかコピペしてください。

require 'webrick'
srv = WEBrick::HTTPServer.new({ :DocumentRoot => './',
                                :BindAddress => '127.0.0.1',
                                :Port => 20080})
srv.mount('/view.cgi', WEBrick::HTTPServlet::CGIHandler, 'view.rb')
srv.mount('/foo.html', WEBrick::HTTPServlet::FileHandler, 'hoge.html')
trap("INT"){ srv.shutdown }
srv.start

 これを実行します。

% ruby web.rb
[2021-02-05 19:57:11] INFO  WEBrick 1.6.0
[2021-02-05 19:57:11] INFO  ruby 2.7.0 (2019-12-25) [x86_64-linux]
[2021-02-05 19:57:11] INFO  WEBrick::HTTPServer#start: pid=111395 port=20080

 このように、なにかメッセージが出てそのままプログラムが停止すると思います(細かい数字は違うかもしれません)。エラーが出たり終了してしまったら何か書き間違いがあるかもしれません。書き間違いは熟練プログラマでもよくあることなので注意深く探してみましょう。

 この状態で、Webブラウザを開いてURL http://localhost:20080 にアクセスしてみてください。ファイル一覧みたいなものが見えたら成功です。たぶんさきほど作ったファイル(web.rb)が見えているんじゃないでしょうか。ファイル名をクリックすると中身が見られます。インターネットっぽくなってきましたね。

 終了する時はコマンドの画面に戻ってC-Cです。サンプルコードの中の最後から2行目trap("INT"){ srv.shutdown }の部分が、C-Cを押したら終了するという意味になっています。書かないと終了できなくて不便です。ウィンドウ閉じたら終了したりするんだろうか? よくわかりません。別に試さなくてもよいでしょう。最悪PCを再起動すれば終了します。

ファイルにアクセスする

 まず、/hoge.htmlにアクセスします。URLは http://localhost:20080/hoge.html です。"Not Found"のような文字が表示されたと思います。それで正常です。つまり「"hoge.html"というファイルはありません」ということです。実際に無いので、これはこれで正しい。

 では"hoge.html"というファイルを作ってみましょう。何か適当なことを書いて、現在ソースを置いているのと同じフォルダに"hoge.html"という名前で保存します。内容が思いつかない人はこうしましょう。

You just don't have enough fight in you!

 ファイルを保存して再度、http://localhost:20080/hoge.html にアクセスしてみましょう。「You just don't have enough fight in you!」または適当に書いた文字が表示されていれば成功です。文字化けした人は、アルファベット以外を書きましたね。このように、Webサーバはファイルをブラウザに配信することができます。HTMLが書けるならHTMLを書くともうちょっとそれらしくなります。文字化けした人は、HTMLでエンコードを指定するか、ブラウザの設定でエンコードをいじるか、なんかやってみてください。

 これだけでも何かと便利なので覚えておくと良いでしょう。

 次に、/foo.htmlにアクセスします。URLは http://localhost:20080/foo.html です。/hoge.htmlにアクセスした時と同じ結果になったと思います。なっていない人は、コードのどこかを間違えていると思います。がんばって間違いを探しましょう。この間違いさがしを専門用語でデバッグとも言います(デバッグはもっと広い意味での間違いさがしですが)。

 同じ結果になるのは、コードの中のsrv.mount('/foo.html', WEBrick::HTTPServlet::FileHandler, 'hoge.html')の行の効果です。これで「"/foo.html"にアクセスした時は"hoge.html"のファイルを返す」という意味になります。詳しくはWEBrickのマニュアルにありますが、難しいのでまた別に解説してもいいと思っています。話せば長くなります(たのしい)。

プログラムにアクセスする(CGI)

 本題です。ファイルにアクセスしてファイルが返る、または適当なパス(ここでは"/foo.html")にアクセスしてファイルが返るように、プログラムを実行して実行結果をブラウザに返すといったようなことができます。ものすごく簡単に言うと、このような仕組みをCGIと言います。

 まだ解説していない以下の行に注目してください。

srv.mount('/view.cgi', WEBrick::HTTPServlet::CGIHandler, 'view.rb')

 これは日本語訳すると「"/view.cgi"にアクセスされたら"view.rb"を起動してその結果を返す」という意味です。ここで出てくる"view.rb"はRubyのプログラムです。そんなものは存在しないので、現時点で http://localhost:20080/view.cgi にアクセスすると「Internal Server Error」みたいなのが出ると思います。Webサービスがエラーになった時にこのような画面を見たことがある人もいるかもしれません。つまりそういうことです。

 ということで、ファイル"view.rb"を用意します。内容は以下のようにしてください。

#!/usr/bin/env ruby

print "Content-Type: text/plain\r\n\r\n"

print "Ciao!"

 保存して http://localhost:20080/view.cgi にアクセスして、"Ciao!"と表示されれば成功です。print "Ciao!"の部分を好きなように改造して遊んでみるといいと思います。これまで使っていたputsも使えます。

 うまくいかなかった人は以下を試してみてください。うまくいった人にはすみませんが、ここまで含めてプログラミングです。

  • view.cgiのコードに間違いが無いか確認する
  • ブラウザの画面に出るエラーメッセージを確認して手掛かりを探す
  • ruby web.rbを実行した方のウィンドウで、最近出ているメッセージから原因を探す
  • 1行目に間違いが無いか確認する(1行目は絶対に#!で始まらないといけない)
  • 1行目の書き方を変える。C-Cbashに戻って、which rubyコマンドを実行する。実行した結果の文字列を1行目の"#!"の後に入れる。例えばwhich rubyの結果が/usr/bin/rubyだったら1行目は#!/usr/bin/rubyにする

 それでもわからなかった場合や、動いたけどなんで動いたのか気になった場合は、何をしたかも含めて文章でもスクリーンショットでも良いので添えて詳しい人に聞いてみてください。

 動いた人は、それがつまりWebサービスにつながる第一歩です。Twitterやブログなんかもここから始まっています。

倫理的なやつ

 ここで作ったプログラムには何の危険もありませんし、自分以外の誰にもアクセスできません。安全です。というのをわかった上で。

 これでなんらかのプログラムを使ったサービスをインターネットで公開できるようになりました。大抵はそのままではファイアウォールだったりネットワーク設定だったりに阻まれてそのままインターネットに公開というわけにはいきませんが、理論的にはこれで全世界に公開できるサービスを作ることができるようになったということになります。きっとそのために勉強をすればすぐにできるでしょう。

 ただし、ここから先、本当にインターネットに公開しようとすると色々な現実的な問題が発生します。今回はまだ外のシステムにアクセスするようなことはしていませんが、単に公開するだけでも、例えばどこか知らない誰かからの攻撃の危険にさらされます。プログラムに脆弱性があった場合、もしくはプログラムに非は無くとも利用しているWEBrickRubyそのものに脆弱性があった場合、そこを突かれてシステムが乗っ取られたり、不正アクセスのための踏み台にされることがあります。つまり直接的に誰かに迷惑をかけるだけでなく、知らないうちに悪事に加担してしまう恐れまである。それは非常にマズい。いわゆるハッキングとかクラッキングとかいうやつです。

 ということで、何か作って公開したいと思った時は、詳しい人に相談するか、インターネットセキュリティや倫理についてしっかり勉強するなどしてからやるようにしてください。

 実際のところ、悪事を働くための技術や守るための技術の理屈はすごく難しいですが、対策する方法自体はそんなに難しくないことが多いので、「これやっとけば大丈夫よ」くらいのアドバイスで済むことが多いです。

 ただそれはそれとして、クラッキングというやつの仕組みはそこそこ楽しいので、悪用しない前提で勉強するのは楽しいと思います。死ぬほど難しいけどそれなりの需要はあります。

それから

 今回はここで終わりです。今回はこれまでと違って説明を省いた部分が大量にあります。ここから先は、これまでのどこがおもしろかったかで進む方向を決めると良いでしょう。どの方向に行っても1歩目までは大抵サポートできると思います。

 ざっと思いつくのはこんな感じです。

  • もっと色々できるWebサービスを作りたい
  • ここで出てきたURLの意味がわからん(localhostとか:20080とか)
  • コードの中のWEBrick::HTTPServer.newのオプションは一体何(:DocumentRoot => './',とか:BindAddress => '127.0.0.1',とか:Port => 20080)
  • 文字化け直したい
  • trap("INT")の詳細が知りたい
  • WEBrickが何をしているのかを知りたい
  • そもそもrubyみたいなのを作りたい

 これ以外にも色々なことを思うかもしれません。ほとんどの共通部分は同じではありますが、特にどこが気になるかによってかなり分野が違っていたりするので、おすすめする本なども変わってくると思います。私も挙げた分についてはそこそこ知っているものもあるので聞いてみてもいいかもしれません。半分くらいRubyじゃないですけども。