技術的なやつ

技術的なやつ

9.1 サーバ組んだ(まだ公開サーバじゃない)

Why

  • コンちゃんを動かしていた借り物Macを返却することになった
  • 今やりたいことにサーバが必要っぽい
  • どうせなら勉強ついでにPCを組んで自宅サーバにしたい

すぺっく

今までPCを組んだことはない。用途を考えるとクソザコでも良いんじゃね~、ッて思ってたが、なんか不安なので非常に中途半端なスペックになってる気がする。

種類 商品名 購入場所 値段
マザーボード GA-H81M-DS2V ヨドバシカメラ京都 5370
CPU Pentium G3220 ヨドバシカメラ京都 7170
メモリ UMAX Castor LoDDR3-4GB-1333 ヨドバシカメラ京都 4880
HDD TOSHIBA DT01ACA100 (1TB) ドスパラ 5910
ケース ENERMAX Fulmo Q ECA3360B-BT(U3) ドスパラ 3480
電源 Thermaltake PS-TR2-0450NPCBJP-B ドスパラ 6253
光学ドライブ ASUS DRW-24D3ST ドスパラ 2270
DVI-HDMI変換ケーブル 変換名人 DVHD-18GS ドスパラ 699

合計、36032円也。微妙に高いと思う。
なお、OSはUbuntu14.04をUSBメモリからインストールした。サーバ用途でUbuntuって不思議な気もするけど、使い慣れてるのが一番かなぁと思った。
マザーボード付属のCD、インストール必須かと思ったらそうじゃなかったみたい。光学ドライブは要らなかった。

組んでみた感想

手先が不器用な自分だが、思ったより簡単だった。プラスドライバーが一本あれば十分。
一番苦労したのは、ケースのフロントパネルを開けるところ。説明書にもなんかよく分からないイラストしか載ってなくて、いろいろやってるうちにどうにか開けることが出来た*1
ケースに付いてたクーラーのピン数と、マザーボードSYS_FANのピン数が合わなかったので、挿すのは止めておいた。挿したら常に全力でクーラーが回ることになるので、五月蝿くなると考えた。もちろん、CPUクーラーは回している。時々様子を見て、熱がヤバそうならクーラーを回すことにする。

お名前

このマシンの名前はsyaroに決めた。
理由: 見た目の割に(?)中身が貧乏なのと、そこそこ頭(CPU)が良いから。

f:id:ibako31:20150211222232j:plain

syaroです。

コンちゃんの復活

rubyとかsqliteとかtwitter gemとかmecabとか、まぁ色々入れて復旧した。環境構築は30分位で終わった。apt-get様は偉大。Macの環境構築はクッソ面倒だった記憶がある。

apacheをインストール

サーバを立てたいので、apacheをインストールした。It works!まで行った。

redmineをインストール

タスク管理に便利だと噂のredmineをインストールした。
インストールは結構骨の折れる作業だと聞いていたが、このページを参考にすれば20分くらいで終わった。
ついでにMySQLがインストールされた。

ぐえ

ぐえぐえ

*1:なんで開いたのか最後まで分からなかった

8.3 Web API: The Good Parts 5章~6章(完)

第5章 設計変更をしやすい Web API を作る

APIはなるべく更新しない

  • APIが変わるとそれに依存するコードは期待通りの動作をしなくなる可能性が高い
  • LSUDs向けAPIでは告知が大変
  • SSKDs向けAPIではモバイルクライアントのバージョン問題
  • 自分たちの使っているアプリではユーザのキャッシュ問題

バージョン管理

  • パラメータでバージョン番号を取得する方法は微妙
    • 省略された場合の動作は「最新のバージョンで対応する」というようにはいかない
      • ユーザがバージョンについて意識していない可能性がある
  • URIのパスの中にバージョンを埋め込むのが良い
  • できるだけバージョンアップは避ける
    • たとえば数値を返していたパラメータgenderを文字列を返すようにしたい場合、genderStrを新たに設けて、後方互換性を保つ(バージョン番号の変化しない)変更のほうが良い

バージョン番号の付け方

  • 1.0.0のようにメジャー・マイナー・パッチの3つの数字で表すのが良い。
    • メジャーバージョンは後方互換性のない変更の時
    • マイナーバージョンは後方互換性のある変更の時
    • パッチバージョンはシステムのインターフェースに変更のないバグ修正の時
  • よって、APIURIに含めるバージョン番号はメジャーバージョン番号のみにするのが良い
    • また、バージョン番号を示すことを明示するため「v1」のような形式にするのが良い

