Server-side Swift, Perfectを使ってHelloWorld ~HTTP Server編~
あけましておめでとうございます。 前回のSwiftのLinux環境を作った記事の続きです。
今回のゴールはSwiftのフレームワークであるところのPerfectを用いてHTTPレスポンスを返すところがゴールになります。
環境等は前回の続きです。
とりあえずログイン。 今回はgitを利用するので、鍵等はssh-agentを利用していきます。
$ ssh-add ~/.ssh/github用key $ vagrant ssh
サーバを立ててみる
Perfectのダウンロード
$ git clone git@github.com:PerfectlySoft/Perfect.git
Perfectを利用する場合、まずはサーバとライブラリのビルドをする必要があります。
$ cd Perfect/ $ make
Makefile
の中身はこちら。
どうやらPerfectLib
のMakefileとPerfectServer
のMakefileを叩いているようです。
ちなみにPerfectServer
のMakefile
は下記です。
# Makefile for Perfect Server TARGET_FCGI = perfectserverfcgi TARGET_HTTP = perfectserverhttp DEBUG = -g -Onone -Xcc -DDEBUG=1 OS = $(shell uname) SWIFTC = swift SWIFTC_FLAGS = -frontend $(DEBUG) -c -module-cache-path $(MODULE_CACHE_PATH) -emit-module -I /usr/local/lib -I ../PerfectLib/linked/LibEvent \ -I ../PerfectLib/linked/OpenSSL -I ../PerfectLib/linked/ICU -I ../PerfectLib/linked/SQLite3 -I ../PerfectLib/linked/LinuxBridge MODULE_CACHE_PATH = /tmp/modulecache Linux_SHLIB_PATH = $(shell dirname $(shell dirname $(shell which swiftc)))/lib/swift/linux SHLIB_PATH = -L$($(OS)_SHLIB_PATH) LFLAGS = $(SHLIB_PATH) -g -lFoundation -lswiftCore -lswiftGlibc /usr/local/lib/PerfectLib.so -Xlinker -rpath -Xlinker $($(OS)_SHLIB_PATH) all: modulecache $(TARGET_FCGI) $(TARGET_HTTP) install: all ln -sf `pwd`/$(TARGET_FCGI) /usr/local/bin/ ln -sf `pwd`/$(TARGET_HTTP) /usr/local/bin/ modulecache: @mkdir -p $(MODULE_CACHE_PATH) $(TARGET_FCGI): $(TARGET_FCGI).o clang++ $(LFLAGS) $@.o -o $@ $(TARGET_HTTP): $(TARGET_HTTP).o clang++ $(LFLAGS) $@.o -o $@ $(TARGET_FCGI).o: main_fcgi.swift $(SWIFTC) $(SWIFTC_FLAGS) main.swift $< -o $@ -module-name $(subst .o,,$@) -emit-module-path $(subst .o,,$@).swiftmodule $(TARGET_HTTP).o: main_http.swift $(SWIFTC) $(SWIFTC_FLAGS) main.swift $< -o $@ -module-name $(subst .o,,$@) -emit-module-path $(subst .o,,$@).swiftmodule clean: @rm *.o
どうやらswift -frontend
でオブジェクトファイルを生成し、clang++
でコンパイルしているようです。
make
すると、perfectserverfcgi
とperfectserverhttp
が生成されて、make install
するとコマンドのパスを通すところまでやってくれます。
上記コマンドをコンパイルするときにPerfectLib
に依存しているため、そちらのビルドが先に必要なようです。ナルホド。
これでサーバを立てる準備が整いましたので、とりあえずHTTPサーバを起動してみます。
下記の通りデフォルトでは8181
番ポートに割り当てられています。
$ Perfectserverhttp Current working directory: /home/vagrant/Perfect/PerfectServer Starting HTTP server on 0.0.0.0:8181
とりあえずリクエストを投げてみます。
curl localhost:8181 The file "/" was not found.
HTTPレスポンスが返ってきました。どうやら動いてるっぽいですね。
次に出力用のHTMLを用意して動作を確認します。
main_http.swiftファイルを見るとわかりますが、デフォルトのドキュメントルートはプロジェクト直下のwebroot/
になっていました。
PerfectServerはHTMLあるいは画像リソース、Mustache
のテンプレートを扱うことができますので、とりあえず適当なHTMLを用意。
$ echo 'Hello World!!' > webroot/index.html
実際にHTMLを解釈してレスポンスが返ってくるか確認しましょう。
$ curl localhost:8181 Hello World!!
正しくレスポンスが返ってきていそうです。非常に簡単ですね。
ルーティング用のスクリプトを書いてみる
PerfectServerのREADMEを読む限り、ハンドラー用のModuleを作成して利用する機能があるようです。
ModuleはPerfectServerModuleInit
という関数をもった共有ライブラリとして作成し、PerfectLibraries
ディレクトリに配置することで、PerfectServerに登録されます。
PerfectServer.swift
ファイルのこの辺りを見るとその様子が伺えます。
また、実際にルーティングを行っているサンプルがこちらにあります。
下記がルーティングの登録部分の抜粋です。
PerfectServerModuleInit()
内でRouting.Handler.Register()
を呼び出して登録し、Routing
のRoutes
(RoutMap構造体
)に対して、パスと振る舞いを表すクラスを登録することで、動作を定義することができます。
public func PerfectServerModuleInit() { // Install the built-in routing handler. // Using this system is optional and you could install your own system if desired. Routing.Handler.registerGlobally() Routing.Routes["GET", ["/", "index.html"] ] = { (_:WebResponse) in return IndexHandler() } Routing.Routes["/foo/*/baz"] = { _ in return EchoHandler() } Routing.Routes["/foo/bar/baz"] = { _ in return EchoHandler() } Routing.Routes["GET", "/user/{id}/baz"] = { _ in return Echo2Handler() } Routing.Routes["POST", "/user/{id}/baz"] = { _ in return Echo3Handler() } // Check the console to see the logical structure of what was installed. print("\(Routing.Routes.description)") }
振る舞いを表すクラスは、RequestHandler
を継承しhandleRequest()
を実装したものとなります。
リクエストを表すWebRequest
のオブジェクトが渡ってくるので、それを用いて特定の処理を行い、WebResponse
のrequestCompletedCallback()
を呼び出すことでレスポンスを返す事ができます。
サンプルではレスポンスボディに文字列を含めて返すものを実装していました。
class EchoHandler: RequestHandler { func handleRequest(request: WebRequest, response: WebResponse) { response.appendBodyString("Echo handler: You accessed path \(request.requestURI()) with variables \(request.urlVariables)") response.requestCompletedCallback() } }
上記サンプルを参考にして、Hello World
を返す処理を実装してみます。
まずはModuleのソースを用意します。今回はこんな感じで実装しました。
import PerfectLib public func PerfectServerModuleInit() { Routing.Handler.registerGlobally() Routing.Routes["GET", "/"] = { _ in return IndexHandler() } print("\(Routing.Routes.description)") } class IndexHandler: RequestHandler { func handleRequest(request: WebRequest, response: WebResponse) { response.appendBodyString("Hello World!! with Request Handler\n") response.requestCompletedCallback() } }
次にソースをコンパイルしていきます。
$ swift -frontend -c -module-cache-path /tmp/modulecache -emit-module -I /usr/local/lib -I /home/vagrant/Perfect/PerfectLib/linked/LibEvent -I \ /home/vagrant/Perfect/PerfectLib/linked/OpenSSL_Linux -I /home/vagrant/Perfect/PerfectLib/linked/ICU \ -I /home/vagrant/Perfect/PerfectLib/linked/SQLite3 -I /home/vagrant/Perfect/PerfectLib/linked/LinuxBridge \ "URLHandlers.swift" -o URLRouting.o -module-name URLRouting -emit-module-path URLRouting.swiftmodule $ clang++ -L/home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/lib/swift/linux \ -lFoundation -lswiftCore -lswiftGlibc /usr/local/lib/PerfectLib.so \ -Xlinker -rpath -Xlinker /home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/lib/swift/linux \ -shared URLRouting.o -o URLRouting.so
結果、下記のファイル群が生成されました。
drwxrwxr-x 2 vagrant vagrant 4096 Jan 8 02:23 ./ drwxrwxr-x 3 vagrant vagrant 4096 Jan 8 01:31 ../ -rw-rw-r-- 1 vagrant vagrant 395 Jan 8 02:23 URLHandlers.swift -rw-rw-r-- 1 vagrant vagrant 9624 Jan 8 02:23 URLRouting.o -rwxrwxr-x 1 vagrant vagrant 20003 Jan 8 02:23 URLRouting.so* -rw-rw-r-- 1 vagrant vagrant 27924 Jan 8 02:23 URLRouting.swiftmodule
このうち、共有ファイルであるところのURLRouting.so
をプロジェクト直下の PerfectLibraries
ディレクトリにつっこんでperfectserverhttp
を起動すれば動作が確認できます。
mkdir -p /home/vagrant/Perfect/PerfectLibraries/ cp URLRouting.so /home/vagrant/Perfect/PerfectLibraries/.
サーバを立てなおして動作確認をします
$ perfectserverhttp
$ curl localhost:8181 Hello World!! with Request Handler
作成したモジュールが読み込まれて、文字列の出力が確認できました。
次回はPerfectが採用しているテンプレートエンジンであるところのMustacheあたりを触ってまとめようかと思っています。