10年前の世界からやってきたオッサンがCGIをSwiftで書いてみる

# タイトルの意味(≒この記事の概要)

* およそ10年ぶりに[個人サイト](https://YOCKOW.jp/-)[^personal-site]を更新してみた。
* 全てのページを[Perl](https://www.perl.org)によるCGIで生成していたが、[Swift](https://swift.org)で一から書き直した。
* しかし、サーバやCSS, HTMLに関する知識は10年前から変わっていない[^knowledge]。
* 即ち**10年前からタイムスリップしてきたことと同じ**だった(誇張)。

[^personal-site]: ここでいう「個人サイト」とは、「自分の自分による自分のためのサイト」のこと。

[^knowledge]: 実際には2003年ぐらいの知識で止まっているような感覚。そして、もともと知識は乏しい。

それを踏まえてSwiftでCGIプログラムを書いた経緯をインタビュー形式で記事にします。なぜインタビュー形式なのか、それは永遠の謎です。

一応、CGIプログラムについて基礎的なことから書いてあります。Swiftに限らずいろんな言語でCGIを書くきっかけになれば…。


# SwiftでCGI

## そもそもCGIって…

**通りすがりの知ったかぶりインタビュアー(以下I)**: [CGI](https://ja.wikipedia.org/wiki/Common_Gateway_Interface)という言葉自体が死語になりつつあるようですが？

**10年前の世界からやってきたオッサンこと[YOCKOW](https://qiita.com/YOCKOW)(以下Y)**: そうなんですか？昔は自分のサイトに*掲示板(死語)*や*チャット(死語)*を設置するために**フリーのCGIプログラム**をもらってくることが流行った気がします。でも、自分が借りてる*レンタルサーバー*の環境に合わなくてプログラムを修正しなきゃいけなかったり、細かい仕様が気に入らなかったり…。それなら自分で最初から作っちゃえば良いじゃん、ってなってCGIのプログラムを書き書きし始める…というのが一般的な流れでした(N=1)。そういう意味でCGIという言葉はサイト運営をする上で切っても切り離せないものでした。

I: もちろん今もCGIという仕組みが無くなった訳ではないですが、動的なページを作るのにCGIの仕様を意識しなければいけない環境は減っていると言えます。Webアプリケーションのバックエンドを作るにしても便利なフレームワークがありますし、クラウド・アプリケーション・プラットフォームなどというものまでありますからね。

Y: ただ、個人サイトを作るだけなら、そんな大層なソフトウェアを使わなくても…という気はします(10年前の感覚)。だって、CGIは標準出力に文字列(データ)を書き出すことができればそれで良い訳ですから。もちろん`POST`を処理するなら標準入力も扱えないといけないですけど。**なんなら[bash](https://www.gnu.org/software/bash/)でだってCGIは書けます**[^cgi-with-command-tools]。

[^cgi-with-command-tools]: 他の例→[Perlから始めないCGI入門](https://qiita.com/amanoese/items/c86eab16f08456b0fbae)

I: ところで、10年前にはSwiftは存在していませんでした[^swift-birth]。当時は何の言語でCGIを？

[^swift-birth]: Swiftは2014年6月登場。

Y: **当然**Perlですね。CGIといえばPerl、PerlといえばCGIという時代でした。次点でPHPでしょうか。Yahoo!知恵袋に**「CGIとPerlって何が違うんですか？」**みたいな質問がいくつもありました[^q-cgi-perl]。

[^q-cgi-perl]: 実例→ https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q147029824

I: 「法律と警察って何が違うんですか？」みたいな質問ですね。

Y: 確かに質問として本来成り立たないはずのものですが、それだけ**CGIといえばPerlという“常識”**があったのでしょう。


## CGIプログラムの実際

I: “CGIは標準出力に文字列を書き出すことができれば良い”という話がありましたが、具体的にはどうすればいいのでしょうか？

Y: 厳密な話は[RFC3875](https://tools.ietf.org/html/rfc3875)を読んでいただくとして、最低限`Content-Type`ヘッダ行と0バイト以上のボディーさえあればOKです。Perlで書くと

```Perl:simple.cgi
#!/usr/bin/perl

print "Content-Type: text/plain; charset=utf-8\n";
print "\n";
print "Hello, CGI.";
```

Y: これだけで"Hello, CGI."という文字を表示するCGIの完成です。ちなみに、HTTPヘッダの改行は必ず`CR+LF`でなくてはなりませんが、CGIプログラムの段階での改行コードは`LF`で問題ありません。なぜなら、Webサーバが**必ず改行文字を`CR+LF`に変換してくれる**からです[^rfc3875-section-6.3.4]。

[^rfc3875-section-6.3.4]: https://tools.ietf.org/html/rfc3875#section-6.3.4

I: ところで、上記のコードではHTTPレスポンスコードが指定されていませんが、どうなっているのでしょうか？

Y: 基本的に、CGIが正常終了すれば`200`、異常終了すれば`500`ですね。

I: え？それだけですか？

Y: まぁ、もちろん、それだけではないです。CGIでは`Status`という特別なフィールドが規定されています[^rfc3875-section-6.3.3]。他のHTTPヘッダと同じように`Status: 200 OK`などと出力すれば、Webサーバが**良きに計らってくれて**適切なHTTPレスポンスに変換されます。もちろん404なら`Status: 404 Not Found`とします。Reason Phraseを自分で決めることもできます。`Status: 500 I have given up`とかでも問題ありません[^reason-phrase]。

[^rfc3875-section-6.3.3]: https://tools.ietf.org/html/rfc3875#section-6.3.3

[^reason-phrase]: HTTP/2だとreason phraseが空文字列に…🥺

I: 文字列を出力するだけでいいなら、確かにどんな言語でも良さそうですね。

Y: そうですね。もちろんSwiftでもCGIプログラムを書くことが可能です。

```Swift:cgi.swift
#!/usr/bin/swift

print("""
  Status: 200 Swift is also OK
  Content-Type: text/plain; charset=utf-8
  
  Hello, Swift CGI.
  """)
```

I: **Swiftでも[shebang](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%90%E3%83%B3_%28Unix%29)が使える**んですね。

Y: そうなんです[^swift-shebang]。ただ、shebangを使うと単一ファイルのコードしか実行できないですし、<small>Swiftはお世辞にもコンパイルが早いとは言えないので(小声)</small>、予めコンパイルした実行ファイルをCGIプログラムとして起動することをお勧めします。

[^swift-shebang]: https://takuya-1st.hatenablog.jp/entry/2017/03/03/164033


## CGIに使える言語

I: 今回CGIを一から作り直すにあたって、最初から言語をSwiftにすると決めていたのでしょうか？

Y: それを話すと少し長くなりますが良いですか？

I: (え、めんどくさい…)

Y: 私が個人サイト[^personal-site]を立ち上げたのは1999年の春でした。確か「フリーティケットシアター」(freett.com)[^freett]を利用していましたね。

[^freett]: 2016年3月末でサービス終了

I: (話し始めちゃった…)

Y: そして、CGIが使えるレンタルサーバに移ったのが2002年頃です。その頃から一部のページをCGIで表示するようになり、遅くとも2003年にはほぼ全てのページをCGIで出力するようにしました。たぶん。

I: 先の話にあったように、その時使っていた言語はPerlだった訳ですね。

Y: そうです。最初の頃は[とほほのWWW入門](http://www.tohoho-web.com/)にお世話になりました。

I: 今や老舗のサイトですね。

Y: 当時、Mac OS X(macOSの当時の名前)用のソフトウェアを公開することも自分のサイトの目的の一つでした。そして、[Objective-C](https://ja.wikipedia.org/wiki/Objective-C)を触っているうちに気づいてしまったのです。

I: と言いますと？

Y: **オブジェクト指向は素晴らしい！**

I: あぁー…。

Y: ただ、今思い返すと、オブジェクト指向そのものが素晴らしいというよりは、[Cocoa](https://ja.wikipedia.org/wiki/Cocoa) (= Foundation + AppKit)が素晴らしいということだったとは思いますけどね。ちなみに、木下誠氏[^makoto-kinoshita]の書籍やWeb連載が好きでした。

[^makoto-kinoshita]: 現[エイチエムディティ株式会社](http://hmdt.jp/)社長。

I: なるほど。

Y: 本当にFoundationが好きすぎて、PerlでFoundationを再実装するような行為に勤しんでいました。もちろん、完コピではなく自分が必要とする部分だけの実装でしたが。たとえば、`NSString`に倣って書いた`HTB::String`(HTBは当時のサイト名にちなんだprefix)では、

```Perl:String.pm
sub stringByReplacingOccurrencesOfString_withString_options_range {
  my $self = shift;
  my $target = shift;
  my $replacement = shift;
  my $options = shift;
  my $searchRange = shift;

  # 以下実際のコード
  # :
}
```

Y: というようにメソッド(Perl用語ではサブルーチン)の名前を付けていました。

I: Foundationにおけるネーミング[そのまま](https://developer.apple.com/documentation/foundation/nsstring/1416484-stringbyreplacingoccurrencesofst?language=objc)ですね。

Y: ただ、Perl(Perl 5)は「オブジェクト指向*のような*書き方ができる」程度で、言語レベルでクラス定義などをサポートしていませんでした。…ですよね？当時、"Perl 6"が出る出ると言われて全然出なかったこともあり[^perl6]、Perl以外の言語も考えていました。しかし、2008年ごろから<ruby>現実世界<rt>アクチュアル・ワールド</rt></ruby>が忙しくなってプログラミングに時間を割けなくなりました。さらに、2010年頃は[ゆるキャラ](https://ja.wikipedia.org/wiki/%E3%82%86%E3%82%8B%E3%82%AD%E3%83%A3%E3%83%A9)や[某音ゲー](https://ja.wikipedia.org/wiki/REFLEC_BEAT)のことしか考えていませんでした。こうしてプログラミングのブランクが始まったのです。2012年に一念発起してCGIを一からPerlで書き直したのですが8割方完成したところで諸事情により再び中断することになりました。

[^perl6]: 2000年に設計が始まったPerl 6の正式リリースは2015年12月25日のこと。結局Perl 5との互換性はなく、のちに言語名がRakuに変わった。

I: (途中のよくわからない話は無視して)次にプログラミングを始めるのは？

Y: まずは**2016年**にVPS(Virtual Private Server)を借りることにしました。多少(時間や金銭に)余裕ができたので。レンタルサーバが公園の砂場なら、VPSは無人島[^desert-island]というところでしょうか。[『何もないから、なんでもできる』](https://www.google.com/search?q=%22%E4%BD%95%E3%82%82%E3%81%AA%E3%81%84%E3%81%8B%E3%82%89%E3%80%81%E3%81%AA%E3%82%93%E3%81%A7%E3%82%82%E3%81%A7%E3%81%8D%E3%82%8B%22)みたいな。でも、すぐに何かをしたいというよりは、いつか何かできたらいいなという感じでした。最初、CGIに使う言語としては[Ruby](https://www.ruby-lang.org/)を考えていました[^without-rails]。日本発の言語で日本語の資料も豊富にあったというのも理由です。Rubyでのプログラミングは実際楽しかったです。Perlの`AUTOLOAD`[^perl-autoload]をRubyでも実現させてみたり。でも、『Rubyは遅い』みたいな話もあったりして。ここらへんは結局宗教論争のようなところはあるので、どうでもいいと言えばどうでもよかったのですが…。そもそも速さを求めるようなサイトではないですし。それでもなんとなく速さのことが気になって…。そこで、速さのことを考えるならそもそもインタプリタ方式ではなくて**コンパイラ方式にすればええやん**と思ったのです。VPSなら色んなコンパイラを導入できますしね。

[^desert-island]: 専サバに比べれば狭い無人島なのでしょうが…。

[^without-rails]: もちろんRailsは使わない

[^perl-autoload]: 参考記事[『AUTOLOADについて』](https://qiita.com/mp0liiu/items/c1205cba9ea22cbdbbf8)

I: なるほど、それでSwiftに？

Y: いいえ、**[C言語](https://ja.wikipedia.org/wiki/C%E8%A8%80%E8%AA%9E)** です。

I: まさかのC。…なぜSwiftではなかったのでしょう？Swiftは2015年末にはオープンソース化もされ、名目上Linux対応も謳っていましたが。

Y: ただ単に**Swiftを知らなかった**のです。Mac OS X用のソフトウェアを作ったのは2005年が最後でした[^last-mac-software]。そこからはMacソフト開発からは遠ざかっていたので、Swiftなどというものの存在に触れる機会がなかった訳です。結果的に、まずはC言語を試すことになりました。

[^last-mac-software]: 最後に作ったソフトは"DKT"という[ディベート甲子園](https://nade.jp/koshien/)専用のタイマー。2005年に[愛・地球博](http://www.expo2005.or.jp/)で開催された第10回大会で実際に使用した。

I: そういえば、かの「2ちゃんねる」(現・5ちゃんねる)も軽量化のためにかつてPerlからC言語に移行したなんていうこともありました<sup>*\[要出典\]*</sup>。

Y: C言語でまずやり始めたのは…また**Foundationの猿真似**でした。2005年頃にPerlでやっていたことを再度始めたのでした。これも今思い返してみれば、[Core Foundation](https://developer.apple.com/documentation/corefoundation)を使えばそれで良かったんですよね。Objective-CのFoundationはプロプライエタリですが、C言語で書かれた**Core Foundationはオープンソースかつクロスプラットフォーム**ですからね。どうしても**車輪の再発明が大好きな<ruby>質<rt>たち</rt></ruby>なので**、一から作ることを先に考えちゃうんですよね。まぁ、それはそれで楽しかったです。メモリ管理とか、ディクショナリのためのハッシュ関数を自作したりとか。楽しかったけど、もちろん、面倒でした。なので、C言語から別の言語を使うことを考え始めて…。

I: そこでSwift？

Y: いいえ、**Objective-C**です。

I: おっと。もしかしてサーバをMacに…？

Y: Linuxです。Objective-Cは決してMac専用の言語ではありません。その誕生は1983年(または1984年？)と古く、[GCC](https://gcc.gnu.org/)も標準でObjective-Cをサポートしています。もちろんFoundationは前出の通りプロプライエタリなのでLinuxでは使えませんが、[GNUstep](http://gnustep.org/)があります。GNUstepはNeXTのOPENSTEPと互換性を持たせたフリーソフトウェアです[^gnustep]。ご存知の通り、NeXTはのちにAppleに買収され、OPENSTEPのObjective-CライブラリはCocoaと名前を変え開発が進められることになります。GNUstepはAppleのCocoaに追随しており、Mac, Linux両方で開発を進められると思っていたのです…。

[^gnustep]: Unix系OSだけでなくWindowsでも動作する。

I: 思って*「いた」*？

Y: はい。実際初期のコードはMacでもLinuxでも動作したのですが、FoundationのAPIをいろいろ調べているうちに違和感を覚えるようになったのです。…違和感の正体は[Objective-C 2.0](https://ja.wikipedia.org/wiki/Objective-C#Objective-C_2.0)でした。Objective-C 2.0はMac OS X v10.5 Leopardとともに登場したものです。つまり2007年登場です。当時(2016年頃)で既に登場から10年ほど経っているので、Objective-C 2.0は当たり前のように使われていました。しかし、前述の通り私が最後にMac OS X用のソフトウェアを作ったのは**2005年**のことです。Objective-C 2.0などというものを知る由もありませんでした。調べていくと、Linuxでも[libobjc2](https://github.com/gnustep/libobjc2)を使えばObjective-C 2.0が利用できると分かったのですが、自分の中でObjective-C 2.0は新しい言語を覚えるような気分でした。一方で、FoundationのAPIをApple Developerのサイトで調べているときに気づいたことがありました。

I: それは…？

Y: ページの右上に"Language"を選択するメニューがあったのです。Languageといっても日本語や英語という意味ではなく、プログラミングの言語を選択するメニューです。…Objective-CとSwiftが並んでいました。そこから「Swiftって何だろう？」と調べるようになりました。その時、Swiftのバージョンは2.2で、もうすぐ3が登場するだろうというタイミングでした。最初に見た時は、一見してスクリプト言語のような印象だったので、**AppleScriptの進化版か何か**かと思っていました。でも、Qiitaで[@koherさん](https://qiita.com/koher)や[@omochimetaruさん](https://qiita.com/omochimetaru)の記事を読んだりして、思ったのです。**「SwiftはObjective-Cを置き換えようとしている」**と。実際にAppleがどういう意図をもってSwiftを開発しているのかはわかりませんでしたが、そう思った瞬間、自分のCGIプログラムの言語はSwiftにすることに決まりました。Linuxでも使えるということはもちろん、今後、[Darwin](https://ja.wikipedia.org/wiki/Darwin_%28%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%29)プラットフォーム(macOSやiOS)のソフトウェアを開発するときにも必ず役に立つと考えたからです[^darwin-software]。

[^darwin-software]: 結局開発してませんが。

I: やっとSwiftに辿り着きましたね(長かった…)。


## CGIとSwift

Y: まずはLinuxでSwiftコンパイラを動かすところから始めました。当時はCentOSを使っていたのですが、CentOS用のバイナリが配布されておらず、Swiftを2時間ぐらいかけてビルドした記憶があります。しかもFoundation(Swiftで書かれたFoundation)のビルドに失敗してやり直しになるという…。のちにUbuntuに乗り換えたのですが、今はCentOS以外にもAmazon Linux 2用のバイナリまで配布されるなど、便利になりました。

I: 今はLinuxどころかWindows用までありますからね[^swift-for-windows]。

[^swift-for-windows]: https://swift.org/download/#releases

Y: 当時既にSwiftはオープンソースになっていたのですが、Swiftで検索してもDarwin以外のプラットフォームで動かす情報が少なかったように思います。そこで、少しずつ自分が得た知見を共有しようと、Qiitaの記事もたまに書くようになりました。初めて書いた記事は、[『C言語とSwiftを一つのプロジェクトで混在させてみる(OS X, Linux)』](https://qiita.com/YOCKOW/items/3b56f546f2bf9bbce5f6)(2016年5月)です。[Swift Package Manager](https://swift.org/package-manager/)がまだ無かった時代の記事ですね。ちなみに、これまでに投稿したSwiftに関する記事は**全てDarwinでもLinuxでも動作するコード**を前提に記載しています。

I: CGIだけであればSwiftUIとかは関係ないですからね。

Y: そしてSwiftに多少慣れたタイミングでCGIプログラムを作るためのライブラリを作り始めました。その名も[SwiftCGIResponder](https://github.com/YOCKOW/SwiftCGIResponder)。`CGIResponder`という[単純なstruct](https://github.com/YOCKOW/SwiftCGIResponder/blob/d0f488199b24eb95e682bba96835509533f3d9db/Sources/CGIResponder/CGIResponder.swift#L14)を用意して、CGIとしての出力を任せるというものです。

I: 一応、その頃には[Kitura](https://www.kitura.dev/)も[Vapor](https://vapor.codes/)も登場済みだとは思いますが…。

Y: そうだったんですね。全然気にしてませんでした。Perlの時代と同じように、一からCGIプログラムを書くことしか考えていませんでした。ただ、Perlの時代とは大きく違うことがありました。それは、**Foundationが最初から存在する**ということです。PerlのときもRubyのときもCのときも、まず自分で*Foundationっぽい何か*を作るところから始めていました。Foundation大好きっ子ですからね。それがSwiftではLinuxでもFoundationが使えるのです。

I: Foundationがあれば充分だった、と。

Y: その通りです。ですが、それはそれで単純ではありませんでした。Darwinでは、Swiftで使うFoundationはObjective-CのFoundationをインポートする形です。即ちCocoaを(原則)そのままSwiftでも使えます。しかし、Darwin以外のプラットフォームでは、Swiftで書かれたFoundation[^swift-foundation]を使うことになります。そのSwiftFoundationが、**バグと未実装の嵐**だったのです。

[^swift-foundation]: 多くはCore FoundationをSwiftでラップしたもの。

I: となると、Swift以外の言語へ移ることも検討を？

Y: そんなことはしません。Foundation大好きっ子なので(2回目)。**バグがあるなら直せば良いじゃないですか**。幸いにもSwiftはオープンソースです。GitHubでPRを送ることもできます。というわけで、あると困るバグは[自分で直しました](https://github.com/apple/swift-corelibs-foundation/pulls?q=author%3AYOCKOW)。こうしてFoundationを使ってCGIプログラムを作るという、**夢のような世界**に到達できたのです。今のところ、SwiftCGIResponderを使うと、

```Swift:CGIResponserSample.cgi
import CGIResponder

var responder = CGIResponder()
responder.status = .ok
responder.contentType = ContentType(pathExtension: .txt, parameters: ["charset": "UTF-8"])!
responder.content = .string("Hello, World!\n", encoding: .utf8)
try! responder.respond()

// -- Output --
// Status: 200 OK
// Content-Type: text/plain; charset=UTF-8
//
// Hello, World!
//
```

Y: のように書くことができます。基本的に`CGIResponder`は単純で、上のコードを見るだけだと「直接文字列を出力すればいいのでは？」となりそうですが、その裏では、HTTP Headerを適切に扱うような仕組み・国際化ドメイン名を含めたドメインの処理・HTTP Cookieと[Public Suffix](https://publicsuffix.org/)の処理・XHTMLパーサ/ジェネレータなどを実装してあります。ただ、コードサイズが大きくなってしまったので、結果として、少しずつ機能を切り離して独立したパッケージにしました。見かけ上、依存するパッケージが多くなりましたが、全部自分で作ったものという…。

> https://github.com/YOCKOW/SwiftCGIResponder#dependencies
> 
> ![SwiftCGIResponder's Dependencies](https://raw.githubusercontent.com/YOCKOW/SwiftCGIResponder/master/dependencies.svg)

I: 車輪の再発明も含まれていそうですね…。

Y: そうですね。でも、車輪の再発明も大好きなので。こうして、2008年ごろから更新が滞り始め2012年には完全に止まってしまったサイトの更新を2021年に再開することができました[^restart]。サイトの更新が目的となってしまっているので中身はないですし、CSSやHTMLの知識は昔のままですが。気付いたら、HTMLはフロントエンドで生成することが主流になっているようで(？)、完全に時代に取り残されていますね[^frontend-html-generation]。本当は[Markdown](https://daringfireball.net/projects/markdown/)のパーサもSwiftで書きたかったのですが、一旦はフロントエンド側で処理することにしました。ただ、その時には**「JavaScript怖い」**という思いは強かったです。

[^restart]: いつまた止まるか分かりませんが。

[^frontend-html-generation]: 実は2012年に作り直した(作り直そうとした)サイトはJavaScriptでページを生成する仕組みでした。しかし、当時のGoogleクローラは動的ページに対応しておらず(たぶん)、JavaScript非対応のクライアントにはPerlでページを生成して返していました。つまり、PerlとJavaScriptで同じ結果を生み出すための同じコードを書かなくてはならず、2倍の作業量になっていたのです。時代を先取りしすぎたのかもしれません…。

I: 何が怖かったのでしょう？

Y: 動的型付けだからです。Swiftを長く触ったあとに久しぶりにJavaScriptを触ったので、コード上に型表記できないことやnull safetyがないことに強烈な恐怖心を覚えました。そこからTypeScriptを導入するまではすぐでした。Swiftの素晴らしさの再発見でもありますね。


# おわりに: SwiftはCGIに相応しい言語か

I: Swift(というかFoundation)への想いは強いようですが、SwiftはCGIプログラムに相応しい言語と言えるでしょうか？

Y: 現在のところ最も相応しいですね、**私にとっては**。当たり前ですが、言語の選択は「誰が何のためにどのような環境で動かすプログラムなのか」によって変わってくるものですから。CGIプログラムに限らない話ですね。最初のほうの話にもあったように、CGIプログラムは標準出力と標準入力を扱うことができればそれで良いのです。bashが一番自分にとって書きやすいのであればbashで書けば良いと思いますし、Cが良ければCで書けば良いという話です。少なくともフレームワークがないと何もできないということはあり得ません。まずは`print`文から始めれば良いと思います。Swiftなら[SwiftCGIResponder](https://github.com/YOCKOW/SwiftCGIResponder)を使っても良いですよ(宣伝)。まぁ、あくまで個人サイト[^personal-site]の開発に限った話ですが。