提供終了

  • APIの公開が終了した時は、ステータスコード410(Gone)を返す仕様にしておく
  • 利用規約にサポート期限を明記する
    • 1年くらいが目安?最低6ヶ月
    • 下手に長い年数を記述すると、多くのAPIをメンテナンスする必要が出てくるかもしれない

第6章 堅牢な Web API を作る

サーバ・クライアント間での情報の不正取得対策

  • HTTPSにより通信を暗号化する

XSSXSRF対策

  • Content-Typeをきちんとapplication/jsonとする
  • IEのContent Sniffering対策にはX-Content-Type-Optionsnosniffとする
  • 通常のブラウザでは送信されないリクエストヘッダの有無を確認する
    • X-Requested-WithXMLHttpRequestであるか
  • データを16進数エスケープしておく
  • セッションごとにユニークなトークンを作り照合する(XSRF対策)
    • 直接APIを叩かれることを防ぐ

悪意あるユーザからのアクセス対策

  • パラメータ改竄の可能性を考え、サーバサイドでバリデーションを行う
    • 消費アイテムの個数に対してマイナスのパラメータを受け取った時、などに注意
  • リクエスト再送信対策
    • リクエスト送信時、ユニークな値を添える(Appleではreceiptと呼ばれる)
    • 同じreceiptで送られてきたリクエストは弾く

セキュリティ関係のHTTPヘッダ

  • X-Content-Type-Options
    • IEでContent Sniffing(メディアタイプの指定を無視する)の対策
    • nosniff
  • X-XSS-Protection
    • IEXSS検出ブロック機能が切られている時の対策
    • 1; mode=block
  • X-Frame-Options
    • 指定したページがフレーム内で読み込まれることを許可するか
    • deny
  • Content-Security-Policy
    • そのページのIMG・SCRIPT要素などの読み込み先としてどこを許可するか
    • default-src 'none'
  • Strict-Transport-Security
    • HTTPSでのアクセスを必須とする
    • max-age=15768000…この期間中、必ずHTTPSでアクセスする
  • Set-Cookie
    • session=hoge; Path=/; Secure; HttpOnly
    • Secure属性は、そのクッキーはHTTPSでのみサーバに送信される
    • HttpOnly属性は、そのクッキーはHTTP通信のみで使用され、JavaScript等からアクセス出来ない

大量アクセス対策

  • ユーザごとにAPIに対するレートリミット(アクセス回数制限)を設ける
    • 使用頻度の高いAPIは制限を緩くする
    • リミットリセットのタイミングは、長くとも1時間くらい
      • プログラムミス等でのうっかり大量アクセス時に、アクセス制限にかかる時間が長いとつらい
  • 制限値を超えた場合はステータスコード429(Too Many Requests)を返す
    • 新しいステータスコードで稼働中サービスでの使用例は少ない
    • Retry-Afterヘッダに、制限解除時間を示すとベター
    • JSONでもエラーメッセージを伝えた方が良い
  • レートリミットを知るためのAPIを用意する
    • それぞれのAPIに対して、remaining(残り回数)、reset(解除までの時間)、limit(単位時間当たりの制限数)など
    • APIのレスポンス時に、HTTPヘッダにレートリミット情報を付加する
      • X-RateLimit-Limit
      • X-RateLimit-Remaining
      • X-RateLimit-Reset

付録A Web API を公開する際にできること

  • APIドキュメントを提供する
    • ドキュメント制作用のサービスやツールもある
  • サンドボックスAPIを提供する
    • テスト環境
    • API仕様は本番環境と同じものにする
    • api.sandbox.example.comのようにホスト名を変える

まとめ

  • APIバージョンはv1の形式でURI中に含める
  • APIはなるべく後方互換性のあるバージョンアップに留める
  • 提供終了時の仕様やサポート期限を明記する
  • HTTPS
  • Content-Typeをきちんとapplication/jsonとする
  • セキュリティヘッダはとりあえずぶち込んでおく
  • レートリミットを設ける
    • レートリミット情報をきちんとユーザに渡す配慮も忘れずに
  • APIドキュメントをちゃんと書く
  • サンドボックスAPIも提供するとベター

感想

  • 付録BのWebAPIチェックリストが便利

