Webリクエストの処理

本章ではApp Engine上でユーザ (ブラウザ) から送られたHTTPリクエストがどのような道筋をたどってくるのかを見ていきます。App Engineのアーキテクチャですね。とても重要なところだと思うのでしっかり学習していきます。

App Engineのアーキテクチャ

App Engine上でユーザからのHTTPリクエストがどのような道筋を通るのかをざっくりと図にしてみました。



フロントエンドサーバは受信したリクエストのURLがapp.yamlの中でマッチしない場合はHTTP 404 (Not found) を返します。この図を見るとapp.yamlがどのように使われるのかがよく分かります。

また、アプリケーションサーバがリクエストハンドラを起動する環境 (ランタイム環境) はサンドボックスとよく言われます。これはサーバ上のファイルシステムにアクセスしたり、他のコンピュータにネットワーク接続出来ない、砂場 (Sandbox) なわけです。でもそんな環境ではあまり大したことは出来ません。なので、App Engineのランタイム環境にはいろいろなサービスが利用可能なわけです。

ここで注目すべきは、フロントエンドサーバは圧縮レスポンス (gzip) に対応しているということです。もちろんリクエストハンドラ側はそんなことは知らなくても平気です。

フロントエンドの設定

フロントエンドの設定は全てapp.yamlファイルで行います。設定の仕方を学習していきます。

アプリケーション設定

アプリケーションの設定でもっとも基本的なものはランタイムとそのバージョンです。

runtime: python
api_version: 1


これはPythonランタイム環境のバージョン1を使うと宣言しています。ちなみに現時点でPythonランタイムのバージョンは1しかありません。

ドメイン

こちらの内容はapp.yamlには関係ない内容ですが、appspot.com上にアプリケーションを配置する場合、

app-id.appspot.com/url/path...


というドメイン名でアクセスすることになります。んでちょっと分からなかったのがドメイン名の追加。本書によると

anything.app-id.appspot.com


というドメイン名もいけるらしい。とうことで先日アップロードしたアプリで試してみました。もちろん

kemuo-clock.appspot.com


でアクセス出来るのですが、確かにかかれていたように

hogehoge.kemuo-clock.appspot.com


でもアクセスすることが出来ました。ホスト名はプログラム上で識別可能ですので、出力内容を変えることは可能です。

class MainPage(webapp.RequestHandler):
    def get(self):
        logging.debug('Host: %s' % self.request.headers['Host']);
        # ...


app.yamlでの設定は出来ないようです。

App IDとVersion

アプリケーションのIDとバージョンはapp.yamlで次のように指定します。

application: helloworld
version: 1


そうです。App Engineはバージョンでアプリケーションを管理することが出来るのです。これってとっても便利ですよね。↑で示したようにバージョン指定がないURLではデフォルトバージョンのアプリケーションを起動します。もちろんバージョンを指定することも可能です。


バージョン1を指定する場合

1.latest.kemuo-clock.appspot.com


なお、バージョンは数字である必要はありません。開発中のバージョンであればこんなバージョンもありでしょう。

application: helloworld
version: dev
dev.latest.kemuo-clock.appspot.com
リクエストハンドラ

リクエストハンドラとは、ユーザからのHTTPリクエストをどのように処理するのかを指定する項目のことです。たとえば、「/image/」というパスで始まるリクエストは静的ファイルサーバに配信し、「/」というパスはアプリケーションサーバに配信する (スクリプトを起動する) などを指定します。

handlers
- url: /images
  static_dir: images
- url: /css
  static_dir: css
- url: /.*
  script: main.py


これは以下を意味します。

ブラウザからのアクセスパス 配信先サーバ 備考
/images以下 静的ファイルサーバ ./imagesの静的ファイルを返却
/css以下 静的ファイルサーバ ./cssの静的ファイルを返却
上記以外全て アプリケーションサーバ ./main.pyを起動