8.2 Web API: The Good Parts 3章~4章

第3章 レスポンスデータの設計

JSONP

JSONをブラウザに渡す際、JavaScriptで以下のようにpaddingしたものをJSONPという。

callback({"id":123, "name":"Jack"})

JSONPが考えだされた背景には、同一生成元ポリシーによりXHTTPRequestは同じ生成元へのアクセスしか行うことが出来ないがscript要素はその制限の対象外であるということがある。つまり、抜け道としての手段である。そのため、必要な場合のみサポートするという姿勢を取ったほうが良い。 JSONPをサポートする際は、クエリパラメータとしてコールバック関数の名前を指定させるほうが良い。 以下に理由を説明する。まず、グローバル変数JSONを渡す仕様は、グローバルを汚染してしまう。次に、コールバック関数名を固定する方法は、関数名の衝突が考えられ、柔軟性がない。よって、コールバック関数の名前をユーザーが指定できるような設計にすべきである。この時のパラメータ名にはcallbackが多用される。

データの内部構造

ユーザーが必要とするであろう情報を返す

たとえば、SNSの友達一覧を取得するAPIでユーザーIDの配列だけが返されても、ユーザーはそのIDを利用して別のAPIを呼ぶ必要があり、不便な上にオーバーヘッドが増加する。よって、IDの他に名前等の情報も返すべきである。

レスポンス内容をユーザーが選べるようにする

上記方針でレスポンス内容を設計すると、ユーザーに返すデータが膨大になる。そこで、いくつかのデータセットを選択肢として与えて、どのデータセットを渡すかユーザーに選択させる設計を行ったほうが良い。この時のパラメータ名にはfieldsが多用される。

エンベロープは不必要

すべてのAPIを同じ構造でくるむことをエンベロープという。たとえば、すべてのAPIを以下のような構造にくるむと、一見良い設計に思える。

{
  "header": {
    "status": "success",
    "errorCode": 0
  },
  "response": {
    ...
  }
}

しかし、APIはそもそもHTTPを介しているので、HTTPというエンベロープが存在する。そのため、実際に利用される以外のメタデータに関しては、HTTPを利用する方が理に適っている。

データはなるべくフラットに

深い階層にデータを置くべきではない。これは、JSONのデータサイズを大きくしないようにするためである。しかし、階層化したほうが絶対に良い場合はそうしたほうが良い。

配列はオブジェクトとして包む

配列で包める場合も、オブジェクトで包むほうが良い。以下の様なメリットが有る。 * レスポンスデータが何を示しているものか分かりやすい * レスポンスデータをオブジェクトに統一できる * セキュリティリスクを回避できる セキュリティリスクについて、全体を配列で包むとそのJSONJavaScriptとして正しいものになり、レスポンスに不正コードを仕込まれる可能性がある。オブジェクトの場合はそれ単体ではJavaScriptとして正しいコードではないため、ブラウザからアクセスしても不正コードは実行されない。

日付のフォーマット

HTTPヘッダで用いられているRFC3339を利用するのが良い。2015-10-12T11:30:22+09:00のような形式である。

大きな数字

大きな数字は、数値データではなく文字列として返した方が良い。ユーザーのプログラムでのオーバーフローを防ぐためである。

エラーの伝え方

エラーが発生した場合は、おおよその意味をHTTPのステータスコードで返し、HTTPヘッダまたはJSON内にエラーの詳細を記述すれば良い。 また、エラー発生の際にHTMLが返ってしまうことは避け、メンテナンス時にもきちんとJSONを返す(ステータスコードは503)。

第4章 HTTPの仕様を最大限利用する

なぜHTTPの仕様を利用するのか?

前章で述べたように、メタデータをユーザーに渡すためのエンベロープとして使えるため。

ステータスコードの利用

大まかに、200番台:成功、300番台:追加で処理が必要、400番台:クライアントのリクエストに起因するエラー、500番台:サーバエラーである。 以下、APIに必要そうなやつだけ。

200:OK

PUT PATCHメソッドで正常にデータ更新が出来た時。

201:Created

POSTメソッドで、正常にデータが新規登録された時。

202:Accepted

処理は受理されたが、まだ完了していない時に返すステータスコード

204:No Content

レスポンスが空の時に利用。DELETEメソッドでデータの削除を行った時。

401:Unauthorized

「認証がなされていない(アクセスユーザーが特定できない)」。アクセスにユーザー情報が必要なAPIで、トークン無しでアクセスしてきた場合。

403:Authorization

「アクセス権限がない(ユーザーは特定できたが、そのユーザーに操作権限がない)」。許可されたユーザー以外のトークンでアクセスしてきた場合。

404:Not Found

存在しないユーザー情報にアクセスしようとした場合など。ただし、エンドポイントそのものが存在しないのか、データが存在しないのかが判別できないため、詳しい情報を付加するのが親切。

405:Method Not Allowed

エンドポイントは存在しているが、そのアクセスメソッドは許可されていない。

406:Not Acceptable

クライアントが指定してきたデータ形式APIが対応していない(たとえば、JSONしか対応していないのにXMLを指定してきた時)。

409:Conflict

リソースの競合。たとえば、ユニークキーを指定して新規登録するようなAPIで、重複キーを送ってきた場合。

410:Gone

かつて存在したが今は存在しないデータにアクセスしてきた場合。ただし、410を実装するなら、過去のデータを全て残しておく必要があり、セキュリティ上問題がある可能性もある(かつて登録されたメールアドレスなどが分かってしまう可能性がある)。

429:Too Many Requests

アクセス回数が許容範囲の限界を超えた。

503:Service Unavailable

サーバが一時的に利用できない。メンテナンス時など。

キャッシュについて

期限切れモデル

Cache-ControlレスポンスヘッダまたはExpiresレスポンスヘッダを利用して、データの有効期限を指定する。プロキシサーバで蓄えられたデータが再利用されるため、処理が高速になる。

検証モデル

Last-ModifiedレスポンスヘッダとETag(エンティティタグ)レスポンスヘッダを利用して、オリジンサーバとプロキシサーバの持つデータが一致しているか確認する。ETagには、レスポンスデータのハッシュ値などが入る。この方式では、期限切れモデルと違いオリジンサーバとの通信が発生するため、レスポンスデータが膨大な場合には効果があるが、それ以外の場合はあまり意味を成さない。

キャッシュさせたくない場合

Cache-Controlヘッダにno-cacheを入れる。

メディアタイプの指定

JSONの場合、Content-Typeヘッダにapplication/jsonを入れる。指定しなかった場合、XSSの影響を受ける可能性がある。

{"data":"<script>alert('xss');</script>"}

クロスオリジンリソース共有

同一生成元ポリシーによりXHTTPRequestで異なるドメインに対してアクセスは出来ない。しかし、CORSを行うことによって特定のアクセス元からのアクセスを許可することが可能である。 リクエストのOriginヘッダにアクセス元を指定する。 レスポンスのAccess-Control-Allow-Originヘッダに、CORSを許可するアクセス元を指定する。ワイルドカードの利用が可能。

CORSとユーザー認証情報

リクエストでCookieAuthenticationヘッダにユーザー認証情報がセットされている場合、サーバはレスポンスでAccess-Control-Allow-Credentialsヘッダにtrueをセットする必要がある。

独自のHTTPヘッダを定義する場合

X-AppName-を接頭辞として付けることが一般的であったが、AppName-で十分であるという議論がなされている。

まとめと感想

  • レスポンスデータ構造は必要以上に複雑にせず、フラットにする。
  • レスポンスデータ内容はいくつかのデータセットを定義しておき、ユーザーが必要な情報を一度に返すような設計にする(LSUDsの場合。SSKDsの場合はその限りでない)。
  • 大きな数値データは文字列とすることに注意する。
  • ステータスコードを有効利用する。
  • 同一生成元ポリシーに対応するためにはJSONPとCORSの2通りの方法がある。JSONPは仕様の穴を突いたような方法のため、CORSの方が良いと言える。
  • 独自のHTTPヘッダを定義する場合、AppName-を接頭辞とするだけで十分だと思う。

8.1 Web API: The Good Parts 1章~2章

Web API: The Good Parts

Web API: The Good Parts

年明けから、この本を読んでいる。もう辞めるけど、仕事でAPI触っていたのと、個人的に作りたいものにAPIが必要になりそうなので。
APIの設計についての200ページ位の本。サクッと1月中旬までに読みたい(できるかな)。
読んでて気になったとことか雑多に纏めていこうかなーと思う。

第1章 Web APIとは何か

Web APIの定義

URIにアクセスすることで、情報を(主に)JSONで取得できるようなもの。つまり、サーバとクライアントとの橋渡しとなる部分で、ブラウザを通して人間が読むためのものではなく、第三者がプログラムを通して利用することを前提としている。