url要素は正規表現を使うことが出来ます。後は何となく分かりますよね。static_dirで静的ファイルサーバ、scriptでアプリケーションサーバを指定出来ます。

静的ファイルおよびリソースファイル

↑でも見たように静的ファイルであることの指定はapp.yamlで行います。が、肝心の静的ファイルはどのように扱われるのでしょうか?それはデプロイ時に決まります。デプロイ時に静的ファイルは全て、静的ファイルサーバ側に送られます。つまりスクリプトを実行するアプリケーションサーバ (上のランタイム環境) からはこの静的ファイルにアクセスすることは出来ないのです。まったくの別管理というわけです。

その一方でアプリケーションサーバに転送されるファイルのことをリソースファイルと言います。静的ファイル以外のファイルです。スクリプト上からこれらのファイルに読み込み専用でアクセスすることが可能です。

このようにApp Engineでは静的ファイルとリソースファイルが全く別に扱われるので
きちんと区別して管理する必要があります。

こちらもフロントエンドサーバの仕事になりますので、app.yaml上で指定します。

handlers:
- url: /images/.*/(.*)
  static_files: images/\1
  expiration: "90d"
  upload: images/.*


expiration要素は静的ファイルの有効期間です。ブラウザ側でのキャッシュ期間ですね。だいたい静的ファイルというものは変わらないものなので、かなり長め (↑の例では90日間) に設定します。でもやっぱり画像やJavascriptファイルなんかを差し替えたい時ってありますよね。その場合は次のようにURLを動的に設定してやることで解決します。

- url: /images/.*/(.*)
  static_files: images/\1
  expiration: "90d"
  upload: images/.*
self.response.out(
                '<img src="/images/' +
                os.environ['CURRENT_VERSION_ID'] +
                'sample.png' />')

※この手法を使う場合、static_dir指定ではうまく行きませんでした。なのでディレクトリではなく、ファイルを指定するstatic_files要素で指定しています。なお、その場合upload要素で何をディプロイするのかをきちんと指定してあげる必要があります。


これはアプリケーションのバージョンがアップされる度にsample.pngへのパスが変わります。でもapp.yamlを見ると分かるようにパス中に現れるバージョン番号はあまり意味がありません。しかしブラウザはパスが変わったので有効期間に関わらず再取得するわけです。これ、使えますね。

セキュアな接続

HTTPS接続のことですね。HTTPS接続はappspot.comドメインでしか使うことが出来ません。カスタムドメインで使う場合は、一度appspot.comドメインにリダイレクトする必要があります。指定の方法は至って簡単。app.yamlで設定します。ということはフロントエンドサーバの仕事になります。

handler:
- url: /users/.*
  script: users.py
  secure: always


secure要素で指定可能な値は以下の通りです。

説明
always 常にHTTPS接続を要求する
never 常にHTTP接続を要求する
optional HTTPS, HTTPいずれの接続も受け入れる


デフォルトはoptionalです。optionalとした時、スクリプト上でHTTPS接続なのかHTTP接続なのかを判定することが可能です。


ちなみにApp Engineでは以下のようにポート番号が決められています。

プロトコル ポート番号
HTTP 80
HTTPS 443
Googleアカウントでの認可

Googleアカウントでユーザを識別する方法は二通りあります。一つはユーザ管理サービスを利用する方法です。先に作成したアプリ (clock) ではこの方法を使いました。それとは別に、フロントエンドサーバに任せてしまう方法もあります。

handler:
- url: /account/.*
  script: account.py
  login: required


予想通りの動きをしてくれます。Googleアカウントにログインしていなければログイン画面に遷移し、ログインしたら元のサイトにリダイレクトされます。login要素に設定出来る値はこんな感じ。

説明
required ログインしていないとログイン画面に遷移する
admin 開発者アカウントでログインしていないとアクセス出来ない


admin指定を使えば、簡単に管理者用サイトを作ることが可能ですね。