Web APIの公開メリット

APIを公開することは、第三者のサービスが自分のサービスの情報を利用できるようになるということで、一見利点が無いように見える。だが、良いAPIを公開することで結果的に自分のサービスの知名度を上げることが出来、第三者のサービスは自分のAPIを通しているので、事実的に自分の掌の上にあるも同然であり、メリットがデメリットを上回る。また、自分のサービスが保持する情報に価値があるなら、どちらにせよ第三者はWebスクレイピングで何とかして情報を得ようとするだろう。

APIの対象開発者を考える

対象開発者にはLSUDs(large set of unknown developers)とSSKDs(small set of known developer)がある。LSUDsは未知のたくさんの開発者、SSKDsは既知の小数の開発者という意味である。対象者によってAPIの設計は変える必要があるだろう。

第2章 エンドポイントの設計とリクエストの形式

言葉の定義

エンドポイントは、APIURIのこと。

URI設計

覚えやすく、どんな機能を持つURIなのかひと目で分かるものが良い
具体的には、以下の通り。

短く入力しやすい

http://api.example.com/service/api/search より http://api.example.com/search が良い。

人間が読んで理解できる

http://api.example.com/s より http://api.example.com/search が良い。

大文字小文字が混在していない

getUserNameとか。こういう名前を付けなければならない状況が発生するのは設計ミス。

改造しやすい(Hackableである)

http://api.example.com/items/123456 というURIは、ID:123456のアイテムに関するURIで、番号を変えれば他のアイテム情報にもアクセスできることが容易に想像可能。

サーバ側のアーキテクチャが反映されていない

APIの形式をデータベースのテーブル構成等に合わせる必要はない。また、どんな言語を利用しているかをURIに反映させる必要もない。逆に、そのような情報を間接的に公開してしまうことで、攻撃者にとって有益な情報を与えてしまうことになる。

ルールが統一されている

http://api.example.com/items/123456http://api.example.com/friends?id=123456 のようなURIを混在させない

HTTPメソッドの利用

HTTPメソッドにはGET・POSTの他にも、PUT・DELETE等も存在する。エンドポイントは一箇所にして、メソッドによって操作を変更するのがHTTPの理念に沿っている。

メソッド 説明
GET リソースの取得
POST リソースの新規登録
PUT 既存リソースの更新
DELETE リソースの削除
PATCH リソースの一部変更
HEAD リソースのメタ情報の取得

たとえば、http://api.example.com/users というエンドポイントに対して、GETメソッドでアクセスした場合はユーザーの一覧取得、POSTメソッドでアクセスした場合はユーザーの新規登録を行うと良い。

検索のクエリパラメータ

タイムライン一覧の検索等では、データの全てを取得することは考えにくく、取得数の制限(limit)とどこから取得するかという情報が必要になる。
「どこから取得するか」に関しては、相対位置(offset)と絶対位置(since_id)の2つが考えられる。
相対位置では、最新情報からいくつかの情報を省いて取得する。これは、更新頻度の高いテーブルには適さない。また、データ数が増えるに連れて線形的に動作速度が落ちる。
絶対位置では、クライアントが最後に取得した情報のIDを利用する。

自分の情報へのエイリアス

自分の情報を取得するのに、http://api.example.com/users/123456 というURIの他に、http://api.example.com/users/self 等のURIを利用できるようにした方が良い。

OAuth2.0の利用

OAuth2.0は、標準化された、広く認知された仕組み。あるサービスのユーザー情報を第三者サービスに、情報源サービスのパスワードを第三者サービスに送信すること無く流すことが可能。

SSKDsに対するAPIデザイン

第三者サービスがホーム画面を用意することが分かっているなら、そこに必要な情報(ユーザー情報・タイムライン等)を全て一度に返すAPIを用意すべき。
1スクリーン1APIコール、1セーブ1APIコール

HATEOAS と REST LEVEL3 API

REST LEVEL
  • REST LEVEL0…HTTPを使っている
  • REST LEVEL1…リソースの概念の導入(エンドポイントの/usersとか/itemsとか)
  • REST LEVEL2…HTTPの動詞の導入(HTTPメソッドAPIへの利用)
  • REST LEVEL3…HATEOAS概念の導入
HATEOAS

HATEOAS(hypermedia as the engine of application state)とは、APIのレスポンスに次に想定されるアクセスAPIURIを含めること。
たとえば、friends情報を取得した時に、あるフレンドの情報を得るためのURIおよびその意味を、

"friends": {
	{ "name": "Jack",
	"link" : {
		"uri" : "http://api.example.com/users/
		"rel" : "user/detail"
	},
  ...
}

の形式でレスポンスに含める。

まとめ

  • URIは短く簡潔に。ただし、意味が分からないレベルまで短くしない。
  • URIは対象の名詞(複数形)にして、HTTPメソッドで動詞を表すと良い。
  • 検索で「どこから取得するか」については、相対位置と絶対位置の2通りが考えられる。
  • 認証ユーザーに対するアクセスにはself等のエイリアスを用いることが可能なようにする。
  • SSKDsには、場合によっては複数情報を纏めて返すAPIを用意する必要がある。
  • REST LEVEL3 を目指したい。

(重大っぽい)誤植?

この本、割と誤植が多いので、重大っぽい誤植だけ挙げておく。初版第1刷です。

P39

「mediumがmediaの複数形」ではなく、「mediaがmediumの複数形」。

図2-6 OAuthの基本的な仕組み

3番と4番の矢印が逆。

感想

HATEOASについては、まだ思想が広まっていないのでこの先どうなるか分からない、としか触れられていないが、概念を取り入れたAPI設計にすべきだと思う。
なぜなら、エンドポイントの変更があった場合でも、第三者サービスに改修の必要が生じないからだ。また、第三者サービスのプログラムに具体的なURIを含める必要性が少なくなり、第三者にとって扱いやすいAPIとなる。
デメリットとしては、APIのレスポンスが煩雑になるということが挙げられるが、JSONの設計を上手く行えばマシになるのでは無いかと思う。

0.3 Haskellでエラトステネスの篩

職場でエラトステネスの篩の話になって、ふと「関数型言語なら綺麗に書けそう」と思ったので、書いた。

一応、エラトステネスの篩とは、指定した範囲に含まれる素数を列挙するための高速アルゴリズムで、以下の様なものである。

1. 探索リストに2からnまでの整数を昇順で入れる。
2. 探索リストの先頭の数を素数リストに移動し、その倍数を探索リストから篩い落とす。
3. 上記の篩い落とし操作を探索リストの先頭値がxの平方根に達するまで行う。
4. 探索リストに残った数を素数リストに移動して処理終了。

今回書いたやつでは、探索リストが空になるまで探索を続けている。

remover :: Integer -> [Integer] -> [Integer]
remover n ns = [x | x <- ns, x `mod` n /= 0]

recursion :: [Integer] -> [Integer]
recursion xs = 
  case xs of
    [] -> []
    (x:_) -> x : recursion (remover x xs)

eratosthenes :: Integer -> [Integer]
eratosthenes n =
  if n < 2 then error "Input bigger than 1." 
  else 
    let primes = [2..n]
    in  recursion primes

removerは、nsからnの倍数の要素を落としたリストを返す。
recursionは、引数のリストが空になるまでremoverを適用し続ける。適用する際、引数のリストの先頭は戻り値のリストの元とする。
eratosthenesは、2~nまでに含まれる素数をリスト形式で出力する。primesに探索リストを作り、recursionを適用することで実現する。

で、もう少し簡略化出来そうだな~、と思って、以下の形に落ち着いた。

eratosthenes :: Integer -> [Integer]
eratosthenes n =
  if n < 2 then error "Input bigger than 1." 
  else 
    let primes = [2..n]
        recursion ms = 
          case ms of
            [] -> []
            (m:_) -> m : recursion [x | x <- ms, x `mod` m /= 0]
    in  recursion primes

case ms of辺りが少し汚いけど、再帰書くならどうしようもないかなぁ…。
リスト内包表記強い。

で、実際に実行してみる。

Prelude> :l eratosthenes.hs
[1 of 1] Compiling Main             ( eratosthenes.hs, interpreted )
Ok, modules loaded: Main.
*Main> eratosthenes 10
[2,3,5,7]
*Main> eratosthenes 100
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
*Main> eratosthenes 0
*** Exception: Input bigger than 1.
eratosthenes 10000000000000000000000000000000000000000000000
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,...

これ、怖いのは最後の例で、アホみたいな引数を与えても迅速に結果を返してくる。
さすがに実行時間が速すぎる。遅延評価の賜物なのか?
久しぶりにHaskell触ると、割と楽しかった。

7.1 Unity2DでSTG作る日記 1日目

職場でUnityを使うことになったのと、個人的に作りたいものがあるので、いろいろテストしていく日記にしようと思う。日記なので、雑多な感じで書いていく。

最後にUnityを触ったのがカラオケ採点機を作った時で、完全に仕様とか忘れているので、色々と躓きながらの作業だった。

f:id:ibako31:20141203014847j:plain

なお、使用しているのはUnity2D(v4.3.3f1)である。少し古い。

とりあえず、画像のインポート

「どうするんだっけ?」となっていたので調べた。プロジェクトフォルダのAssets内に置けばいいらしい。素材にしたい場合、Spriteとかいうものになるらしいので、Assetsフォルダの中にSpritesフォルダ作成。Spritesってなんなんだ。
で、以前DXライブラリで作っていたやつの素材フォルダを、すべてぶち込んだ。Unityに戻ると、正常に読み込まれた。平和に解決。

ドラッグ&ドロップでプレイヤー0のスプライトをSceneに突っ込む

画面上に追加された。Hierarchyに「0」とかいう名前のオブジェクトがある。あー、思い出してきた。
オブジェクトの名前をPlayerに変更。

当たり判定を付ける

Playerを選択して、InspectorからAdd Component -> Box Collider 2D。でも、これアクションゲームじゃないから、Colliderは要らないかもしれない。まぁ、よく分からんけど今は付けておこう。当たった瞬間をスイッチとして起こすイベントとか書けると思うし、たぶん。

操作できるようにする

サークルの後輩のスライドを頼った。Add Component -> New Script(C Sharp)。ファイル名はとりあえずPlayerにした。
スクリプトについて。デフォルトではAssets直下に配置されるので、Assets以下にScriptsフォルダを作り、ついでにSpritesフォルダと同様のフォルダ構成にして、Scripts/player/Player.csとした。

public class Player : MonoBehaviour {	
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
       		float x = Input.GetAxis("Horizontal");
	        float y = Input.GetAxis("Vertical");
	        this.transform.Translate(new Vector3(x, y));
	}	
}

Input.GetAxisで入力を受け取って、this.transform.Translateで座標を更新するっぽい。詳しくは調査してない。
実行する。はやい。制御できん。スピードをつける。

public class Player : MonoBehaviour {
	public float Speed { get; private set; }

	// Use this for initialization
	void Start () {
       		Speed = 0.2f;	
	}
	
	// Update is called once per frame
	void Update () {
       		float x = Input.GetAxis("Horizontal");
       		float y = Input.GetAxis("Vertical");
       		Vector3 v = new Vector3(x, y) * Speed;
	        this.transform.Translate(v);
	}

}

移動速度はいい感じになったけど、なんか、移動に慣性がついてるような…。
this.transform.Translateが悪いのかな?というわけで書き換える。

public class Player : MonoBehaviour {
	public float Speed { get; private set; }	

	// Use this for initialization
	void Start () {
       		Speed = 0.2f;	   
	}

	// Update is called once per frame
	void Update () {
       		float inputX = Input.GetAxis("Horizontal");
	        float inputY = Input.GetAxis("Vertical");
       		Vector3 newPos = this.transform.position + new Vector3(inputX, inputY) * Speed;
	        this.transform.position = newPos;
	}	
}

なんか、これでも慣性がついてる。ということは、Input.GetAxisの戻り値に慣性が付いてる可能性がある?
と、ここで時間が無くなったので終了。
Assets/Scenesフォルダを作り、test.unityとして現在のシーンを保存。てか、採点機作ってる時は1シーンだけでやってたし、遷移とかよく分かってないなぁ。

感想

そういえばUnityってこんなのだった。
ネット上の情報は割と偏っているので、書籍買ってちゃんと勉強したいなぁ。

0.2 OAuth認証(PINコード)

計算機科学実験及び演習4で、C#を用いたTwitter REST APIを利用するライブラリ制作を行った。
OAuth認証についてはライブラリを用いて良いということだったが、Twitter用にOAuth認証するライブラリは大体タイムライン取得やつぶやきも出来るようになってて、あまり意味が無いと感じたので、Google先生が提供しているOAuthBaseを利用して、割と一から認証処理を書いた。
おかげで、OAuth認証が何をやっているのかが分かったので、メモ書き程度の記事にしてみようと思う。
なお、今回扱うOAuth認証はPINコードを用いるもので、バージョンは1.0である。

OAuth1.0(PINコード)の流れ(見た目)

ブラウザアプリの場合は必要ないので、あまり見かけることはないが、数字を入力させる認証方法である。
具体的には、Twitterでは以下の様な流れになる。

  1. ブラウザ立ち上げ
  2. Twitterにログイン
  3. アプリと連携させるか選択
  4. PINコード(7桁の数字)を表示する
  5. アプリ内にPINコードを入力
  6. 認証完了

これが内部で何を行っているのかを探る。

OAuth1.0(PINコード)の流れ(中身)

まず、OAuth認証で重要なsignatureについて述べる。

signature

signatureはBase64形式のハッシュ値である。サーバとクライアントでこの値が一致していれば認証が通る。
signatureは、基礎値となるsignature_base文字列と、鍵となるkey文字列をHMAC-SHA1でハッシュ化したものをさらにBase64に変換した文字列となっている。

signature_base

基礎値となる文字列は、以下の情報を基に生成される。

  • URL(パラメータ含む)
  • consumer_key
  • access_token
  • timestamp
  • nonce

consumer_keyはアプリケーションが持つ鍵の一つである。access_tokenはユーザが各自持つ鍵の一つである。nonceはランダム文字列である。

key

keyは、以下の情報を基に生成される。

  • consumer_secret
  • access_token_secret

これらを繋げてハッシュ化したものが鍵となる。

TwitterにおけるOAuth認証の流れ

request_token

ユーザは、アプリケーションの持つconsumer_keyおよびconsumer_secretしか情報を持っていない。よって、まずはaccess_tokenおよびaccess_token_secretを取得する必要がある。
そこで、access_tokenおよびaccess_token_secretを空の文字列としたsignatureをTwitter側に送信し、Twitterが生成したaccess_tokenおよびaccess_token_secretを受け取る。
signatureの受け渡しでは、同時に基礎文字列の材料も全てパラメータで受け渡す。つまり、パラメータさえ見ればsignature_baseは簡単に求められる。しかし、鍵の基となる情報は送受信しないため(Twitterとユーザが個人で持っている)、安全性が保証されるわけである。
この段階は、「そのアプリケーションに、渡したaccess_tokenおよびaccess_token_secretを持つ認証待ちユーザがいる」という意味を持つ。

TwitterログインとPINコード入力

ここで、ユーザにはTwitterにログインしてもらう。ログイン後、ユーザがアプリ連携を認めると、7桁のPINコードが表示される。アプリケーションは、このPINコードを受け取る。

access_token

先ほど受け取ったaccess_tokenおよびaccess_token_secretは、request_tokenおよびrequest_token_secretと呼ぶ。これらのリクエストトークンを用いて再びsignatureを求める。このsignatureと、先ほど受け取ったPINコードを、再びTwitterに送信し、Twitterから新しいaccess_tokenおよびaccess_token_secretを受け取る。これがAPIを叩くときのOAuth認証に使われるトークンとなる。
この段階は、「Twitterでログインされた、確かなユーザが認証された」という意味を持つ。

APIを叩く

APIを叩く際は、signature_baseの生成に必要な情報と、signatureをTwitterに送信する。
先ほどと同様、signature_baseはパラメータから簡単に生成できるが、signature生成のための鍵はお互い送受信してないデータを用いて生成するため、安全性が保証されるわけである。

雑感

要するに、

  • signature(signature_baseとkeyを使ったハッシュ)を照合させて認証する
  • signature_baseは固定文字列とタイムスタンプとランダム文字列を用いて生成される
  • keyはサーバ・クライアントがお互いローカルに保持する2つの文字列を用いて生成される
  • 2つの鍵は、アプリケーションの鍵とユーザの鍵
  • この2段階認証によって特定のアプリ上の特定のユーザが認証できる

というわけであるが、access_token_secretは最初の一回はサーバ側からクライアント側に送信される必要があるわけで、少し微妙な認証方式だと思う。

Rubybot作ってた時は、意味もわからずconsumer_key・consumer_secret・access_token・access_token_secretを入力していたが、今回の実験でその利用法が分かって良かった。

全然関係ない話になるが、RSA暗号はこの「鍵配送問題」を解決した画期的な暗号である。次回の記事で解説しようと思う。